mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-10-03 19:00:50 +08:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
862a6586cb | ||
|
|
61d8331ef3 | ||
|
|
acc3bf1fdc | ||
|
|
83eabd7c01 | ||
|
|
630ee03f23 | ||
|
|
f6968bc589 | ||
|
|
adfef62900 | ||
|
|
6fa7ca9317 | ||
|
|
d6dd6d7b55 | ||
|
|
dea4980f4e | ||
|
|
c8fb3d2458 | ||
|
|
3d33caaef8 | ||
|
|
9b424db0f4 | ||
|
|
d95062737e | ||
|
|
7c880f80c7 | ||
|
|
aaa8a51bd8 | ||
|
|
ba35dd734e | ||
|
|
d41f5fff69 | ||
|
|
810ef0cf76 | ||
|
|
5792c66879 | ||
|
|
39d54702a6 | ||
|
|
60889bc9a1 |
@@ -21,11 +21,13 @@ on:
|
||||
"**/*.c",
|
||||
"**/*.cpp",
|
||||
"**/*.cu",
|
||||
"examples/server/frontend/**",
|
||||
]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
[
|
||||
".github/workflows/**",
|
||||
"**/CMakeLists.txt",
|
||||
"**/Makefile",
|
||||
"**/*.h",
|
||||
@@ -33,6 +35,7 @@ on:
|
||||
"**/*.c",
|
||||
"**/*.cpp",
|
||||
"**/*.cu",
|
||||
"examples/server/frontend/**",
|
||||
]
|
||||
|
||||
env:
|
||||
@@ -53,6 +56,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
@@ -70,7 +83,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
id: system-info
|
||||
@@ -106,6 +119,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
@@ -123,7 +146,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
id: system-info
|
||||
@@ -162,7 +185,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [musa, sycl, vulkan]
|
||||
variant: [musa, sycl, vulkan, cuda]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
@@ -174,10 +197,20 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -223,6 +256,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Dependencies
|
||||
id: depends
|
||||
run: |
|
||||
@@ -240,7 +283,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Fetch system info
|
||||
id: system-info
|
||||
@@ -294,6 +337,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Install cuda-toolkit
|
||||
id: cuda-toolkit
|
||||
if: ${{ matrix.build == 'cuda12' }}
|
||||
@@ -340,7 +393,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Pack artifacts
|
||||
id: pack_artifacts
|
||||
@@ -399,6 +452,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Cache ROCm Installation
|
||||
id: cache-rocm
|
||||
uses: actions/cache@v4
|
||||
@@ -463,7 +526,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Pack artifacts
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
@@ -502,6 +565,16 @@ jobs:
|
||||
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: 9
|
||||
|
||||
- name: Free disk space
|
||||
run: |
|
||||
# Remove preinstalled SDKs and caches not needed for this job
|
||||
@@ -581,7 +654,7 @@ jobs:
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Prepare artifacts
|
||||
id: prepare_artifacts
|
||||
@@ -660,7 +733,7 @@ jobs:
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
uses: prompt/actions-commit-hash@v2
|
||||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
[submodule "ggml"]
|
||||
path = ggml
|
||||
url = https://github.com/ggml-org/ggml.git
|
||||
[submodule "examples/server/frontend"]
|
||||
path = examples/server/frontend
|
||||
url = https://github.com/leejet/stable-ui.git
|
||||
|
||||
@@ -36,7 +36,6 @@ option(SD_VULKAN "sd: vulkan backend" OFF)
|
||||
option(SD_OPENCL "sd: opencl backend" OFF)
|
||||
option(SD_SYCL "sd: sycl backend" OFF)
|
||||
option(SD_MUSA "sd: musa backend" OFF)
|
||||
option(SD_FAST_SOFTMAX "sd: x1.5 faster softmax, indeterministic (sometimes, same seed don't generate same image), cuda only" OFF)
|
||||
option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF)
|
||||
option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF)
|
||||
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
||||
@@ -70,18 +69,12 @@ if (SD_HIPBLAS)
|
||||
message("-- Use HIPBLAS as backend stable-diffusion")
|
||||
set(GGML_HIP ON)
|
||||
add_definitions(-DSD_USE_CUDA)
|
||||
if(SD_FAST_SOFTMAX)
|
||||
set(GGML_CUDA_FAST_SOFTMAX ON)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if(SD_MUSA)
|
||||
message("-- Use MUSA as backend stable-diffusion")
|
||||
set(GGML_MUSA ON)
|
||||
add_definitions(-DSD_USE_CUDA)
|
||||
if(SD_FAST_SOFTMAX)
|
||||
set(GGML_CUDA_FAST_SOFTMAX ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SD_LIB stable-diffusion)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
ARG CUDA_VERSION=12.6.3
|
||||
ARG UBUNTU_VERSION=24.04
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-devel-ubuntu${UBUNTU_VERSION} AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git ccache cmake
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG CUDACXX=/usr/local/cuda/bin/nvcc
|
||||
RUN cmake . -B ./build -DSD_CUDA=ON
|
||||
RUN cmake --build ./build --config Release -j$(nproc)
|
||||
|
||||
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-runtime-ubuntu${UBUNTU_VERSION} AS runtime
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --yes --no-install-recommends libgomp1 && \
|
||||
apt-get clean
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
+2
-1
@@ -5,6 +5,7 @@
|
||||
- Download Anima
|
||||
- safetensors: https://huggingface.co/circlestone-labs/Anima/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/Bedovyy/Anima-GGUF/tree/main
|
||||
- gguf Anima2: https://huggingface.co/JusteLeo/Anima2-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/circlestone-labs/Anima/tree/main/split_files/vae
|
||||
- Download Qwen3-0.6B-Base
|
||||
@@ -17,4 +18,4 @@
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\anima-preview.safetensors --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\qwen_3_06b_base.safetensors -p "a lovely cat holding a sign says 'anima.cpp'" --cfg-scale 6.0 --sampling-method euler -v --offload-to-cpu --diffusion-fa
|
||||
```
|
||||
|
||||
<img alt="anima image example" src="../assets/anima/example.png" />
|
||||
<img alt="anima image example" src="../assets/anima/example.png" />
|
||||
|
||||
+24
-9
@@ -11,6 +11,7 @@ Caching methods accelerate diffusion inference by reusing intermediate computati
|
||||
| `dbcache` | DiT models | Block-level L1 residual threshold |
|
||||
| `taylorseer` | DiT models | Taylor series approximation |
|
||||
| `cache-dit` | DiT models | Combined DBCache + TaylorSeer |
|
||||
| `spectrum` | UNET models | Chebyshev + Taylor output forecasting |
|
||||
|
||||
### UCache (UNET Models)
|
||||
|
||||
@@ -79,7 +80,7 @@ Uses Taylor series approximation to predict block outputs:
|
||||
Combines DBCache and TaylorSeer:
|
||||
|
||||
```bash
|
||||
--cache-mode cache-dit --cache-preset fast
|
||||
--cache-mode cache-dit
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
@@ -91,14 +92,6 @@ Combines DBCache and TaylorSeer:
|
||||
| `threshold` | L1 residual difference threshold | 0.08 |
|
||||
| `warmup` | Steps before caching starts | 8 |
|
||||
|
||||
#### Presets
|
||||
|
||||
Available presets: `slow`, `medium`, `fast`, `ultra` (or `s`, `m`, `f`, `u`).
|
||||
|
||||
```bash
|
||||
--cache-mode cache-dit --cache-preset fast
|
||||
```
|
||||
|
||||
#### SCM Options
|
||||
|
||||
Steps Computation Mask controls which steps can be cached:
|
||||
@@ -118,6 +111,28 @@ Mask values: `1` = compute, `0` = can cache.
|
||||
--scm-policy dynamic
|
||||
```
|
||||
|
||||
### Spectrum (UNET Models)
|
||||
|
||||
Spectrum uses Chebyshev polynomial fitting blended with Taylor extrapolation to predict denoised outputs, skipping entire UNet forward passes. Based on the paper [Spectrum: Adaptive Spectral Feature Forecasting for Efficient Diffusion Sampling](https://github.com/tingyu215/Spectrum).
|
||||
|
||||
```bash
|
||||
sd-cli -m model.safetensors -p "a cat" --cache-mode spectrum
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `w` | Chebyshev vs Taylor blend weight (0=Taylor, 1=Chebyshev) | 0.40 |
|
||||
| `m` | Chebyshev polynomial degree | 3 |
|
||||
| `lam` | Ridge regression regularization | 1.0 |
|
||||
| `window` | Initial window size (compute every N steps) | 2 |
|
||||
| `flex` | Window growth per computed step after warmup | 0.50 |
|
||||
| `warmup` | Steps to always compute before caching starts | 4 |
|
||||
| `stop` | Stop caching at this fraction of total steps | 0.9 |
|
||||
|
||||
```
|
||||
|
||||
### Performance Tips
|
||||
|
||||
- Start with default thresholds and adjust based on output quality
|
||||
|
||||
+17
-12
@@ -4,11 +4,12 @@
|
||||
usage: ./bin/sd-cli [options]
|
||||
|
||||
CLI Options:
|
||||
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image sequences (default: ./output.png) (eg. output_%03d.png)
|
||||
--output-begin-idx <int> starting index for output image sequence, must be non-negative (default 0 if specified %d in output path, 1 otherwise)
|
||||
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image sequences (default:
|
||||
./output.png) (eg. output_%03d.png)
|
||||
--preview-path <string> path to write preview image to (default: ./preview.png)
|
||||
--preview-interval <int> interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at
|
||||
every step)
|
||||
--output-begin-idx <int> starting index for output image sequence, must be non-negative (default 0 if specified %d in output path, 1 otherwise)
|
||||
--canny apply canny preprocessor (edge detection)
|
||||
--convert-name convert tensor name (for convert mode)
|
||||
-v, --verbose print extra info
|
||||
@@ -59,6 +60,7 @@ Context Options:
|
||||
--circularx enable circular RoPE wrapping on x-axis (width) only
|
||||
--circulary enable circular RoPE wrapping on y-axis (height) only
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
@@ -107,7 +109,7 @@ Generation Options:
|
||||
medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--eta <float> eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--high-noise-cfg-scale <float> (high noise) unconditional guidance scale: (default: 7.0)
|
||||
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||
@@ -115,7 +117,7 @@ Generation Options:
|
||||
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||
--high-noise-skip-layer-start <float> (high noise) SLG enabling point (default: 0.01)
|
||||
--high-noise-skip-layer-end <float> (high noise) SLG disabling point (default: 0.2)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||
--pm-style-strength <float>
|
||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||
@@ -124,21 +126,24 @@ Generation Options:
|
||||
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||
--disable-auto-resize-ref-image disable auto resize of ref images
|
||||
-s, --seed RNG seed (default: 42, use random seed for < 0)
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd,
|
||||
res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a
|
||||
otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm,
|
||||
ddim_trailing, tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan,
|
||||
euler_a otherwise
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||
kl_optimal, lcm, bong_tangent], default: discrete
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||
--skip-layers layers to skip for SLG steps (default: [7,8,9])
|
||||
--high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9])
|
||||
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level)
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level),
|
||||
'spectrum' (UNET/DiT Chebyshev+Taylor forecasting)
|
||||
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples:
|
||||
"threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--cache-preset cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u'
|
||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=;
|
||||
spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=. Examples:
|
||||
"threshold=0.25" or "threshold=1.5,reset=0" or "w=0.4,window=2"
|
||||
--scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g., "1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache
|
||||
--scm-policy SCM policy: 'dynamic' (default) or 'static'
|
||||
```
|
||||
|
||||
+28
-55
@@ -1047,7 +1047,6 @@ struct SDGenerationParams {
|
||||
|
||||
std::string cache_mode;
|
||||
std::string cache_option;
|
||||
std::string cache_preset;
|
||||
std::string scm_mask;
|
||||
bool scm_policy_dynamic = true;
|
||||
sd_cache_params_t cache_params{};
|
||||
@@ -1422,8 +1421,8 @@ struct SDGenerationParams {
|
||||
}
|
||||
cache_mode = argv_to_utf8(index, argv);
|
||||
if (cache_mode != "easycache" && cache_mode != "ucache" &&
|
||||
cache_mode != "dbcache" && cache_mode != "taylorseer" && cache_mode != "cache-dit") {
|
||||
fprintf(stderr, "error: invalid cache mode '%s', must be 'easycache', 'ucache', 'dbcache', 'taylorseer', or 'cache-dit'\n", cache_mode.c_str());
|
||||
cache_mode != "dbcache" && cache_mode != "taylorseer" && cache_mode != "cache-dit" && cache_mode != "spectrum") {
|
||||
fprintf(stderr, "error: invalid cache mode '%s', must be 'easycache', 'ucache', 'dbcache', 'taylorseer', 'cache-dit', or 'spectrum'\n", cache_mode.c_str());
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
@@ -1461,21 +1460,6 @@ struct SDGenerationParams {
|
||||
return 1;
|
||||
};
|
||||
|
||||
auto on_cache_preset_arg = [&](int argc, const char** argv, int index) {
|
||||
if (++index >= argc) {
|
||||
return -1;
|
||||
}
|
||||
cache_preset = argv_to_utf8(index, argv);
|
||||
if (cache_preset != "slow" && cache_preset != "s" && cache_preset != "S" &&
|
||||
cache_preset != "medium" && cache_preset != "m" && cache_preset != "M" &&
|
||||
cache_preset != "fast" && cache_preset != "f" && cache_preset != "F" &&
|
||||
cache_preset != "ultra" && cache_preset != "u" && cache_preset != "U") {
|
||||
fprintf(stderr, "error: invalid cache preset '%s', must be 'slow'/'s', 'medium'/'m', 'fast'/'f', or 'ultra'/'u'\n", cache_preset.c_str());
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
options.manual_options = {
|
||||
{"-s",
|
||||
"--seed",
|
||||
@@ -1513,16 +1497,12 @@ struct SDGenerationParams {
|
||||
on_ref_image_arg},
|
||||
{"",
|
||||
"--cache-mode",
|
||||
"caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level)",
|
||||
"caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level), 'spectrum' (UNET/DiT Chebyshev+Taylor forecasting)",
|
||||
on_cache_mode_arg},
|
||||
{"",
|
||||
"--cache-option",
|
||||
"named cache params (key=value format, comma-separated). easycache/ucache: threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples: \"threshold=0.25\" or \"threshold=1.5,reset=0\"",
|
||||
"named cache params (key=value format, comma-separated). easycache/ucache: threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=; spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=. Examples: \"threshold=0.25\" or \"threshold=1.5,reset=0\"",
|
||||
on_cache_option_arg},
|
||||
{"",
|
||||
"--cache-preset",
|
||||
"cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u'",
|
||||
on_cache_preset_arg},
|
||||
{"",
|
||||
"--scm-mask",
|
||||
"SCM steps mask for cache-dit: comma-separated 0/1 (e.g., \"1,1,1,0,0,1,0,0,1,0\") - 1=compute, 0=can cache",
|
||||
@@ -1575,7 +1555,6 @@ struct SDGenerationParams {
|
||||
load_if_exists("negative_prompt", negative_prompt);
|
||||
load_if_exists("cache_mode", cache_mode);
|
||||
load_if_exists("cache_option", cache_option);
|
||||
load_if_exists("cache_preset", cache_preset);
|
||||
load_if_exists("scm_mask", scm_mask);
|
||||
|
||||
load_if_exists("clip_skip", clip_skip);
|
||||
@@ -1779,7 +1758,23 @@ struct SDGenerationParams {
|
||||
} else if (key == "Bn" || key == "bn") {
|
||||
cache_params.Bn_compute_blocks = std::stoi(val);
|
||||
} else if (key == "warmup") {
|
||||
cache_params.max_warmup_steps = std::stoi(val);
|
||||
if (cache_mode == "spectrum") {
|
||||
cache_params.spectrum_warmup_steps = std::stoi(val);
|
||||
} else {
|
||||
cache_params.max_warmup_steps = std::stoi(val);
|
||||
}
|
||||
} else if (key == "w") {
|
||||
cache_params.spectrum_w = std::stof(val);
|
||||
} else if (key == "m") {
|
||||
cache_params.spectrum_m = std::stoi(val);
|
||||
} else if (key == "lam") {
|
||||
cache_params.spectrum_lam = std::stof(val);
|
||||
} else if (key == "window") {
|
||||
cache_params.spectrum_window_size = std::stoi(val);
|
||||
} else if (key == "flex") {
|
||||
cache_params.spectrum_flex_window = std::stof(val);
|
||||
} else if (key == "stop") {
|
||||
cache_params.spectrum_stop_percent = std::stof(val);
|
||||
} else {
|
||||
LOG_ERROR("error: unknown cache parameter '%s'", key.c_str());
|
||||
return false;
|
||||
@@ -1794,39 +1789,17 @@ struct SDGenerationParams {
|
||||
|
||||
if (!cache_mode.empty()) {
|
||||
if (cache_mode == "easycache") {
|
||||
cache_params.mode = SD_CACHE_EASYCACHE;
|
||||
cache_params.reuse_threshold = 0.2f;
|
||||
cache_params.start_percent = 0.15f;
|
||||
cache_params.end_percent = 0.95f;
|
||||
cache_params.error_decay_rate = 1.0f;
|
||||
cache_params.use_relative_threshold = true;
|
||||
cache_params.reset_error_on_compute = true;
|
||||
cache_params.mode = SD_CACHE_EASYCACHE;
|
||||
} else if (cache_mode == "ucache") {
|
||||
cache_params.mode = SD_CACHE_UCACHE;
|
||||
cache_params.reuse_threshold = 1.0f;
|
||||
cache_params.start_percent = 0.15f;
|
||||
cache_params.end_percent = 0.95f;
|
||||
cache_params.error_decay_rate = 1.0f;
|
||||
cache_params.use_relative_threshold = true;
|
||||
cache_params.reset_error_on_compute = true;
|
||||
cache_params.mode = SD_CACHE_UCACHE;
|
||||
} else if (cache_mode == "dbcache") {
|
||||
cache_params.mode = SD_CACHE_DBCACHE;
|
||||
cache_params.Fn_compute_blocks = 8;
|
||||
cache_params.Bn_compute_blocks = 0;
|
||||
cache_params.residual_diff_threshold = 0.08f;
|
||||
cache_params.max_warmup_steps = 8;
|
||||
cache_params.mode = SD_CACHE_DBCACHE;
|
||||
} else if (cache_mode == "taylorseer") {
|
||||
cache_params.mode = SD_CACHE_TAYLORSEER;
|
||||
cache_params.Fn_compute_blocks = 8;
|
||||
cache_params.Bn_compute_blocks = 0;
|
||||
cache_params.residual_diff_threshold = 0.08f;
|
||||
cache_params.max_warmup_steps = 8;
|
||||
cache_params.mode = SD_CACHE_TAYLORSEER;
|
||||
} else if (cache_mode == "cache-dit") {
|
||||
cache_params.mode = SD_CACHE_CACHE_DIT;
|
||||
cache_params.Fn_compute_blocks = 8;
|
||||
cache_params.Bn_compute_blocks = 0;
|
||||
cache_params.residual_diff_threshold = 0.08f;
|
||||
cache_params.max_warmup_steps = 8;
|
||||
cache_params.mode = SD_CACHE_CACHE_DIT;
|
||||
} else if (cache_mode == "spectrum") {
|
||||
cache_params.mode = SD_CACHE_SPECTRUM;
|
||||
}
|
||||
|
||||
if (!cache_option.empty()) {
|
||||
|
||||
@@ -1,6 +1,73 @@
|
||||
set(TARGET sd-server)
|
||||
|
||||
option(SD_SERVER_BUILD_FRONTEND "Build server frontend with pnpm" ON)
|
||||
|
||||
set(FRONTEND_DIR "${CMAKE_CURRENT_SOURCE_DIR}/frontend")
|
||||
set(GENERATED_HTML_HEADER "${FRONTEND_DIR}/dist/gen_index_html.h")
|
||||
|
||||
set(HAVE_FRONTEND_BUILD OFF)
|
||||
|
||||
if(SD_SERVER_BUILD_FRONTEND AND EXISTS "${FRONTEND_DIR}")
|
||||
if(WIN32)
|
||||
find_program(PNPM_EXECUTABLE NAMES pnpm.cmd pnpm)
|
||||
else()
|
||||
find_program(PNPM_EXECUTABLE NAMES pnpm)
|
||||
endif()
|
||||
|
||||
if(PNPM_EXECUTABLE)
|
||||
message(STATUS "Frontend dir found: ${FRONTEND_DIR}")
|
||||
message(STATUS "pnpm found: ${PNPM_EXECUTABLE}")
|
||||
|
||||
set(HAVE_FRONTEND_BUILD ON)
|
||||
|
||||
add_custom_target(${TARGET}_frontend_install
|
||||
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" install
|
||||
WORKING_DIRECTORY "${FRONTEND_DIR}"
|
||||
COMMENT "Installing frontend dependencies"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(${TARGET}_frontend_build
|
||||
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build
|
||||
WORKING_DIRECTORY "${FRONTEND_DIR}"
|
||||
COMMENT "Building frontend"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(${TARGET}_frontend_header
|
||||
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build:header
|
||||
WORKING_DIRECTORY "${FRONTEND_DIR}"
|
||||
COMMENT "Generating gen_index_html.h"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_dependencies(${TARGET}_frontend_build ${TARGET}_frontend_install)
|
||||
add_dependencies(${TARGET}_frontend_header ${TARGET}_frontend_build)
|
||||
|
||||
add_custom_target(${TARGET}_frontend
|
||||
DEPENDS ${TARGET}_frontend_header
|
||||
)
|
||||
|
||||
set_source_files_properties("${GENERATED_HTML_HEADER}" PROPERTIES GENERATED TRUE)
|
||||
else()
|
||||
message(WARNING "pnpm not found, frontend build disabled")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Frontend disabled or directory not found: ${FRONTEND_DIR}")
|
||||
endif()
|
||||
|
||||
add_executable(${TARGET} main.cpp)
|
||||
|
||||
if(HAVE_FRONTEND_BUILD)
|
||||
add_dependencies(${TARGET} ${TARGET}_frontend)
|
||||
target_sources(${TARGET} PRIVATE "${GENERATED_HTML_HEADER}")
|
||||
target_include_directories(${TARGET} PRIVATE "${FRONTEND_DIR}/dist")
|
||||
target_compile_definitions(${TARGET} PRIVATE HAVE_INDEX_HTML)
|
||||
message(STATUS "HAVE_INDEX_HTML enabled")
|
||||
else()
|
||||
message(STATUS "HAVE_INDEX_HTML disabled")
|
||||
endif()
|
||||
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17)
|
||||
+106
-15
@@ -1,15 +1,104 @@
|
||||
# Frontend
|
||||
|
||||
## Build with Frontend
|
||||
|
||||
The server can optionally build the web frontend and embed it into the binary as `gen_index_html.h`.
|
||||
|
||||
### Requirements
|
||||
|
||||
Install the following tools:
|
||||
|
||||
* **Node.js** ≥ 22.18
|
||||
https://nodejs.org/
|
||||
|
||||
* **pnpm** ≥ 10
|
||||
Install via npm:
|
||||
|
||||
```bash
|
||||
npm install -g pnpm
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
|
||||
```bash
|
||||
node -v
|
||||
pnpm -v
|
||||
```
|
||||
|
||||
### Install frontend dependencies
|
||||
|
||||
Go to the frontend directory and install dependencies:
|
||||
|
||||
```bash
|
||||
cd examples/server/frontend
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Build the server with CMake
|
||||
|
||||
Enable the frontend build option when configuring CMake:
|
||||
|
||||
```bash
|
||||
cmake -B build -DSD_SERVER_BUILD_FRONTEND=ON
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
If `pnpm` is available, the build system will automatically run:
|
||||
|
||||
```
|
||||
pnpm run build
|
||||
pnpm run build:header
|
||||
```
|
||||
|
||||
and embed the generated frontend into the server binary.
|
||||
|
||||
## Frontend Repository
|
||||
|
||||
The web frontend is maintained in a **separate repository**, https://github.com/leejet/stable-ui.
|
||||
|
||||
If you want to modify the UI or frontend logic, please submit pull requests to the **frontend repository**.
|
||||
|
||||
This repository (`stable-diffusion.cpp`) only vendors the frontend periodically. Changes from the frontend repo are synchronized:
|
||||
|
||||
* approximately **every 1–2 weeks**, or
|
||||
* when there are **major frontend updates**
|
||||
|
||||
Because of this, frontend changes will **not appear here immediately** after being merged upstream.
|
||||
|
||||
## Using an external frontend
|
||||
|
||||
By default, the server uses the **embedded frontend** generated during the build (`gen_index_html.h`).
|
||||
|
||||
You can also serve a custom frontend file instead of the embedded one by using:
|
||||
|
||||
```bash
|
||||
--serve-html-path <path-to-index.html>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
sd-server --serve-html-path ./index.html
|
||||
```
|
||||
|
||||
In this case, the server will load and serve the specified `index.html` file instead of the embedded frontend. This is useful when:
|
||||
|
||||
* developing or testing frontend changes
|
||||
* using a custom UI
|
||||
* avoiding rebuilding the binary after frontend modifications
|
||||
|
||||
# Run
|
||||
|
||||
```
|
||||
usage: ./bin/sd-server [options]
|
||||
|
||||
Svr Options:
|
||||
-l, --listen-ip <string> server listen ip (default: 127.0.0.1)
|
||||
--listen-port <int> server listen port (default: 1234)
|
||||
--serve-html-path <string> path to HTML file to serve at root (optional)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
-h, --help show this help message and exit
|
||||
-l, --listen-ip <string> server listen ip (default: 127.0.0.1)
|
||||
--serve-html-path <string> path to HTML file to serve at root (optional)
|
||||
--listen-port <int> server listen port (default: 1234)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
-h, --help show this help message and exit
|
||||
|
||||
Context Options:
|
||||
-m, --model <string> path to full model
|
||||
@@ -39,10 +128,10 @@ Context Options:
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--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
|
||||
--mmap whether to memory-map model
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--mmap whether to memory-map model
|
||||
--fa use flash attention
|
||||
--diffusion-fa use flash attention in the diffusion model only
|
||||
--diffusion-conv-direct use ggml_conv2d_direct in the diffusion model
|
||||
@@ -51,6 +140,7 @@ Context Options:
|
||||
--circularx enable circular RoPE wrapping on x-axis (width) only
|
||||
--circulary enable circular RoPE wrapping on y-axis (height) only
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
@@ -99,7 +189,7 @@ Default Generation Options:
|
||||
medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--eta <float> eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--high-noise-cfg-scale <float> (high noise) unconditional guidance scale: (default: 7.0)
|
||||
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||
@@ -107,7 +197,7 @@ Default Generation Options:
|
||||
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||
--high-noise-skip-layer-start <float> (high noise) SLG enabling point (default: 0.01)
|
||||
--high-noise-skip-layer-end <float> (high noise) SLG disabling point (default: 0.2)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||
--pm-style-strength <float>
|
||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||
@@ -116,21 +206,22 @@ Default Generation Options:
|
||||
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||
--disable-auto-resize-ref-image disable auto resize of ref images
|
||||
-s, --seed RNG seed (default: 42, use random seed for < 0)
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd,
|
||||
res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd, res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a
|
||||
otherwise)
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm,
|
||||
ddim_trailing, tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan,
|
||||
euler_a otherwise
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||
kl_optimal, lcm, bong_tangent], default: discrete
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||
--skip-layers layers to skip for SLG steps (default: [7,8,9])
|
||||
--high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9])
|
||||
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level)
|
||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level), 'spectrum' (UNET/DiT Chebyshev+Taylor forecasting)
|
||||
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples:
|
||||
"threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--cache-preset cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u'
|
||||
--scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g., "1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache
|
||||
--scm-policy SCM policy: 'dynamic' (default) or 'static'
|
||||
```
|
||||
|
||||
Submodule
+1
Submodule examples/server/frontend added at 1a34176cd6
+44
-10
@@ -13,6 +13,10 @@
|
||||
|
||||
#include "common/common.hpp"
|
||||
|
||||
#ifdef HAVE_INDEX_HTML
|
||||
#include "frontend/dist/gen_index_html.h"
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// ----------------------- helpers -----------------------
|
||||
@@ -266,8 +270,21 @@ void sd_log_cb(enum sd_log_level_t level, const char* log, void* data) {
|
||||
struct LoraEntry {
|
||||
std::string name;
|
||||
std::string path;
|
||||
std::string fullpath;
|
||||
};
|
||||
|
||||
void free_results(sd_image_t* result_images, int num_results) {
|
||||
if (result_images) {
|
||||
for (int i = 0; i < num_results; ++i) {
|
||||
if (result_images[i].data) {
|
||||
stbi_image_free(result_images[i].data);
|
||||
result_images[i].data = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(result_images);
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
if (argc > 1 && std::string(argv[1]) == "--version") {
|
||||
std::cout << version_string() << "\n";
|
||||
@@ -321,7 +338,8 @@ int main(int argc, const char** argv) {
|
||||
|
||||
LoraEntry e;
|
||||
e.name = p.stem().u8string();
|
||||
std::string rel = fs::relative(p, lora_dir).u8string();
|
||||
e.fullpath = p.u8string();
|
||||
std::string rel = p.lexically_relative(lora_dir).u8string();
|
||||
std::replace(rel.begin(), rel.end(), '\\', '/');
|
||||
e.path = rel;
|
||||
|
||||
@@ -340,10 +358,11 @@ int main(int argc, const char** argv) {
|
||||
}
|
||||
};
|
||||
|
||||
auto is_valid_lora_path = [&](const std::string& path) -> bool {
|
||||
auto get_lora_full_path = [&](const std::string& path) -> std::string {
|
||||
std::lock_guard<std::mutex> lock(lora_mutex);
|
||||
return std::any_of(lora_cache.begin(), lora_cache.end(),
|
||||
[&](const LoraEntry& e) { return e.path == path; });
|
||||
auto it = std::find_if(lora_cache.begin(), lora_cache.end(),
|
||||
[&](const LoraEntry& e) { return e.path == path; });
|
||||
return (it != lora_cache.end()) ? it->fullpath : "";
|
||||
};
|
||||
|
||||
httplib::Server svr;
|
||||
@@ -365,7 +384,13 @@ int main(int argc, const char** argv) {
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
|
||||
// root
|
||||
// index html
|
||||
std::string index_html;
|
||||
#ifdef HAVE_INDEX_HTML
|
||||
index_html.assign(reinterpret_cast<const char*>(index_html_bytes), index_html_size);
|
||||
#else
|
||||
index_html = "Stable Diffusion Server is running";
|
||||
#endif
|
||||
svr.Get("/", [&](const httplib::Request&, httplib::Response& res) {
|
||||
if (!svr_params.serve_html_path.empty()) {
|
||||
std::ifstream file(svr_params.serve_html_path);
|
||||
@@ -377,7 +402,7 @@ int main(int argc, const char** argv) {
|
||||
res.set_content("Error: Unable to read HTML file", "text/plain");
|
||||
}
|
||||
} else {
|
||||
res.set_content("Stable Diffusion Server is running", "text/plain");
|
||||
res.set_content(index_html, "text/html");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -534,6 +559,7 @@ int main(int argc, const char** argv) {
|
||||
item["b64_json"] = b64;
|
||||
out["data"].push_back(item);
|
||||
}
|
||||
free_results(results, num_results);
|
||||
|
||||
res.set_content(out.dump(), "application/json");
|
||||
res.status = 200;
|
||||
@@ -564,8 +590,9 @@ int main(int argc, const char** argv) {
|
||||
|
||||
std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(prompt);
|
||||
|
||||
size_t image_count = req.form.get_file_count("image[]");
|
||||
if (image_count == 0) {
|
||||
size_t image_count = req.form.get_file_count("image[]");
|
||||
bool has_legacy_image = req.form.has_file("image");
|
||||
if (image_count == 0 && !has_legacy_image) {
|
||||
res.status = 400;
|
||||
res.set_content(R"({"error":"at least one image[] required"})", "application/json");
|
||||
return;
|
||||
@@ -576,6 +603,10 @@ int main(int argc, const char** argv) {
|
||||
auto file = req.form.get_file("image[]", i);
|
||||
images_bytes.emplace_back(file.content.begin(), file.content.end());
|
||||
}
|
||||
if (image_count == 0 && has_legacy_image) {
|
||||
auto file = req.form.get_file("image");
|
||||
images_bytes.emplace_back(file.content.begin(), file.content.end());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> mask_bytes;
|
||||
if (req.form.has_file("mask")) {
|
||||
@@ -773,6 +804,7 @@ int main(int argc, const char** argv) {
|
||||
item["b64_json"] = b64;
|
||||
out["data"].push_back(item);
|
||||
}
|
||||
free_results(results, num_results);
|
||||
|
||||
res.set_content(out.dump(), "application/json");
|
||||
res.status = 200;
|
||||
@@ -862,11 +894,12 @@ int main(int argc, const char** argv) {
|
||||
return bad("lora.path required");
|
||||
}
|
||||
|
||||
if (!is_valid_lora_path(path)) {
|
||||
std::string fullpath = get_lora_full_path(path);
|
||||
if (fullpath.empty()) {
|
||||
return bad("invalid lora path: " + path);
|
||||
}
|
||||
|
||||
lora_path_storage.push_back(path);
|
||||
lora_path_storage.push_back(fullpath);
|
||||
sd_lora_t l;
|
||||
l.is_high_noise = is_high_noise;
|
||||
l.multiplier = multiplier;
|
||||
@@ -1092,6 +1125,7 @@ int main(int argc, const char** argv) {
|
||||
std::string b64 = base64_encode(image_bytes);
|
||||
out["images"].push_back(b64);
|
||||
}
|
||||
free_results(results, num_results);
|
||||
|
||||
res.set_content(out.dump(), "application/json");
|
||||
res.status = 200;
|
||||
|
||||
@@ -251,6 +251,7 @@ enum sd_cache_mode_t {
|
||||
SD_CACHE_DBCACHE,
|
||||
SD_CACHE_TAYLORSEER,
|
||||
SD_CACHE_CACHE_DIT,
|
||||
SD_CACHE_SPECTRUM,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -271,6 +272,13 @@ typedef struct {
|
||||
int taylorseer_skip_interval;
|
||||
const char* scm_mask;
|
||||
bool scm_policy_dynamic;
|
||||
float spectrum_w;
|
||||
int spectrum_m;
|
||||
float spectrum_lam;
|
||||
int spectrum_window_size;
|
||||
float spectrum_flex_window;
|
||||
int spectrum_warmup_steps;
|
||||
float spectrum_stop_percent;
|
||||
} sd_cache_params_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -0,0 +1,930 @@
|
||||
#ifndef __AUTO_ENCODER_KL_HPP__
|
||||
#define __AUTO_ENCODER_KL_HPP__
|
||||
|
||||
#include "vae.hpp"
|
||||
|
||||
/*================================================== AutoEncoderKL ===================================================*/
|
||||
|
||||
#define VAE_GRAPH_SIZE 20480
|
||||
|
||||
class ResnetBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int64_t out_channels;
|
||||
|
||||
public:
|
||||
ResnetBlock(int64_t in_channels,
|
||||
int64_t out_channels)
|
||||
: in_channels(in_channels),
|
||||
out_channels(out_channels) {
|
||||
// temb_channels is always 0
|
||||
blocks["norm1"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(in_channels));
|
||||
blocks["conv1"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
blocks["norm2"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(out_channels));
|
||||
blocks["conv2"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
if (out_channels != in_channels) {
|
||||
blocks["nin_shortcut"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, {1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w]
|
||||
// t_emb is always None
|
||||
auto norm1 = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm1"]);
|
||||
auto conv1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv1"]);
|
||||
auto norm2 = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm2"]);
|
||||
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv2"]);
|
||||
|
||||
auto h = x;
|
||||
h = norm1->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
h = conv1->forward(ctx, h);
|
||||
// return h;
|
||||
|
||||
h = norm2->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
// dropout, skip for inference
|
||||
h = conv2->forward(ctx, h);
|
||||
|
||||
// skip connection
|
||||
if (out_channels != in_channels) {
|
||||
auto nin_shortcut = std::dynamic_pointer_cast<Conv2d>(blocks["nin_shortcut"]);
|
||||
|
||||
x = nin_shortcut->forward(ctx, x); // [N, out_channels, h, w]
|
||||
}
|
||||
|
||||
h = ggml_add(ctx->ggml_ctx, h, x);
|
||||
return h; // [N, out_channels, h, w]
|
||||
}
|
||||
};
|
||||
|
||||
class AttnBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
bool use_linear;
|
||||
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, const std::string prefix = "") {
|
||||
auto iter = tensor_storage_map.find(prefix + "proj_out.weight");
|
||||
if (iter != tensor_storage_map.end()) {
|
||||
if (iter->second.n_dims == 4 && use_linear) {
|
||||
use_linear = false;
|
||||
blocks["q"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["k"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["v"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["proj_out"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
} else if (iter->second.n_dims == 2 && !use_linear) {
|
||||
use_linear = true;
|
||||
blocks["q"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["k"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["v"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["proj_out"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AttnBlock(int64_t in_channels, bool use_linear)
|
||||
: in_channels(in_channels), use_linear(use_linear) {
|
||||
blocks["norm"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(in_channels));
|
||||
if (use_linear) {
|
||||
blocks["q"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["k"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["v"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
} else {
|
||||
blocks["q"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["k"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["v"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w]
|
||||
auto norm = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm"]);
|
||||
auto q_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["q"]);
|
||||
auto k_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["k"]);
|
||||
auto v_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["v"]);
|
||||
auto proj_out = std::dynamic_pointer_cast<UnaryBlock>(blocks["proj_out"]);
|
||||
|
||||
auto h_ = norm->forward(ctx, x);
|
||||
|
||||
const int64_t n = h_->ne[3];
|
||||
const int64_t c = h_->ne[2];
|
||||
const int64_t h = h_->ne[1];
|
||||
const int64_t w = h_->ne[0];
|
||||
|
||||
ggml_tensor* q;
|
||||
ggml_tensor* k;
|
||||
ggml_tensor* v;
|
||||
if (use_linear) {
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
h_ = ggml_reshape_3d(ctx->ggml_ctx, h_, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
q = q_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
k = k_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
v = v_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
} else {
|
||||
q = q_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
q = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, q, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
q = ggml_reshape_3d(ctx->ggml_ctx, q, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
k = k_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
k = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, k, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
k = ggml_reshape_3d(ctx->ggml_ctx, k, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
v = v_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
v = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, v, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
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);
|
||||
|
||||
if (use_linear) {
|
||||
h_ = proj_out->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 0, 2, 3)); // [N, in_channels, h * w]
|
||||
h_ = ggml_reshape_4d(ctx->ggml_ctx, h_, w, h, c, n); // [N, in_channels, h, w]
|
||||
} else {
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 0, 2, 3)); // [N, in_channels, h * w]
|
||||
h_ = ggml_reshape_4d(ctx->ggml_ctx, h_, w, h, c, n); // [N, in_channels, h, w]
|
||||
|
||||
h_ = proj_out->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
}
|
||||
|
||||
h_ = ggml_add(ctx->ggml_ctx, h_, x);
|
||||
return h_;
|
||||
}
|
||||
};
|
||||
|
||||
class AE3DConv : public Conv2d {
|
||||
public:
|
||||
AE3DConv(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
std::pair<int, int> kernel_size,
|
||||
int video_kernel_size = 3,
|
||||
std::pair<int, int> stride = {1, 1},
|
||||
std::pair<int, int> padding = {0, 0},
|
||||
std::pair<int, int> dilation = {1, 1},
|
||||
bool bias = true)
|
||||
: Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) {
|
||||
int kernel_padding = video_kernel_size / 2;
|
||||
blocks["time_mix_conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(out_channels,
|
||||
out_channels,
|
||||
{video_kernel_size, 1, 1},
|
||||
{1, 1, 1},
|
||||
{kernel_padding, 0, 0}));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x) override {
|
||||
// timesteps always None
|
||||
// skip_video always False
|
||||
// x: [N, IC, IH, IW]
|
||||
// result: [N, OC, OH, OW]
|
||||
auto time_mix_conv = std::dynamic_pointer_cast<Conv3d>(blocks["time_mix_conv"]);
|
||||
|
||||
x = Conv2d::forward(ctx, x);
|
||||
// timesteps = x.shape[0]
|
||||
// x = rearrange(x, "(b t) c h w -> b c t h w", t=timesteps)
|
||||
// x = conv3d(x)
|
||||
// return rearrange(x, "b c t h w -> (b t) c h w")
|
||||
int64_t T = x->ne[3];
|
||||
int64_t B = x->ne[3] / T;
|
||||
int64_t C = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W * H, C, T, B); // (b t) c h w -> b t c (h w)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b t c (h w) -> b c t (h w)
|
||||
x = time_mix_conv->forward(ctx, x); // [B, OC, T, OH * OW]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b c t (h w) -> b t c (h w)
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, C, T * B); // b t c (h w) -> (b t) c h w
|
||||
return x; // [B*T, OC, OH, OW]
|
||||
}
|
||||
};
|
||||
|
||||
class VideoResnetBlock : public ResnetBlock {
|
||||
protected:
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, const std::string prefix = "") override {
|
||||
enum ggml_type wtype = get_type(prefix + "mix_factor", tensor_storage_map, GGML_TYPE_F32);
|
||||
params["mix_factor"] = ggml_new_tensor_1d(ctx, wtype, 1);
|
||||
}
|
||||
|
||||
float get_alpha() {
|
||||
float alpha = ggml_ext_backend_tensor_get_f32(params["mix_factor"]);
|
||||
return sigmoid(alpha);
|
||||
}
|
||||
|
||||
public:
|
||||
VideoResnetBlock(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
int video_kernel_size = 3)
|
||||
: ResnetBlock(in_channels, out_channels) {
|
||||
// merge_strategy is always learned
|
||||
blocks["time_stack"] = std::shared_ptr<GGMLBlock>(new ResBlock(out_channels, 0, out_channels, {video_kernel_size, 1}, 3, false, true));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w] aka [b*t, in_channels, h, w]
|
||||
// return: [N, out_channels, h, w] aka [b*t, out_channels, h, w]
|
||||
// t_emb is always None
|
||||
// skip_video is always False
|
||||
// timesteps is always None
|
||||
auto time_stack = std::dynamic_pointer_cast<ResBlock>(blocks["time_stack"]);
|
||||
|
||||
x = ResnetBlock::forward(ctx, x); // [N, out_channels, h, w]
|
||||
// return x;
|
||||
|
||||
int64_t T = x->ne[3];
|
||||
int64_t B = x->ne[3] / T;
|
||||
int64_t C = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W * H, C, T, B); // (b t) c h w -> b t c (h w)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b t c (h w) -> b c t (h w)
|
||||
auto x_mix = x;
|
||||
|
||||
x = time_stack->forward(ctx, x); // b t c (h w)
|
||||
|
||||
float alpha = get_alpha();
|
||||
x = ggml_add(ctx->ggml_ctx,
|
||||
ggml_ext_scale(ctx->ggml_ctx, x, alpha),
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_mix, 1.0f - alpha));
|
||||
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b c t (h w) -> b t c (h w)
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, C, T * B); // b t c (h w) -> (b t) c h w
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.modules.diffusionmodules.model.Encoder
|
||||
class Encoder : public GGMLBlock {
|
||||
protected:
|
||||
int ch = 128;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
int in_channels = 3;
|
||||
int z_channels = 4;
|
||||
bool double_z = true;
|
||||
|
||||
public:
|
||||
Encoder(int ch,
|
||||
std::vector<int> ch_mult,
|
||||
int num_res_blocks,
|
||||
int in_channels,
|
||||
int z_channels,
|
||||
bool double_z = true,
|
||||
bool use_linear_projection = false)
|
||||
: ch(ch),
|
||||
ch_mult(ch_mult),
|
||||
num_res_blocks(num_res_blocks),
|
||||
in_channels(in_channels),
|
||||
z_channels(z_channels),
|
||||
double_z(double_z) {
|
||||
blocks["conv_in"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, ch, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
|
||||
int block_in = 1;
|
||||
for (int i = 0; i < num_resolutions; i++) {
|
||||
if (i == 0) {
|
||||
block_in = ch;
|
||||
} else {
|
||||
block_in = ch * ch_mult[i - 1];
|
||||
}
|
||||
int block_out = ch * ch_mult[i];
|
||||
for (int j = 0; j < num_res_blocks; j++) {
|
||||
std::string name = "down." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_out));
|
||||
block_in = block_out;
|
||||
}
|
||||
if (i != num_resolutions - 1) {
|
||||
std::string name = "down." + std::to_string(i) + ".downsample";
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new DownSampleBlock(block_in, block_in, true));
|
||||
}
|
||||
}
|
||||
|
||||
blocks["mid.block_1"] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_in));
|
||||
blocks["mid.attn_1"] = std::shared_ptr<GGMLBlock>(new AttnBlock(block_in, use_linear_projection));
|
||||
blocks["mid.block_2"] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_in));
|
||||
|
||||
blocks["norm_out"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(block_in));
|
||||
blocks["conv_out"] = std::shared_ptr<GGMLBlock>(new Conv2d(block_in, double_z ? z_channels * 2 : z_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
virtual struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
// x: [N, in_channels, h, w]
|
||||
|
||||
auto conv_in = std::dynamic_pointer_cast<Conv2d>(blocks["conv_in"]);
|
||||
auto mid_block_1 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_1"]);
|
||||
auto mid_attn_1 = std::dynamic_pointer_cast<AttnBlock>(blocks["mid.attn_1"]);
|
||||
auto mid_block_2 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_2"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
||||
|
||||
auto h = conv_in->forward(ctx, x); // [N, ch, h, w]
|
||||
|
||||
// downsampling
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
for (int i = 0; i < num_resolutions; i++) {
|
||||
for (int j = 0; j < num_res_blocks; j++) {
|
||||
std::string name = "down." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
auto down_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||
|
||||
h = down_block->forward(ctx, h);
|
||||
}
|
||||
if (i != num_resolutions - 1) {
|
||||
std::string name = "down." + std::to_string(i) + ".downsample";
|
||||
auto down_sample = std::dynamic_pointer_cast<DownSampleBlock>(blocks[name]);
|
||||
|
||||
h = down_sample->forward(ctx, h);
|
||||
}
|
||||
}
|
||||
|
||||
// middle
|
||||
h = mid_block_1->forward(ctx, h);
|
||||
h = mid_attn_1->forward(ctx, h);
|
||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||
|
||||
// end
|
||||
h = norm_out->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // nonlinearity/swish
|
||||
h = conv_out->forward(ctx, h); // [N, z_channels*2, h, w]
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.modules.diffusionmodules.model.Decoder
|
||||
class Decoder : public GGMLBlock {
|
||||
protected:
|
||||
int ch = 128;
|
||||
int out_ch = 3;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
int z_channels = 4;
|
||||
bool video_decoder = false;
|
||||
int video_kernel_size = 3;
|
||||
|
||||
virtual std::shared_ptr<GGMLBlock> get_conv_out(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}) {
|
||||
if (video_decoder) {
|
||||
return std::shared_ptr<GGMLBlock>(new AE3DConv(in_channels, out_channels, kernel_size, video_kernel_size, stride, padding));
|
||||
} else {
|
||||
return std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, kernel_size, stride, padding));
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<GGMLBlock> get_resnet_block(int64_t in_channels,
|
||||
int64_t out_channels) {
|
||||
if (video_decoder) {
|
||||
return std::shared_ptr<GGMLBlock>(new VideoResnetBlock(in_channels, out_channels, video_kernel_size));
|
||||
} else {
|
||||
return std::shared_ptr<GGMLBlock>(new ResnetBlock(in_channels, out_channels));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Decoder(int ch,
|
||||
int out_ch,
|
||||
std::vector<int> ch_mult,
|
||||
int num_res_blocks,
|
||||
int z_channels,
|
||||
bool use_linear_projection = false,
|
||||
bool video_decoder = false,
|
||||
int video_kernel_size = 3)
|
||||
: ch(ch),
|
||||
out_ch(out_ch),
|
||||
ch_mult(ch_mult),
|
||||
num_res_blocks(num_res_blocks),
|
||||
z_channels(z_channels),
|
||||
video_decoder(video_decoder),
|
||||
video_kernel_size(video_kernel_size) {
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
int block_in = ch * ch_mult[num_resolutions - 1];
|
||||
|
||||
blocks["conv_in"] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, block_in, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
blocks["mid.block_1"] = get_resnet_block(block_in, block_in);
|
||||
blocks["mid.attn_1"] = std::shared_ptr<GGMLBlock>(new AttnBlock(block_in, use_linear_projection));
|
||||
blocks["mid.block_2"] = get_resnet_block(block_in, block_in);
|
||||
|
||||
for (int i = num_resolutions - 1; i >= 0; i--) {
|
||||
int mult = ch_mult[i];
|
||||
int block_out = ch * mult;
|
||||
for (int j = 0; j < num_res_blocks + 1; j++) {
|
||||
std::string name = "up." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
blocks[name] = get_resnet_block(block_in, block_out);
|
||||
|
||||
block_in = block_out;
|
||||
}
|
||||
if (i != 0) {
|
||||
std::string name = "up." + std::to_string(i) + ".upsample";
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new UpSampleBlock(block_in, block_in));
|
||||
}
|
||||
}
|
||||
|
||||
blocks["norm_out"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(block_in));
|
||||
blocks["conv_out"] = get_conv_out(block_in, out_ch, {3, 3}, {1, 1}, {1, 1});
|
||||
}
|
||||
|
||||
virtual struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
// z: [N, z_channels, h, w]
|
||||
// alpha is always 0
|
||||
// merge_strategy is always learned
|
||||
// time_mode is always conv-only, so we need to replace conv_out_op/resnet_op to AE3DConv/VideoResBlock
|
||||
// AttnVideoBlock will not be used
|
||||
auto conv_in = std::dynamic_pointer_cast<Conv2d>(blocks["conv_in"]);
|
||||
auto mid_block_1 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_1"]);
|
||||
auto mid_attn_1 = std::dynamic_pointer_cast<AttnBlock>(blocks["mid.attn_1"]);
|
||||
auto mid_block_2 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_2"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
||||
|
||||
// conv_in
|
||||
auto h = conv_in->forward(ctx, z); // [N, block_in, h, w]
|
||||
|
||||
// middle
|
||||
h = mid_block_1->forward(ctx, h);
|
||||
// return h;
|
||||
|
||||
h = mid_attn_1->forward(ctx, h);
|
||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||
|
||||
// upsampling
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
for (int i = num_resolutions - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < num_res_blocks + 1; j++) {
|
||||
std::string name = "up." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
auto up_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||
|
||||
h = up_block->forward(ctx, h);
|
||||
}
|
||||
if (i != 0) {
|
||||
std::string name = "up." + std::to_string(i) + ".upsample";
|
||||
auto up_sample = std::dynamic_pointer_cast<UpSampleBlock>(blocks[name]);
|
||||
|
||||
h = up_sample->forward(ctx, h);
|
||||
}
|
||||
}
|
||||
|
||||
h = norm_out->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // nonlinearity/swish
|
||||
h = conv_out->forward(ctx, h); // [N, out_ch, h*8, w*8]
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.models.autoencoder.AutoencoderKL
|
||||
class AutoEncoderKLModel : public GGMLBlock {
|
||||
protected:
|
||||
SDVersion version;
|
||||
bool decode_only = true;
|
||||
bool use_video_decoder = false;
|
||||
bool use_quant = true;
|
||||
int embed_dim = 4;
|
||||
struct {
|
||||
int z_channels = 4;
|
||||
int resolution = 256;
|
||||
int in_channels = 3;
|
||||
int out_ch = 3;
|
||||
int ch = 128;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
bool double_z = true;
|
||||
} dd_config;
|
||||
|
||||
public:
|
||||
AutoEncoderKLModel(SDVersion version = VERSION_SD1,
|
||||
bool decode_only = true,
|
||||
bool use_linear_projection = false,
|
||||
bool use_video_decoder = false)
|
||||
: version(version), decode_only(decode_only), use_video_decoder(use_video_decoder) {
|
||||
if (sd_version_is_dit(version)) {
|
||||
if (sd_version_is_flux2(version)) {
|
||||
dd_config.z_channels = 32;
|
||||
embed_dim = 32;
|
||||
} else {
|
||||
use_quant = false;
|
||||
dd_config.z_channels = 16;
|
||||
}
|
||||
}
|
||||
if (use_video_decoder) {
|
||||
use_quant = false;
|
||||
}
|
||||
blocks["decoder"] = std::shared_ptr<GGMLBlock>(new Decoder(dd_config.ch,
|
||||
dd_config.out_ch,
|
||||
dd_config.ch_mult,
|
||||
dd_config.num_res_blocks,
|
||||
dd_config.z_channels,
|
||||
use_linear_projection,
|
||||
use_video_decoder));
|
||||
if (use_quant) {
|
||||
blocks["post_quant_conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(dd_config.z_channels,
|
||||
embed_dim,
|
||||
{1, 1}));
|
||||
}
|
||||
if (!decode_only) {
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new Encoder(dd_config.ch,
|
||||
dd_config.ch_mult,
|
||||
dd_config.num_res_blocks,
|
||||
dd_config.in_channels,
|
||||
dd_config.z_channels,
|
||||
dd_config.double_z,
|
||||
use_linear_projection));
|
||||
if (use_quant) {
|
||||
int factor = dd_config.double_z ? 2 : 1;
|
||||
|
||||
blocks["quant_conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(embed_dim * factor,
|
||||
dd_config.z_channels * factor,
|
||||
{1, 1}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* decode(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
// z: [N, z_channels, h, w]
|
||||
if (sd_version_is_flux2(version)) {
|
||||
// [N, C*p*p, h, w] -> [N, C, h*p, w*p]
|
||||
int64_t p = 2;
|
||||
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2] / p / p;
|
||||
int64_t h = z->ne[1];
|
||||
int64_t w = z->ne[0];
|
||||
int64_t H = h * p;
|
||||
int64_t W = w * p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w * h, p * p, C, N); // [N, C, p*p, h*w]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, h*w, p*p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, p, w, h * C * N); // [N*C*h, w, p, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, p, w, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, W, H, C, N); // [N, C, h*p, w*p]
|
||||
}
|
||||
|
||||
if (use_quant) {
|
||||
auto post_quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["post_quant_conv"]);
|
||||
z = post_quant_conv->forward(ctx, z); // [N, z_channels, h, w]
|
||||
}
|
||||
auto decoder = std::dynamic_pointer_cast<Decoder>(blocks["decoder"]);
|
||||
|
||||
ggml_set_name(z, "bench-start");
|
||||
auto h = decoder->forward(ctx, z);
|
||||
ggml_set_name(h, "bench-end");
|
||||
return h;
|
||||
}
|
||||
|
||||
struct ggml_tensor* encode(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
// x: [N, in_channels, h, w]
|
||||
auto encoder = std::dynamic_pointer_cast<Encoder>(blocks["encoder"]);
|
||||
|
||||
auto z = encoder->forward(ctx, x); // [N, 2*z_channels, h/8, w/8]
|
||||
if (use_quant) {
|
||||
auto quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["quant_conv"]);
|
||||
z = quant_conv->forward(ctx, z); // [N, 2*embed_dim, h/8, w/8]
|
||||
}
|
||||
if (sd_version_is_flux2(version)) {
|
||||
z = ggml_ext_chunk(ctx->ggml_ctx, z, 2, 2)[0];
|
||||
|
||||
// [N, C, H, W] -> [N, C*p*p, H/p, W/p]
|
||||
int64_t p = 2;
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2];
|
||||
int64_t H = z->ne[1];
|
||||
int64_t W = z->ne[0];
|
||||
int64_t h = H / p;
|
||||
int64_t w = W / p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, w, p, h * C * N); // [N*C*h, p, w, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, w, p, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p * p, w * h, C, N); // [N, C, h*w, p*p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, p*p, h*w]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w, h, p * p * C, N); // [N, C*p*p, h*w]
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
int get_encoder_output_channels() {
|
||||
int factor = dd_config.double_z ? 2 : 1;
|
||||
return dd_config.z_channels * factor;
|
||||
}
|
||||
};
|
||||
|
||||
struct AutoEncoderKL : public VAE {
|
||||
float scale_factor = 1.f;
|
||||
float shift_factor = 0.f;
|
||||
bool decode_only = true;
|
||||
AutoEncoderKLModel ae;
|
||||
|
||||
AutoEncoderKL(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decode_only = false,
|
||||
bool use_video_decoder = false,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decode_only), VAE(version, backend, offload_params_to_cpu) {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
scale_factor = 0.18215f;
|
||||
shift_factor = 0.f;
|
||||
} else if (sd_version_is_sdxl(version)) {
|
||||
scale_factor = 0.13025f;
|
||||
shift_factor = 0.f;
|
||||
} else if (sd_version_is_sd3(version)) {
|
||||
scale_factor = 1.5305f;
|
||||
shift_factor = 0.0609f;
|
||||
} else if (sd_version_is_flux(version) || sd_version_is_z_image(version)) {
|
||||
scale_factor = 0.3611f;
|
||||
shift_factor = 0.1159f;
|
||||
} else if (sd_version_is_flux2(version)) {
|
||||
scale_factor = 1.0f;
|
||||
shift_factor = 0.f;
|
||||
}
|
||||
bool use_linear_projection = false;
|
||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!starts_with(name, prefix)) {
|
||||
continue;
|
||||
}
|
||||
if (ends_with(name, "attn_1.proj_out.weight")) {
|
||||
if (tensor_storage.n_dims == 2) {
|
||||
use_linear_projection = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ae = AutoEncoderKLModel(version, decode_only, use_linear_projection, use_video_decoder);
|
||||
ae.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
void set_conv2d_scale(float scale) override {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
ae.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->set_scale(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "vae";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) override {
|
||||
ae.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
|
||||
z = to_backend(z);
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
|
||||
struct ggml_tensor* out = decode_graph ? ae.decode(&runner_ctx, z) : ae.encode(&runner_ctx, z);
|
||||
|
||||
ggml_build_forward_expand(gf, out);
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
GGML_ASSERT(!decode_only || decode_graph);
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
// ggml_set_f32(z, 0.5f);
|
||||
// print_ggml_tensor(z);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
ggml_tensor* gaussian_latent_sample(ggml_context* work_ctx, ggml_tensor* moments, std::shared_ptr<RNG> rng) {
|
||||
// ldm.modules.distributions.distributions.DiagonalGaussianDistribution.sample
|
||||
ggml_tensor* latents = ggml_new_tensor_4d(work_ctx, moments->type, moments->ne[0], moments->ne[1], moments->ne[2] / 2, moments->ne[3]);
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, latents);
|
||||
ggml_ext_im_set_randn_f32(noise, rng);
|
||||
{
|
||||
float mean = 0;
|
||||
float logvar = 0;
|
||||
float value = 0;
|
||||
float std_ = 0;
|
||||
for (int i = 0; i < latents->ne[3]; i++) {
|
||||
for (int j = 0; j < latents->ne[2]; j++) {
|
||||
for (int k = 0; k < latents->ne[1]; k++) {
|
||||
for (int l = 0; l < latents->ne[0]; l++) {
|
||||
mean = ggml_ext_tensor_get_f32(moments, l, k, j, i);
|
||||
logvar = ggml_ext_tensor_get_f32(moments, l, k, j + (int)latents->ne[2], i);
|
||||
logvar = std::max(-30.0f, std::min(logvar, 20.0f));
|
||||
std_ = std::exp(0.5f * logvar);
|
||||
value = mean + std_ * ggml_ext_tensor_get_f32(noise, l, k, j, i);
|
||||
// printf("%d %d %d %d -> %f\n", i, j, k, l, value);
|
||||
ggml_ext_tensor_set_f32(latents, value, l, k, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return latents;
|
||||
}
|
||||
|
||||
ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) {
|
||||
if (sd_version_is_flux2(version)) {
|
||||
return vae_output;
|
||||
} else if (version == VERSION_SD1_PIX2PIX) {
|
||||
return ggml_view_3d(work_ctx,
|
||||
vae_output,
|
||||
vae_output->ne[0],
|
||||
vae_output->ne[1],
|
||||
vae_output->ne[2] / 2,
|
||||
vae_output->nb[1],
|
||||
vae_output->nb[2],
|
||||
0);
|
||||
} else {
|
||||
return gaussian_latent_sample(work_ctx, vae_output, rng);
|
||||
}
|
||||
}
|
||||
|
||||
void get_latents_mean_std_vec(ggml_tensor* latents, int channel_dim, std::vector<float>& latents_mean_vec, std::vector<float>& latents_std_vec) {
|
||||
// flux2
|
||||
if (sd_version_is_flux2(version)) {
|
||||
GGML_ASSERT(latents->ne[channel_dim] == 128);
|
||||
latents_mean_vec = {-0.0676f, -0.0715f, -0.0753f, -0.0745f, 0.0223f, 0.0180f, 0.0142f, 0.0184f,
|
||||
-0.0001f, -0.0063f, -0.0002f, -0.0031f, -0.0272f, -0.0281f, -0.0276f, -0.0290f,
|
||||
-0.0769f, -0.0672f, -0.0902f, -0.0892f, 0.0168f, 0.0152f, 0.0079f, 0.0086f,
|
||||
0.0083f, 0.0015f, 0.0003f, -0.0043f, -0.0439f, -0.0419f, -0.0438f, -0.0431f,
|
||||
-0.0102f, -0.0132f, -0.0066f, -0.0048f, -0.0311f, -0.0306f, -0.0279f, -0.0180f,
|
||||
0.0030f, 0.0015f, 0.0126f, 0.0145f, 0.0347f, 0.0338f, 0.0337f, 0.0283f,
|
||||
0.0020f, 0.0047f, 0.0047f, 0.0050f, 0.0123f, 0.0081f, 0.0081f, 0.0146f,
|
||||
0.0681f, 0.0679f, 0.0767f, 0.0732f, -0.0462f, -0.0474f, -0.0392f, -0.0511f,
|
||||
-0.0528f, -0.0477f, -0.0470f, -0.0517f, -0.0317f, -0.0316f, -0.0345f, -0.0283f,
|
||||
0.0510f, 0.0445f, 0.0578f, 0.0458f, -0.0412f, -0.0458f, -0.0487f, -0.0467f,
|
||||
-0.0088f, -0.0106f, -0.0088f, -0.0046f, -0.0376f, -0.0432f, -0.0436f, -0.0499f,
|
||||
0.0118f, 0.0166f, 0.0203f, 0.0279f, 0.0113f, 0.0129f, 0.0016f, 0.0072f,
|
||||
-0.0118f, -0.0018f, -0.0141f, -0.0054f, -0.0091f, -0.0138f, -0.0145f, -0.0187f,
|
||||
0.0323f, 0.0305f, 0.0259f, 0.0300f, 0.0540f, 0.0614f, 0.0495f, 0.0590f,
|
||||
-0.0511f, -0.0603f, -0.0478f, -0.0524f, -0.0227f, -0.0274f, -0.0154f, -0.0255f,
|
||||
-0.0572f, -0.0565f, -0.0518f, -0.0496f, 0.0116f, 0.0054f, 0.0163f, 0.0104f};
|
||||
latents_std_vec = {
|
||||
1.8029f, 1.7786f, 1.7868f, 1.7837f, 1.7717f, 1.7590f, 1.7610f, 1.7479f,
|
||||
1.7336f, 1.7373f, 1.7340f, 1.7343f, 1.8626f, 1.8527f, 1.8629f, 1.8589f,
|
||||
1.7593f, 1.7526f, 1.7556f, 1.7583f, 1.7363f, 1.7400f, 1.7355f, 1.7394f,
|
||||
1.7342f, 1.7246f, 1.7392f, 1.7304f, 1.7551f, 1.7513f, 1.7559f, 1.7488f,
|
||||
1.8449f, 1.8454f, 1.8550f, 1.8535f, 1.8240f, 1.7813f, 1.7854f, 1.7945f,
|
||||
1.8047f, 1.7876f, 1.7695f, 1.7676f, 1.7782f, 1.7667f, 1.7925f, 1.7848f,
|
||||
1.7579f, 1.7407f, 1.7483f, 1.7368f, 1.7961f, 1.7998f, 1.7920f, 1.7925f,
|
||||
1.7780f, 1.7747f, 1.7727f, 1.7749f, 1.7526f, 1.7447f, 1.7657f, 1.7495f,
|
||||
1.7775f, 1.7720f, 1.7813f, 1.7813f, 1.8162f, 1.8013f, 1.8023f, 1.8033f,
|
||||
1.7527f, 1.7331f, 1.7563f, 1.7482f, 1.7610f, 1.7507f, 1.7681f, 1.7613f,
|
||||
1.7665f, 1.7545f, 1.7828f, 1.7726f, 1.7896f, 1.7999f, 1.7864f, 1.7760f,
|
||||
1.7613f, 1.7625f, 1.7560f, 1.7577f, 1.7783f, 1.7671f, 1.7810f, 1.7799f,
|
||||
1.7201f, 1.7068f, 1.7265f, 1.7091f, 1.7793f, 1.7578f, 1.7502f, 1.7455f,
|
||||
1.7587f, 1.7500f, 1.7525f, 1.7362f, 1.7616f, 1.7572f, 1.7444f, 1.7430f,
|
||||
1.7509f, 1.7610f, 1.7634f, 1.7612f, 1.7254f, 1.7135f, 1.7321f, 1.7226f,
|
||||
1.7664f, 1.7624f, 1.7718f, 1.7664f, 1.7457f, 1.7441f, 1.7569f, 1.7530f};
|
||||
} else {
|
||||
GGML_ABORT("unknown version %d", version);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
ggml_tensor* vae_latents = ggml_dup(work_ctx, latents);
|
||||
if (sd_version_is_flux2(version)) {
|
||||
int channel_dim = 2;
|
||||
std::vector<float> latents_mean_vec;
|
||||
std::vector<float> latents_std_vec;
|
||||
get_latents_mean_std_vec(latents, channel_dim, latents_mean_vec, latents_std_vec);
|
||||
|
||||
float mean;
|
||||
float std_;
|
||||
for (int i = 0; i < latents->ne[3]; i++) {
|
||||
if (channel_dim == 3) {
|
||||
mean = latents_mean_vec[i];
|
||||
std_ = latents_std_vec[i];
|
||||
}
|
||||
for (int j = 0; j < latents->ne[2]; j++) {
|
||||
if (channel_dim == 2) {
|
||||
mean = latents_mean_vec[j];
|
||||
std_ = latents_std_vec[j];
|
||||
}
|
||||
for (int k = 0; k < latents->ne[1]; k++) {
|
||||
for (int l = 0; l < latents->ne[0]; l++) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, l, k, j, i);
|
||||
value = value * std_ / scale_factor + mean;
|
||||
ggml_ext_tensor_set_f32(vae_latents, value, l, k, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ggml_ext_tensor_iter(latents, [&](ggml_tensor* latents, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, i0, i1, i2, i3);
|
||||
value = (value / scale_factor) + shift_factor;
|
||||
ggml_ext_tensor_set_f32(vae_latents, value, i0, i1, i2, i3);
|
||||
});
|
||||
}
|
||||
return vae_latents;
|
||||
}
|
||||
|
||||
ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
ggml_tensor* diffusion_latents = ggml_dup(work_ctx, latents);
|
||||
if (sd_version_is_flux2(version)) {
|
||||
int channel_dim = 2;
|
||||
std::vector<float> latents_mean_vec;
|
||||
std::vector<float> latents_std_vec;
|
||||
get_latents_mean_std_vec(latents, channel_dim, latents_mean_vec, latents_std_vec);
|
||||
|
||||
float mean;
|
||||
float std_;
|
||||
for (int i = 0; i < latents->ne[3]; i++) {
|
||||
if (channel_dim == 3) {
|
||||
mean = latents_mean_vec[i];
|
||||
std_ = latents_std_vec[i];
|
||||
}
|
||||
for (int j = 0; j < latents->ne[2]; j++) {
|
||||
if (channel_dim == 2) {
|
||||
mean = latents_mean_vec[j];
|
||||
std_ = latents_std_vec[j];
|
||||
}
|
||||
for (int k = 0; k < latents->ne[1]; k++) {
|
||||
for (int l = 0; l < latents->ne[0]; l++) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, l, k, j, i);
|
||||
value = (value - mean) * scale_factor / std_;
|
||||
ggml_ext_tensor_set_f32(diffusion_latents, value, l, k, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ggml_ext_tensor_iter(latents, [&](ggml_tensor* latents, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, i0, i1, i2, i3);
|
||||
value = (value - shift_factor) * scale_factor;
|
||||
ggml_ext_tensor_set_f32(diffusion_latents, value, i0, i1, i2, i3);
|
||||
});
|
||||
}
|
||||
return diffusion_latents;
|
||||
}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return ae.get_encoder_output_channels();
|
||||
}
|
||||
|
||||
void test() {
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = static_cast<size_t>(10 * 1024 * 1024); // 10 MB
|
||||
params.mem_buffer = nullptr;
|
||||
params.no_alloc = false;
|
||||
|
||||
struct ggml_context* work_ctx = ggml_init(params);
|
||||
GGML_ASSERT(work_ctx != nullptr);
|
||||
|
||||
{
|
||||
// CPU, x{1, 3, 64, 64}: Pass
|
||||
// CUDA, x{1, 3, 64, 64}: Pass, but sill get wrong result for some image, may be due to interlnal nan
|
||||
// CPU, x{2, 3, 64, 64}: Wrong result
|
||||
// CUDA, x{2, 3, 64, 64}: Wrong result, and different from CPU result
|
||||
auto x = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 64, 64, 3, 2);
|
||||
ggml_set_f32(x, 0.5f);
|
||||
print_ggml_tensor(x);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
_compute(8, x, false, &out, work_ctx);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("encode test done in %lldms", t1 - t0);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// CPU, z{1, 4, 8, 8}: Pass
|
||||
// CUDA, z{1, 4, 8, 8}: Pass
|
||||
// CPU, z{3, 4, 8, 8}: Wrong result
|
||||
// CUDA, z{3, 4, 8, 8}: Wrong result, and different from CPU result
|
||||
auto z = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 8, 8, 4, 1);
|
||||
ggml_set_f32(z, 0.5f);
|
||||
print_ggml_tensor(z);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
_compute(8, z, true, &out, work_ctx);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("decode test done in %lldms", t1 - t0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif // __AUTO_ENCODER_KL_HPP__
|
||||
@@ -603,87 +603,6 @@ inline std::vector<int> generate_scm_mask(
|
||||
return mask;
|
||||
}
|
||||
|
||||
inline std::vector<int> get_scm_preset(const std::string& preset, int total_steps) {
|
||||
struct Preset {
|
||||
std::vector<int> compute_bins;
|
||||
std::vector<int> cache_bins;
|
||||
};
|
||||
|
||||
Preset slow = {{8, 3, 3, 2, 1, 1}, {1, 2, 2, 2, 3}};
|
||||
Preset medium = {{6, 2, 2, 2, 2, 1}, {1, 3, 3, 3, 3}};
|
||||
Preset fast = {{6, 1, 1, 1, 1, 1}, {1, 3, 4, 5, 4}};
|
||||
Preset ultra = {{4, 1, 1, 1, 1}, {2, 5, 6, 7}};
|
||||
|
||||
Preset* p = nullptr;
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
p = &slow;
|
||||
else if (preset == "medium" || preset == "m" || preset == "M")
|
||||
p = &medium;
|
||||
else if (preset == "fast" || preset == "f" || preset == "F")
|
||||
p = &fast;
|
||||
else if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
p = &ultra;
|
||||
else
|
||||
return {};
|
||||
|
||||
if (total_steps != 28 && total_steps > 0) {
|
||||
float scale = static_cast<float>(total_steps) / 28.0f;
|
||||
std::vector<int> scaled_compute, scaled_cache;
|
||||
|
||||
for (int v : p->compute_bins) {
|
||||
scaled_compute.push_back(std::max(1, static_cast<int>(v * scale + 0.5f)));
|
||||
}
|
||||
for (int v : p->cache_bins) {
|
||||
scaled_cache.push_back(std::max(1, static_cast<int>(v * scale + 0.5f)));
|
||||
}
|
||||
|
||||
return generate_scm_mask(scaled_compute, scaled_cache, total_steps);
|
||||
}
|
||||
|
||||
return generate_scm_mask(p->compute_bins, p->cache_bins, total_steps);
|
||||
}
|
||||
|
||||
inline float get_preset_threshold(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 0.20f;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 0.25f;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 0.30f;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 0.34f;
|
||||
return 0.08f;
|
||||
}
|
||||
|
||||
inline int get_preset_warmup(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 8;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 6;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 6;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 4;
|
||||
return 8;
|
||||
}
|
||||
|
||||
inline int get_preset_Fn(const std::string& preset) {
|
||||
if (preset == "slow" || preset == "s" || preset == "S")
|
||||
return 8;
|
||||
if (preset == "medium" || preset == "m" || preset == "M")
|
||||
return 8;
|
||||
if (preset == "fast" || preset == "f" || preset == "F")
|
||||
return 6;
|
||||
if (preset == "ultra" || preset == "u" || preset == "U")
|
||||
return 4;
|
||||
return 8;
|
||||
}
|
||||
|
||||
inline int get_preset_Bn(const std::string& preset) {
|
||||
(void)preset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void parse_dbcache_options(const std::string& opts, DBCacheConfig& cfg) {
|
||||
if (opts.empty())
|
||||
return;
|
||||
|
||||
+13
-12
@@ -103,11 +103,13 @@ namespace Flux {
|
||||
auto norm = std::dynamic_pointer_cast<QKNorm>(blocks["norm"]);
|
||||
|
||||
auto qkv = qkv_proj->forward(ctx, x);
|
||||
auto qkv_vec = ggml_ext_chunk(ctx->ggml_ctx, qkv, 3, 0, true);
|
||||
int64_t head_dim = qkv_vec[0]->ne[0] / num_heads;
|
||||
auto q = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[0], head_dim, num_heads, qkv_vec[0]->ne[1], qkv_vec[0]->ne[2]);
|
||||
auto k = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[1], head_dim, num_heads, qkv_vec[1]->ne[1], qkv_vec[1]->ne[2]);
|
||||
auto v = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[2], head_dim, num_heads, qkv_vec[2]->ne[1], qkv_vec[2]->ne[2]);
|
||||
int64_t head_dim = qkv->ne[0] / 3 / num_heads;
|
||||
auto q = ggml_view_4d(ctx->ggml_ctx, qkv, head_dim, num_heads, qkv->ne[1], qkv->ne[2],
|
||||
qkv->nb[0] * head_dim, qkv->nb[1], qkv->nb[2], 0);
|
||||
auto k = ggml_view_4d(ctx->ggml_ctx, qkv, head_dim, num_heads, qkv->ne[1], qkv->ne[2],
|
||||
qkv->nb[0] * head_dim, qkv->nb[1], qkv->nb[2], (qkv->nb[0]) * qkv->ne[0] / 3);
|
||||
auto v = ggml_view_4d(ctx->ggml_ctx, qkv, head_dim, num_heads, qkv->ne[1], qkv->ne[2],
|
||||
qkv->nb[0] * head_dim, qkv->nb[1], qkv->nb[2], (qkv->nb[0]) * 2 * qkv->ne[0] / 3);
|
||||
q = norm->query_norm(ctx, q);
|
||||
k = norm->key_norm(ctx, k);
|
||||
return {q, k, v};
|
||||
@@ -491,15 +493,14 @@ namespace Flux {
|
||||
auto x_mod = Flux::modulate(ctx->ggml_ctx, pre_norm->forward(ctx, x), mod.shift, mod.scale);
|
||||
auto qkv_mlp = linear1->forward(ctx, x_mod); // [N, n_token, hidden_size * 3 + mlp_hidden_dim*mlp_mult_factor]
|
||||
|
||||
auto q = ggml_view_3d(ctx->ggml_ctx, qkv_mlp, hidden_size, qkv_mlp->ne[1], qkv_mlp->ne[2], qkv_mlp->nb[1], qkv_mlp->nb[2], 0);
|
||||
auto k = ggml_view_3d(ctx->ggml_ctx, qkv_mlp, hidden_size, qkv_mlp->ne[1], qkv_mlp->ne[2], qkv_mlp->nb[1], qkv_mlp->nb[2], hidden_size * qkv_mlp->nb[0]);
|
||||
auto v = ggml_view_3d(ctx->ggml_ctx, qkv_mlp, hidden_size, qkv_mlp->ne[1], qkv_mlp->ne[2], qkv_mlp->nb[1], qkv_mlp->nb[2], hidden_size * 2 * qkv_mlp->nb[0]);
|
||||
|
||||
int64_t head_dim = hidden_size / num_heads;
|
||||
|
||||
q = ggml_reshape_4d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, q), head_dim, num_heads, q->ne[1], q->ne[2]); // [N, n_token, n_head, d_head]
|
||||
k = ggml_reshape_4d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, k), head_dim, num_heads, k->ne[1], k->ne[2]); // [N, n_token, n_head, d_head]
|
||||
v = ggml_reshape_4d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, v), head_dim, num_heads, v->ne[1], v->ne[2]); // [N, n_token, n_head, d_head]
|
||||
auto q = ggml_view_4d(ctx->ggml_ctx, qkv_mlp, head_dim, num_heads, qkv_mlp->ne[1], qkv_mlp->ne[2],
|
||||
qkv_mlp->nb[0] * head_dim, qkv_mlp->nb[1], qkv_mlp->nb[2], 0);
|
||||
auto k = ggml_view_4d(ctx->ggml_ctx, qkv_mlp, head_dim, num_heads, qkv_mlp->ne[1], qkv_mlp->ne[2],
|
||||
qkv_mlp->nb[0] * head_dim, qkv_mlp->nb[1], qkv_mlp->nb[2], (qkv_mlp->nb[0]) * hidden_size);
|
||||
auto v = ggml_view_4d(ctx->ggml_ctx, qkv_mlp, head_dim, num_heads, qkv_mlp->ne[1], qkv_mlp->ne[2],
|
||||
qkv_mlp->nb[0] * head_dim, qkv_mlp->nb[1], qkv_mlp->nb[2], (qkv_mlp->nb[0]) * 2 * hidden_size);
|
||||
|
||||
q = norm->query_norm(ctx, q);
|
||||
k = norm->key_norm(ctx, k);
|
||||
|
||||
+81
-25
@@ -377,6 +377,12 @@ __STATIC_INLINE__ void copy_ggml_tensor(struct ggml_tensor* dst, struct ggml_ten
|
||||
ggml_free(ctx);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ ggml_tensor* ggml_ext_dup_and_cpy_tensor(ggml_context* ctx, ggml_tensor* src) {
|
||||
ggml_tensor* dup = ggml_dup_tensor(ctx, src);
|
||||
copy_ggml_tensor(dup, src);
|
||||
return dup;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ float sigmoid(float x) {
|
||||
return 1 / (1.0f + expf(-x));
|
||||
}
|
||||
@@ -491,12 +497,16 @@ __STATIC_INLINE__ void ggml_ext_tensor_split_2d(struct ggml_tensor* input,
|
||||
int64_t height = output->ne[1];
|
||||
int64_t channels = output->ne[2];
|
||||
int64_t ne3 = output->ne[3];
|
||||
|
||||
int64_t input_width = input->ne[0];
|
||||
int64_t input_height = input->ne[1];
|
||||
|
||||
GGML_ASSERT(input->type == GGML_TYPE_F32 && output->type == GGML_TYPE_F32);
|
||||
for (int iy = 0; iy < height; iy++) {
|
||||
for (int ix = 0; ix < width; ix++) {
|
||||
for (int k = 0; k < channels; k++) {
|
||||
for (int l = 0; l < ne3; l++) {
|
||||
float value = ggml_ext_tensor_get_f32(input, ix + x, iy + y, k, l);
|
||||
float value = ggml_ext_tensor_get_f32(input, (ix + x) % input_width, (iy + y) % input_height, k, l);
|
||||
ggml_ext_tensor_set_f32(output, value, ix, iy, k, l);
|
||||
}
|
||||
}
|
||||
@@ -516,6 +526,8 @@ __STATIC_INLINE__ void ggml_ext_tensor_merge_2d(struct ggml_tensor* input,
|
||||
int y,
|
||||
int overlap_x,
|
||||
int overlap_y,
|
||||
bool circular_x,
|
||||
bool circular_y,
|
||||
int x_skip = 0,
|
||||
int y_skip = 0) {
|
||||
int64_t width = input->ne[0];
|
||||
@@ -533,12 +545,12 @@ __STATIC_INLINE__ void ggml_ext_tensor_merge_2d(struct ggml_tensor* input,
|
||||
for (int l = 0; l < ne3; l++) {
|
||||
float new_value = ggml_ext_tensor_get_f32(input, ix, iy, k, l);
|
||||
if (overlap_x > 0 || overlap_y > 0) { // blend colors in overlapped area
|
||||
float old_value = ggml_ext_tensor_get_f32(output, x + ix, y + iy, k, l);
|
||||
float old_value = ggml_ext_tensor_get_f32(output, (x + ix) % img_width, (y + iy) % img_height, k, l);
|
||||
|
||||
const float x_f_0 = (overlap_x > 0 && x > 0) ? (ix - x_skip) / float(overlap_x) : 1;
|
||||
const float x_f_1 = (overlap_x > 0 && x < (img_width - width)) ? (width - ix) / float(overlap_x) : 1;
|
||||
const float y_f_0 = (overlap_y > 0 && y > 0) ? (iy - y_skip) / float(overlap_y) : 1;
|
||||
const float y_f_1 = (overlap_y > 0 && y < (img_height - height)) ? (height - iy) / float(overlap_y) : 1;
|
||||
const float x_f_0 = (circular_x || (overlap_x > 0 && x > 0)) ? (ix - x_skip) / float(overlap_x) : 1;
|
||||
const float x_f_1 = (circular_x || (overlap_x > 0 && x < (img_width - width))) ? (width - ix) / float(overlap_x) : 1;
|
||||
const float y_f_0 = (circular_y || (overlap_y > 0 && y > 0)) ? (iy - y_skip) / float(overlap_y) : 1;
|
||||
const float y_f_1 = (circular_y || (overlap_y > 0 && y < (img_height - height))) ? (height - iy) / float(overlap_y) : 1;
|
||||
|
||||
const float x_f = std::min(std::min(x_f_0, x_f_1), 1.f);
|
||||
const float y_f = std::min(std::min(y_f_0, y_f_1), 1.f);
|
||||
@@ -546,9 +558,9 @@ __STATIC_INLINE__ void ggml_ext_tensor_merge_2d(struct ggml_tensor* input,
|
||||
ggml_ext_tensor_set_f32(
|
||||
output,
|
||||
old_value + new_value * smootherstep_f32(y_f) * smootherstep_f32(x_f),
|
||||
x + ix, y + iy, k, l);
|
||||
(x + ix) % img_width, (y + iy) % img_height, k, l);
|
||||
} else {
|
||||
ggml_ext_tensor_set_f32(output, new_value, x + ix, y + iy, k, l);
|
||||
ggml_ext_tensor_set_f32(output, new_value, (x + ix) % img_width, (y + iy) % img_height, k, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,7 +643,7 @@ __STATIC_INLINE__ struct ggml_tensor* ggml_ext_tensor_concat(struct ggml_context
|
||||
}
|
||||
|
||||
// convert values from [0, 1] to [-1, 1]
|
||||
__STATIC_INLINE__ void process_vae_input_tensor(struct ggml_tensor* src) {
|
||||
__STATIC_INLINE__ void scale_to_minus1_1(struct ggml_tensor* src) {
|
||||
int64_t nelements = ggml_nelements(src);
|
||||
float* data = (float*)src->data;
|
||||
for (int i = 0; i < nelements; i++) {
|
||||
@@ -641,7 +653,7 @@ __STATIC_INLINE__ void process_vae_input_tensor(struct ggml_tensor* src) {
|
||||
}
|
||||
|
||||
// convert values from [-1, 1] to [0, 1]
|
||||
__STATIC_INLINE__ void process_vae_output_tensor(struct ggml_tensor* src) {
|
||||
__STATIC_INLINE__ void scale_to_0_1(struct ggml_tensor* src) {
|
||||
int64_t nelements = ggml_nelements(src);
|
||||
float* data = (float*)src->data;
|
||||
for (int i = 0; i < nelements; i++) {
|
||||
@@ -773,10 +785,31 @@ __STATIC_INLINE__ void sd_tiling_calc_tiles(int& num_tiles_dim,
|
||||
float& tile_overlap_factor_dim,
|
||||
int small_dim,
|
||||
int tile_size,
|
||||
const float tile_overlap_factor) {
|
||||
const float tile_overlap_factor,
|
||||
bool circular) {
|
||||
int tile_overlap = static_cast<int>(tile_size * tile_overlap_factor);
|
||||
int non_tile_overlap = tile_size - tile_overlap;
|
||||
|
||||
if (circular) {
|
||||
// circular means the last and first tile are overlapping (wraping around)
|
||||
num_tiles_dim = small_dim / non_tile_overlap;
|
||||
|
||||
if (num_tiles_dim < 1) {
|
||||
num_tiles_dim = 1;
|
||||
}
|
||||
|
||||
tile_overlap_factor_dim = (tile_size - small_dim / num_tiles_dim) / (float)tile_size;
|
||||
|
||||
// if single tile and tile_overlap_factor is not 0, add one to ensure we have at least two overlapping tiles
|
||||
if (num_tiles_dim == 1 && tile_overlap_factor_dim > 0) {
|
||||
num_tiles_dim++;
|
||||
tile_overlap_factor_dim = 0.5;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// else, non-circular means the last and first tile are not overlapping
|
||||
|
||||
num_tiles_dim = (small_dim - tile_overlap) / non_tile_overlap;
|
||||
int overshoot_dim = ((num_tiles_dim + 1) * non_tile_overlap + tile_overlap) % small_dim;
|
||||
|
||||
@@ -805,7 +838,10 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
const int p_tile_size_x,
|
||||
const int p_tile_size_y,
|
||||
const float tile_overlap_factor,
|
||||
on_tile_process on_processing) {
|
||||
const bool circular_x,
|
||||
const bool circular_y,
|
||||
on_tile_process on_processing,
|
||||
bool slient = false) {
|
||||
output = ggml_set_f32(output, 0);
|
||||
|
||||
int input_width = (int)input->ne[0];
|
||||
@@ -829,14 +865,16 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
|
||||
int num_tiles_x;
|
||||
float tile_overlap_factor_x;
|
||||
sd_tiling_calc_tiles(num_tiles_x, tile_overlap_factor_x, small_width, p_tile_size_x, tile_overlap_factor);
|
||||
sd_tiling_calc_tiles(num_tiles_x, tile_overlap_factor_x, small_width, p_tile_size_x, tile_overlap_factor, circular_x);
|
||||
|
||||
int num_tiles_y;
|
||||
float tile_overlap_factor_y;
|
||||
sd_tiling_calc_tiles(num_tiles_y, tile_overlap_factor_y, small_height, p_tile_size_y, tile_overlap_factor);
|
||||
sd_tiling_calc_tiles(num_tiles_y, tile_overlap_factor_y, small_height, p_tile_size_y, tile_overlap_factor, circular_y);
|
||||
|
||||
LOG_DEBUG("num tiles : %d, %d ", num_tiles_x, num_tiles_y);
|
||||
LOG_DEBUG("optimal overlap : %f, %f (targeting %f)", tile_overlap_factor_x, tile_overlap_factor_y, tile_overlap_factor);
|
||||
if (!slient) {
|
||||
LOG_DEBUG("num tiles : %d, %d ", num_tiles_x, num_tiles_y);
|
||||
LOG_DEBUG("optimal overlap : %f, %f (targeting %f)", tile_overlap_factor_x, tile_overlap_factor_y, tile_overlap_factor);
|
||||
}
|
||||
|
||||
int tile_overlap_x = (int32_t)(p_tile_size_x * tile_overlap_factor_x);
|
||||
int non_tile_overlap_x = p_tile_size_x - tile_overlap_x;
|
||||
@@ -867,7 +905,9 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
params.mem_buffer = nullptr;
|
||||
params.no_alloc = false;
|
||||
|
||||
LOG_DEBUG("tile work buffer size: %.2f MB", params.mem_size / 1024.f / 1024.f);
|
||||
if (!slient) {
|
||||
LOG_DEBUG("tile work buffer size: %.2f MB", params.mem_size / 1024.f / 1024.f);
|
||||
}
|
||||
|
||||
// draft context
|
||||
struct ggml_context* tiles_ctx = ggml_init(params);
|
||||
@@ -880,14 +920,16 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
ggml_tensor* input_tile = ggml_new_tensor_4d(tiles_ctx, GGML_TYPE_F32, input_tile_size_x, input_tile_size_y, input->ne[2], input->ne[3]);
|
||||
ggml_tensor* output_tile = ggml_new_tensor_4d(tiles_ctx, GGML_TYPE_F32, output_tile_size_x, output_tile_size_y, output->ne[2], output->ne[3]);
|
||||
int num_tiles = num_tiles_x * num_tiles_y;
|
||||
LOG_DEBUG("processing %i tiles", num_tiles);
|
||||
pretty_progress(0, num_tiles, 0.0f);
|
||||
if (!slient) {
|
||||
LOG_DEBUG("processing %i tiles", num_tiles);
|
||||
pretty_progress(0, num_tiles, 0.0f);
|
||||
}
|
||||
int tile_count = 1;
|
||||
bool last_y = false, last_x = false;
|
||||
float last_time = 0.0f;
|
||||
for (int y = 0; y < small_height && !last_y; y += non_tile_overlap_y) {
|
||||
int dy = 0;
|
||||
if (y + tile_size_y >= small_height) {
|
||||
if (!circular_y && y + tile_size_y >= small_height) {
|
||||
int _y = y;
|
||||
y = small_height - tile_size_y;
|
||||
dy = _y - y;
|
||||
@@ -898,7 +940,7 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
}
|
||||
for (int x = 0; x < small_width && !last_x; x += non_tile_overlap_x) {
|
||||
int dx = 0;
|
||||
if (x + tile_size_x >= small_width) {
|
||||
if (!circular_x && x + tile_size_x >= small_width) {
|
||||
int _x = x;
|
||||
x = small_width - tile_size_x;
|
||||
dx = _x - x;
|
||||
@@ -919,7 +961,7 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
int64_t t1 = ggml_time_ms();
|
||||
ggml_ext_tensor_split_2d(input, input_tile, x_in, y_in);
|
||||
if (on_processing(input_tile, output_tile, false)) {
|
||||
ggml_ext_tensor_merge_2d(output_tile, output, x_out, y_out, overlap_x_out, overlap_y_out, dx, dy);
|
||||
ggml_ext_tensor_merge_2d(output_tile, output, x_out, y_out, overlap_x_out, overlap_y_out, circular_x, circular_y, dx, dy);
|
||||
|
||||
int64_t t2 = ggml_time_ms();
|
||||
last_time = (t2 - t1) / 1000.0f;
|
||||
@@ -931,8 +973,10 @@ __STATIC_INLINE__ void sd_tiling_non_square(ggml_tensor* input,
|
||||
}
|
||||
last_x = false;
|
||||
}
|
||||
if (tile_count < num_tiles) {
|
||||
pretty_progress(num_tiles, num_tiles, last_time);
|
||||
if (!slient) {
|
||||
if (tile_count < num_tiles) {
|
||||
pretty_progress(num_tiles, num_tiles, last_time);
|
||||
}
|
||||
}
|
||||
ggml_free(tiles_ctx);
|
||||
}
|
||||
@@ -942,8 +986,10 @@ __STATIC_INLINE__ void sd_tiling(ggml_tensor* input,
|
||||
const int scale,
|
||||
const int tile_size,
|
||||
const float tile_overlap_factor,
|
||||
const bool circular_x,
|
||||
const bool circular_y,
|
||||
on_tile_process on_processing) {
|
||||
sd_tiling_non_square(input, output, scale, tile_size, tile_size, tile_overlap_factor, on_processing);
|
||||
sd_tiling_non_square(input, output, scale, tile_size, tile_size, tile_overlap_factor, circular_x, circular_y, on_processing);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* ggml_ext_group_norm_32(struct ggml_context* ctx,
|
||||
@@ -1219,6 +1265,11 @@ __STATIC_INLINE__ struct ggml_tensor* ggml_ext_zeros(struct ggml_context* ctx,
|
||||
return ggml_ext_full(ctx, 0.f, ne0, ne1, ne2, ne3);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* ggml_ext_zeros_like(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
return ggml_ext_zeros(ctx, x->ne[0], x->ne[1], x->ne[2], x->ne[3]);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* ggml_ext_ones(struct ggml_context* ctx,
|
||||
int64_t ne0,
|
||||
int64_t ne1,
|
||||
@@ -1227,6 +1278,11 @@ __STATIC_INLINE__ struct ggml_tensor* ggml_ext_ones(struct ggml_context* ctx,
|
||||
return ggml_ext_full(ctx, 1.f, ne0, ne1, ne2, ne3);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* ggml_ext_ones_like(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
return ggml_ext_ones(ctx, x->ne[0], x->ne[1], x->ne[2], x->ne[3]);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ ggml_tensor* ggml_ext_cast_f32(ggml_context* ctx, ggml_tensor* a) {
|
||||
#ifdef SD_USE_VULKAN
|
||||
auto zero_index = ggml_get_tensor(ctx, "ggml_runner_build_in_tensor:zero_int");
|
||||
|
||||
+5
-3
@@ -1057,7 +1057,7 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
if (tensor_storage.name.find("model.diffusion_model.transformer_blocks.0.img_mod.1.weight") != std::string::npos) {
|
||||
return VERSION_QWEN_IMAGE;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.net.llm_adapter.blocks.0.cross_attn.q_proj.weight") != std::string::npos) {
|
||||
if (tensor_storage.name.find("llm_adapter.blocks.0.cross_attn.q_proj.weight") != std::string::npos) {
|
||||
return VERSION_ANIMA;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.double_stream_modulation_img.lin.weight") != std::string::npos) {
|
||||
@@ -1104,10 +1104,12 @@ SDVersion ModelLoader::get_sd_version() {
|
||||
tensor_storage.name.find("unet.mid_block.resnets.1.") != std::string::npos) {
|
||||
has_middle_block_1 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.3.1.transformer_blocks.1") != std::string::npos) {
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.3.1.transformer_blocks.1") != std::string::npos ||
|
||||
tensor_storage.name.find("unet.up_blocks.1.attentions.0.transformer_blocks.1") != std::string::npos) {
|
||||
has_output_block_311 = true;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.7.1") != std::string::npos) {
|
||||
if (tensor_storage.name.find("model.diffusion_model.output_blocks.7.1") != std::string::npos ||
|
||||
tensor_storage.name.find("unet.up_blocks.2.attentions.1") != std::string::npos) {
|
||||
has_output_block_71 = true;
|
||||
}
|
||||
if (tensor_storage.name == "cond_stage_model.transformer.text_model.embeddings.token_embedding.weight" ||
|
||||
|
||||
+15
-9
@@ -653,6 +653,14 @@ std::string convert_diffusers_dit_to_original_lumina2(std::string name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string convert_other_dit_to_original_anima(std::string name) {
|
||||
static const std::string anima_net_prefix = "net.";
|
||||
if (!starts_with(name, anima_net_prefix)) {
|
||||
name = anima_net_prefix + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string convert_diffusion_model_name(std::string name, std::string prefix, SDVersion version) {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
name = convert_diffusers_unet_to_original_sd1(name);
|
||||
@@ -664,6 +672,8 @@ std::string convert_diffusion_model_name(std::string name, std::string prefix, S
|
||||
name = convert_diffusers_dit_to_original_flux(name);
|
||||
} else if (sd_version_is_z_image(version)) {
|
||||
name = convert_diffusers_dit_to_original_lumina2(name);
|
||||
} else if (sd_version_is_anima(version)) {
|
||||
name = convert_other_dit_to_original_anima(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@@ -1094,14 +1104,6 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is_lora && sd_version_is_anima(version)) {
|
||||
static const std::string anima_diffusion_prefix = "model.diffusion_model.";
|
||||
static const std::string anima_net_prefix = "model.diffusion_model.net.";
|
||||
if (starts_with(name, anima_diffusion_prefix) && !starts_with(name, anima_net_prefix)) {
|
||||
name = anima_net_prefix + name.substr(anima_diffusion_prefix.size());
|
||||
}
|
||||
}
|
||||
|
||||
// cond_stage_model
|
||||
{
|
||||
for (const auto& prefix : cond_stage_model_prefix_vec) {
|
||||
@@ -1118,7 +1120,11 @@ std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
for (const auto& prefix : first_stage_model_prefix_vec) {
|
||||
if (starts_with(name, prefix)) {
|
||||
name = convert_first_stage_model_name(name.substr(prefix.size()), prefix);
|
||||
name = prefix + name;
|
||||
if (version == VERSION_SDXS) {
|
||||
name = "tae." + name;
|
||||
} else {
|
||||
name = prefix + name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -404,7 +404,7 @@ namespace Qwen {
|
||||
|
||||
auto t_emb = time_text_embed->forward(ctx, timestep);
|
||||
if (params.zero_cond_t) {
|
||||
auto t_emb_0 = time_text_embed->forward(ctx, ggml_ext_zeros(ctx->ggml_ctx, timestep->ne[0], timestep->ne[1], timestep->ne[2], timestep->ne[3]));
|
||||
auto t_emb_0 = time_text_embed->forward(ctx, ggml_ext_zeros_like(ctx->ggml_ctx, timestep));
|
||||
t_emb = ggml_concat(ctx->ggml_ctx, t_emb, t_emb_0, 1);
|
||||
}
|
||||
auto img = img_in->forward(ctx, x);
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#ifndef __SPECTRUM_HPP__
|
||||
#define __SPECTRUM_HPP__
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
struct SpectrumConfig {
|
||||
float w = 0.40f;
|
||||
int m = 3;
|
||||
float lam = 1.0f;
|
||||
int window_size = 2;
|
||||
float flex_window = 0.50f;
|
||||
int warmup_steps = 4;
|
||||
float stop_percent = 0.9f;
|
||||
};
|
||||
|
||||
struct SpectrumState {
|
||||
SpectrumConfig config;
|
||||
int cnt = 0;
|
||||
int num_cached = 0;
|
||||
float curr_ws = 2.0f;
|
||||
int K = 6;
|
||||
int stop_step = 0;
|
||||
int total_steps_skipped = 0;
|
||||
|
||||
std::vector<std::vector<float>> H_buf;
|
||||
std::vector<float> T_buf;
|
||||
|
||||
void init(const SpectrumConfig& cfg, size_t total_steps) {
|
||||
config = cfg;
|
||||
cnt = 0;
|
||||
num_cached = 0;
|
||||
curr_ws = (float)cfg.window_size;
|
||||
K = std::max(cfg.m + 1, 6);
|
||||
stop_step = (int)(cfg.stop_percent * (float)total_steps);
|
||||
total_steps_skipped = 0;
|
||||
H_buf.clear();
|
||||
T_buf.clear();
|
||||
}
|
||||
|
||||
float taus(int step_cnt) const {
|
||||
return (step_cnt / 50.0f) * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
bool should_predict() {
|
||||
if (cnt < config.warmup_steps)
|
||||
return false;
|
||||
if (stop_step > 0 && cnt >= stop_step)
|
||||
return false;
|
||||
if ((int)H_buf.size() < 2)
|
||||
return false;
|
||||
|
||||
int ws = std::max(1, (int)std::floor(curr_ws));
|
||||
return (num_cached + 1) % ws != 0;
|
||||
}
|
||||
|
||||
void update(const struct ggml_tensor* denoised) {
|
||||
int64_t ne = ggml_nelements(denoised);
|
||||
const float* data = (const float*)denoised->data;
|
||||
|
||||
H_buf.emplace_back(data, data + ne);
|
||||
T_buf.push_back(taus(cnt));
|
||||
|
||||
while ((int)H_buf.size() > K) {
|
||||
H_buf.erase(H_buf.begin());
|
||||
T_buf.erase(T_buf.begin());
|
||||
}
|
||||
|
||||
if (cnt >= config.warmup_steps)
|
||||
curr_ws += config.flex_window;
|
||||
|
||||
num_cached = 0;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
void predict(struct ggml_tensor* denoised) {
|
||||
int64_t F = (int64_t)H_buf[0].size();
|
||||
int K_curr = (int)H_buf.size();
|
||||
int M1 = config.m + 1;
|
||||
float tau_at = taus(cnt);
|
||||
|
||||
// Design matrix X: K_curr x M1 (Chebyshev basis)
|
||||
std::vector<float> X(K_curr * M1);
|
||||
for (int i = 0; i < K_curr; i++) {
|
||||
X[i * M1] = 1.0f;
|
||||
if (M1 > 1)
|
||||
X[i * M1 + 1] = T_buf[i];
|
||||
for (int j = 2; j < M1; j++)
|
||||
X[i * M1 + j] = 2.0f * T_buf[i] * X[i * M1 + j - 1] - X[i * M1 + j - 2];
|
||||
}
|
||||
|
||||
// x_star: Chebyshev basis at current tau
|
||||
std::vector<float> x_star(M1);
|
||||
x_star[0] = 1.0f;
|
||||
if (M1 > 1)
|
||||
x_star[1] = tau_at;
|
||||
for (int j = 2; j < M1; j++)
|
||||
x_star[j] = 2.0f * tau_at * x_star[j - 1] - x_star[j - 2];
|
||||
|
||||
// XtX = X^T X + lambda I
|
||||
std::vector<float> XtX(M1 * M1, 0.0f);
|
||||
for (int i = 0; i < M1; i++) {
|
||||
for (int j = 0; j < M1; j++) {
|
||||
float sum = 0.0f;
|
||||
for (int k = 0; k < K_curr; k++)
|
||||
sum += X[k * M1 + i] * X[k * M1 + j];
|
||||
XtX[i * M1 + j] = sum + (i == j ? config.lam : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Cholesky decomposition
|
||||
std::vector<float> L(M1 * M1, 0.0f);
|
||||
if (!cholesky_decompose(XtX.data(), L.data(), M1)) {
|
||||
float trace = 0.0f;
|
||||
for (int i = 0; i < M1; i++)
|
||||
trace += XtX[i * M1 + i];
|
||||
for (int i = 0; i < M1; i++)
|
||||
XtX[i * M1 + i] += 1e-4f * trace / M1;
|
||||
cholesky_decompose(XtX.data(), L.data(), M1);
|
||||
}
|
||||
|
||||
// Solve XtX v = x_star
|
||||
std::vector<float> v(M1);
|
||||
cholesky_solve(L.data(), x_star.data(), v.data(), M1);
|
||||
|
||||
// Prediction weights per history entry
|
||||
std::vector<float> weights(K_curr, 0.0f);
|
||||
for (int k = 0; k < K_curr; k++)
|
||||
for (int j = 0; j < M1; j++)
|
||||
weights[k] += X[k * M1 + j] * v[j];
|
||||
|
||||
// Blend Chebyshev and Taylor predictions
|
||||
float* out = (float*)denoised->data;
|
||||
float w_cheb = config.w;
|
||||
float w_taylor = 1.0f - w_cheb;
|
||||
const float* h_last = H_buf.back().data();
|
||||
const float* h_prev = H_buf[H_buf.size() - 2].data();
|
||||
|
||||
for (int64_t f = 0; f < F; f++) {
|
||||
float pred_cheb = 0.0f;
|
||||
for (int k = 0; k < K_curr; k++)
|
||||
pred_cheb += weights[k] * H_buf[k][f];
|
||||
|
||||
float pred_taylor = h_last[f] + 0.5f * (h_last[f] - h_prev[f]);
|
||||
|
||||
out[f] = w_taylor * pred_taylor + w_cheb * pred_cheb;
|
||||
}
|
||||
|
||||
num_cached++;
|
||||
total_steps_skipped++;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool cholesky_decompose(const float* A, float* L, int n) {
|
||||
std::memset(L, 0, n * n * sizeof(float));
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j <= i; j++) {
|
||||
float sum = 0.0f;
|
||||
for (int k = 0; k < j; k++)
|
||||
sum += L[i * n + k] * L[j * n + k];
|
||||
if (i == j) {
|
||||
float diag = A[i * n + i] - sum;
|
||||
if (diag <= 0.0f)
|
||||
return false;
|
||||
L[i * n + j] = std::sqrt(diag);
|
||||
} else {
|
||||
L[i * n + j] = (A[i * n + j] - sum) / L[j * n + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cholesky_solve(const float* L, const float* b, float* x, int n) {
|
||||
std::vector<float> y(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
float sum = 0.0f;
|
||||
for (int j = 0; j < i; j++)
|
||||
sum += L[i * n + j] * y[j];
|
||||
y[i] = (b[i] - sum) / L[i * n + i];
|
||||
}
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
float sum = 0.0f;
|
||||
for (int j = i + 1; j < n; j++)
|
||||
sum += L[j * n + i] * x[j];
|
||||
x[i] = (y[i] - sum) / L[i * n + i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __SPECTRUM_HPP__
|
||||
+252
-525
File diff suppressed because it is too large
Load Diff
+55
-84
@@ -442,11 +442,13 @@ protected:
|
||||
bool decode_only;
|
||||
SDVersion version;
|
||||
|
||||
public:
|
||||
int z_channels = 16;
|
||||
|
||||
public:
|
||||
TAEHV(bool decode_only = true, SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decode_only), version(version) {
|
||||
int z_channels = 16;
|
||||
int patch = 1;
|
||||
int patch = 1;
|
||||
if (version == VERSION_WAN2_2_TI2V) {
|
||||
z_channels = 48;
|
||||
patch = 2;
|
||||
@@ -494,10 +496,12 @@ protected:
|
||||
bool decode_only;
|
||||
bool taef2 = false;
|
||||
|
||||
public:
|
||||
int z_channels = 4;
|
||||
|
||||
public:
|
||||
TAESD(bool decode_only = true, SDVersion version = VERSION_SD1)
|
||||
: decode_only(decode_only) {
|
||||
int z_channels = 4;
|
||||
bool use_midblock_gn = false;
|
||||
taef2 = sd_version_is_flux2(version);
|
||||
|
||||
@@ -533,20 +537,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct TinyAutoEncoder : public GGMLRunner {
|
||||
TinyAutoEncoder(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
virtual bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) = 0;
|
||||
|
||||
virtual bool load_from_file(const std::string& file_path, int n_threads) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
};
|
||||
|
||||
struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
struct TinyImageAutoEncoder : public VAE {
|
||||
TAESD taesd;
|
||||
bool decode_only = false;
|
||||
|
||||
@@ -558,7 +549,8 @@ struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decoder_only),
|
||||
taesd(decoder_only, version),
|
||||
TinyAutoEncoder(backend, offload_params_to_cpu) {
|
||||
VAE(version, backend, offload_params_to_cpu) {
|
||||
scale_input = false;
|
||||
taesd.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
@@ -566,37 +558,26 @@ struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
return "taesd";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading taesd from '%s', decode_only = %s", file_path.c_str(), decode_only ? "true" : "false");
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> taesd_tensors;
|
||||
taesd.get_param_tensors(taesd_tensors);
|
||||
std::set<std::string> ignore_tensors;
|
||||
if (decode_only) {
|
||||
ignore_tensors.insert("encoder.");
|
||||
}
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path)) {
|
||||
LOG_ERROR("init taesd model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(taesd_tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tae tensors from model loader failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("taesd model loaded");
|
||||
return success;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taesd.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) {
|
||||
return vae_output;
|
||||
}
|
||||
|
||||
ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return taesd.z_channels;
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
z = to_backend(z);
|
||||
@@ -606,11 +587,11 @@ struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) {
|
||||
bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
@@ -619,7 +600,7 @@ struct TinyImageAutoEncoder : public TinyAutoEncoder {
|
||||
}
|
||||
};
|
||||
|
||||
struct TinyVideoAutoEncoder : public TinyAutoEncoder {
|
||||
struct TinyVideoAutoEncoder : public VAE {
|
||||
TAEHV taehv;
|
||||
bool decode_only = false;
|
||||
|
||||
@@ -631,7 +612,8 @@ struct TinyVideoAutoEncoder : public TinyAutoEncoder {
|
||||
SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decoder_only),
|
||||
taehv(decoder_only, version),
|
||||
TinyAutoEncoder(backend, offload_params_to_cpu) {
|
||||
VAE(version, backend, offload_params_to_cpu) {
|
||||
scale_input = false;
|
||||
taehv.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
@@ -639,37 +621,26 @@ struct TinyVideoAutoEncoder : public TinyAutoEncoder {
|
||||
return "taehv";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading taehv from '%s', decode_only = %s", file_path.c_str(), decode_only ? "true" : "false");
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> taehv_tensors;
|
||||
taehv.get_param_tensors(taehv_tensors);
|
||||
std::set<std::string> ignore_tensors;
|
||||
if (decode_only) {
|
||||
ignore_tensors.insert("encoder.");
|
||||
}
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init taehv model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(taehv_tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tae tensors from model loader failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("taehv model loaded");
|
||||
return success;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taehv.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) {
|
||||
return vae_output;
|
||||
}
|
||||
|
||||
ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return taehv.z_channels;
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
z = to_backend(z);
|
||||
@@ -679,11 +650,11 @@ struct TinyVideoAutoEncoder : public TinyAutoEncoder {
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) {
|
||||
bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
|
||||
+43
-13
@@ -19,6 +19,7 @@ struct UCacheConfig {
|
||||
bool adaptive_threshold = true;
|
||||
float early_step_multiplier = 0.5f;
|
||||
float late_step_multiplier = 1.5f;
|
||||
float relative_norm_gain = 1.6f;
|
||||
bool reset_error_on_compute = true;
|
||||
};
|
||||
|
||||
@@ -45,14 +46,16 @@ struct UCacheState {
|
||||
bool has_output_prev_norm = false;
|
||||
bool has_relative_transformation_rate = false;
|
||||
float relative_transformation_rate = 0.0f;
|
||||
float cumulative_change_rate = 0.0f;
|
||||
float last_input_change = 0.0f;
|
||||
bool has_last_input_change = false;
|
||||
float output_change_ema = 0.0f;
|
||||
bool has_output_change_ema = false;
|
||||
int total_steps_skipped = 0;
|
||||
int current_step_index = -1;
|
||||
int steps_computed_since_active = 0;
|
||||
int expected_total_steps = 0;
|
||||
int consecutive_skipped_steps = 0;
|
||||
float accumulated_error = 0.0f;
|
||||
float reference_output_norm = 0.0f;
|
||||
|
||||
struct BlockMetrics {
|
||||
float sum_transformation_rate = 0.0f;
|
||||
@@ -106,14 +109,16 @@ struct UCacheState {
|
||||
has_output_prev_norm = false;
|
||||
has_relative_transformation_rate = false;
|
||||
relative_transformation_rate = 0.0f;
|
||||
cumulative_change_rate = 0.0f;
|
||||
last_input_change = 0.0f;
|
||||
has_last_input_change = false;
|
||||
output_change_ema = 0.0f;
|
||||
has_output_change_ema = false;
|
||||
total_steps_skipped = 0;
|
||||
current_step_index = -1;
|
||||
steps_computed_since_active = 0;
|
||||
expected_total_steps = 0;
|
||||
consecutive_skipped_steps = 0;
|
||||
accumulated_error = 0.0f;
|
||||
reference_output_norm = 0.0f;
|
||||
block_metrics.reset();
|
||||
total_active_steps = 0;
|
||||
}
|
||||
@@ -133,7 +138,8 @@ struct UCacheState {
|
||||
if (!initialized || sigmas.size() < 2) {
|
||||
return;
|
||||
}
|
||||
size_t n_steps = sigmas.size() - 1;
|
||||
size_t n_steps = sigmas.size() - 1;
|
||||
expected_total_steps = static_cast<int>(n_steps);
|
||||
|
||||
size_t start_step = static_cast<size_t>(config.start_percent * n_steps);
|
||||
size_t end_step = static_cast<size_t>(config.end_percent * n_steps);
|
||||
@@ -207,11 +213,15 @@ struct UCacheState {
|
||||
}
|
||||
|
||||
int effective_total = estimated_total_steps;
|
||||
if (effective_total <= 0) {
|
||||
effective_total = expected_total_steps;
|
||||
}
|
||||
if (effective_total <= 0) {
|
||||
effective_total = std::max(20, steps_computed_since_active * 2);
|
||||
}
|
||||
|
||||
float progress = (effective_total > 0) ? (static_cast<float>(steps_computed_since_active) / effective_total) : 0.0f;
|
||||
progress = std::max(0.0f, std::min(1.0f, progress));
|
||||
|
||||
float multiplier = 1.0f;
|
||||
if (progress < 0.2f) {
|
||||
@@ -309,17 +319,31 @@ struct UCacheState {
|
||||
|
||||
if (has_output_prev_norm && has_relative_transformation_rate &&
|
||||
last_input_change > 0.0f && output_prev_norm > 0.0f) {
|
||||
float approx_output_change_rate = (relative_transformation_rate * last_input_change) / output_prev_norm;
|
||||
accumulated_error = accumulated_error * config.error_decay_rate + approx_output_change_rate;
|
||||
float approx_output_change = relative_transformation_rate * last_input_change;
|
||||
float approx_output_change_rate;
|
||||
if (config.use_relative_threshold) {
|
||||
float base_scale = std::max(output_prev_norm, 1e-6f);
|
||||
float dyn_scale = has_output_change_ema
|
||||
? std::max(output_change_ema * std::max(1.0f, config.relative_norm_gain), 1e-6f)
|
||||
: base_scale;
|
||||
float scale = std::sqrt(base_scale * dyn_scale);
|
||||
approx_output_change_rate = approx_output_change / scale;
|
||||
} else {
|
||||
approx_output_change_rate = approx_output_change;
|
||||
}
|
||||
// Increase estimated error with skip horizon to avoid long extrapolation streaks
|
||||
approx_output_change_rate *= (1.0f + 0.50f * consecutive_skipped_steps);
|
||||
accumulated_error = accumulated_error * config.error_decay_rate + approx_output_change_rate;
|
||||
|
||||
float effective_threshold = get_adaptive_threshold();
|
||||
if (config.use_relative_threshold && reference_output_norm > 0.0f) {
|
||||
effective_threshold = effective_threshold * reference_output_norm;
|
||||
if (!config.use_relative_threshold && output_prev_norm > 0.0f) {
|
||||
effective_threshold = effective_threshold * output_prev_norm;
|
||||
}
|
||||
|
||||
if (accumulated_error < effective_threshold) {
|
||||
skip_current_step = true;
|
||||
total_steps_skipped++;
|
||||
consecutive_skipped_steps++;
|
||||
apply_cache(cond, input, output);
|
||||
return true;
|
||||
} else if (config.reset_error_on_compute) {
|
||||
@@ -340,6 +364,8 @@ struct UCacheState {
|
||||
if (cond != anchor_condition) {
|
||||
return;
|
||||
}
|
||||
steps_computed_since_active++;
|
||||
consecutive_skipped_steps = 0;
|
||||
|
||||
size_t ne = static_cast<size_t>(ggml_nelements(input));
|
||||
float* in_data = (float*)input->data;
|
||||
@@ -359,6 +385,14 @@ struct UCacheState {
|
||||
output_change /= static_cast<float>(ne);
|
||||
}
|
||||
}
|
||||
if (std::isfinite(output_change) && output_change > 0.0f) {
|
||||
if (!has_output_change_ema) {
|
||||
output_change_ema = output_change;
|
||||
has_output_change_ema = true;
|
||||
} else {
|
||||
output_change_ema = 0.8f * output_change_ema + 0.2f * output_change;
|
||||
}
|
||||
}
|
||||
|
||||
prev_output.resize(ne);
|
||||
for (size_t i = 0; i < ne; ++i) {
|
||||
@@ -373,10 +407,6 @@ struct UCacheState {
|
||||
output_prev_norm = (ne > 0) ? (mean_abs / static_cast<float>(ne)) : 0.0f;
|
||||
has_output_prev_norm = output_prev_norm > 0.0f;
|
||||
|
||||
if (reference_output_norm == 0.0f) {
|
||||
reference_output_norm = output_prev_norm;
|
||||
}
|
||||
|
||||
if (has_last_input_change && last_input_change > 0.0f && output_change > 0.0f) {
|
||||
float rate = output_change / last_input_change;
|
||||
if (std::isfinite(rate)) {
|
||||
|
||||
+2
-1
@@ -92,7 +92,8 @@ struct UpscalerGGML {
|
||||
return esrgan_upscaler->compute(n_threads, in, &out);
|
||||
};
|
||||
int64_t t0 = ggml_time_ms();
|
||||
sd_tiling(input_image_tensor, upscaled, esrgan_upscaler->scale, esrgan_upscaler->tile_size, 0.25f, on_tiling);
|
||||
// TODO: circular upscaling?
|
||||
sd_tiling(input_image_tensor, upscaled, esrgan_upscaler->scale, esrgan_upscaler->tile_size, 0.25f, false, false, on_tiling);
|
||||
esrgan_upscaler->free_compute_buffer();
|
||||
ggml_ext_tensor_clamp_inplace(upscaled, 0.f, 1.f);
|
||||
uint8_t* upscaled_data = ggml_tensor_to_sd_image(upscaled);
|
||||
|
||||
+196
-735
@@ -3,635 +3,206 @@
|
||||
|
||||
#include "common_block.hpp"
|
||||
|
||||
/*================================================== AutoEncoderKL ===================================================*/
|
||||
|
||||
#define VAE_GRAPH_SIZE 20480
|
||||
|
||||
class ResnetBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int64_t out_channels;
|
||||
|
||||
public:
|
||||
ResnetBlock(int64_t in_channels,
|
||||
int64_t out_channels)
|
||||
: in_channels(in_channels),
|
||||
out_channels(out_channels) {
|
||||
// temb_channels is always 0
|
||||
blocks["norm1"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(in_channels));
|
||||
blocks["conv1"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
blocks["norm2"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(out_channels));
|
||||
blocks["conv2"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
if (out_channels != in_channels) {
|
||||
blocks["nin_shortcut"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, {1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w]
|
||||
// t_emb is always None
|
||||
auto norm1 = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm1"]);
|
||||
auto conv1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv1"]);
|
||||
auto norm2 = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm2"]);
|
||||
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv2"]);
|
||||
|
||||
auto h = x;
|
||||
h = norm1->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
h = conv1->forward(ctx, h);
|
||||
// return h;
|
||||
|
||||
h = norm2->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
// dropout, skip for inference
|
||||
h = conv2->forward(ctx, h);
|
||||
|
||||
// skip connection
|
||||
if (out_channels != in_channels) {
|
||||
auto nin_shortcut = std::dynamic_pointer_cast<Conv2d>(blocks["nin_shortcut"]);
|
||||
|
||||
x = nin_shortcut->forward(ctx, x); // [N, out_channels, h, w]
|
||||
}
|
||||
|
||||
h = ggml_add(ctx->ggml_ctx, h, x);
|
||||
return h; // [N, out_channels, h, w]
|
||||
}
|
||||
};
|
||||
|
||||
class AttnBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
bool use_linear;
|
||||
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, const std::string prefix = "") {
|
||||
auto iter = tensor_storage_map.find(prefix + "proj_out.weight");
|
||||
if (iter != tensor_storage_map.end()) {
|
||||
if (iter->second.n_dims == 4 && use_linear) {
|
||||
use_linear = false;
|
||||
blocks["q"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["k"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["v"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
blocks["proj_out"] = std::make_shared<Conv2d>(in_channels, in_channels, std::pair{1, 1});
|
||||
} else if (iter->second.n_dims == 2 && !use_linear) {
|
||||
use_linear = true;
|
||||
blocks["q"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["k"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["v"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
blocks["proj_out"] = std::make_shared<Linear>(in_channels, in_channels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AttnBlock(int64_t in_channels, bool use_linear)
|
||||
: in_channels(in_channels), use_linear(use_linear) {
|
||||
blocks["norm"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(in_channels));
|
||||
if (use_linear) {
|
||||
blocks["q"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["k"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["v"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, in_channels));
|
||||
} else {
|
||||
blocks["q"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["k"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["v"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, in_channels, {1, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w]
|
||||
auto norm = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm"]);
|
||||
auto q_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["q"]);
|
||||
auto k_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["k"]);
|
||||
auto v_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["v"]);
|
||||
auto proj_out = std::dynamic_pointer_cast<UnaryBlock>(blocks["proj_out"]);
|
||||
|
||||
auto h_ = norm->forward(ctx, x);
|
||||
|
||||
const int64_t n = h_->ne[3];
|
||||
const int64_t c = h_->ne[2];
|
||||
const int64_t h = h_->ne[1];
|
||||
const int64_t w = h_->ne[0];
|
||||
|
||||
ggml_tensor* q;
|
||||
ggml_tensor* k;
|
||||
ggml_tensor* v;
|
||||
if (use_linear) {
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
h_ = ggml_reshape_3d(ctx->ggml_ctx, h_, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
q = q_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
k = k_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
v = v_proj->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
} else {
|
||||
q = q_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
q = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, q, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
q = ggml_reshape_3d(ctx->ggml_ctx, q, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
k = k_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
k = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, k, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
k = ggml_reshape_3d(ctx->ggml_ctx, k, c, h * w, n); // [N, h * w, in_channels]
|
||||
|
||||
v = v_proj->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
v = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, v, 1, 2, 0, 3)); // [N, h, w, in_channels]
|
||||
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);
|
||||
|
||||
if (use_linear) {
|
||||
h_ = proj_out->forward(ctx, h_); // [N, h * w, in_channels]
|
||||
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 0, 2, 3)); // [N, in_channels, h * w]
|
||||
h_ = ggml_reshape_4d(ctx->ggml_ctx, h_, w, h, c, n); // [N, in_channels, h, w]
|
||||
} else {
|
||||
h_ = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h_, 1, 0, 2, 3)); // [N, in_channels, h * w]
|
||||
h_ = ggml_reshape_4d(ctx->ggml_ctx, h_, w, h, c, n); // [N, in_channels, h, w]
|
||||
|
||||
h_ = proj_out->forward(ctx, h_); // [N, in_channels, h, w]
|
||||
}
|
||||
|
||||
h_ = ggml_add(ctx->ggml_ctx, h_, x);
|
||||
return h_;
|
||||
}
|
||||
};
|
||||
|
||||
class AE3DConv : public Conv2d {
|
||||
public:
|
||||
AE3DConv(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
std::pair<int, int> kernel_size,
|
||||
int video_kernel_size = 3,
|
||||
std::pair<int, int> stride = {1, 1},
|
||||
std::pair<int, int> padding = {0, 0},
|
||||
std::pair<int, int> dilation = {1, 1},
|
||||
bool bias = true)
|
||||
: Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, bias) {
|
||||
int kernel_padding = video_kernel_size / 2;
|
||||
blocks["time_mix_conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(out_channels,
|
||||
out_channels,
|
||||
{video_kernel_size, 1, 1},
|
||||
{1, 1, 1},
|
||||
{kernel_padding, 0, 0}));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
struct ggml_tensor* x) override {
|
||||
// timesteps always None
|
||||
// skip_video always False
|
||||
// x: [N, IC, IH, IW]
|
||||
// result: [N, OC, OH, OW]
|
||||
auto time_mix_conv = std::dynamic_pointer_cast<Conv3d>(blocks["time_mix_conv"]);
|
||||
|
||||
x = Conv2d::forward(ctx, x);
|
||||
// timesteps = x.shape[0]
|
||||
// x = rearrange(x, "(b t) c h w -> b c t h w", t=timesteps)
|
||||
// x = conv3d(x)
|
||||
// return rearrange(x, "b c t h w -> (b t) c h w")
|
||||
int64_t T = x->ne[3];
|
||||
int64_t B = x->ne[3] / T;
|
||||
int64_t C = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W * H, C, T, B); // (b t) c h w -> b t c (h w)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b t c (h w) -> b c t (h w)
|
||||
x = time_mix_conv->forward(ctx, x); // [B, OC, T, OH * OW]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b c t (h w) -> b t c (h w)
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, C, T * B); // b t c (h w) -> (b t) c h w
|
||||
return x; // [B*T, OC, OH, OW]
|
||||
}
|
||||
};
|
||||
|
||||
class VideoResnetBlock : public ResnetBlock {
|
||||
protected:
|
||||
void init_params(struct ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, const std::string prefix = "") override {
|
||||
enum ggml_type wtype = get_type(prefix + "mix_factor", tensor_storage_map, GGML_TYPE_F32);
|
||||
params["mix_factor"] = ggml_new_tensor_1d(ctx, wtype, 1);
|
||||
}
|
||||
|
||||
float get_alpha() {
|
||||
float alpha = ggml_ext_backend_tensor_get_f32(params["mix_factor"]);
|
||||
return sigmoid(alpha);
|
||||
}
|
||||
|
||||
public:
|
||||
VideoResnetBlock(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
int video_kernel_size = 3)
|
||||
: ResnetBlock(in_channels, out_channels) {
|
||||
// merge_strategy is always learned
|
||||
blocks["time_stack"] = std::shared_ptr<GGMLBlock>(new ResBlock(out_channels, 0, out_channels, {video_kernel_size, 1}, 3, false, true));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) override {
|
||||
// x: [N, in_channels, h, w] aka [b*t, in_channels, h, w]
|
||||
// return: [N, out_channels, h, w] aka [b*t, out_channels, h, w]
|
||||
// t_emb is always None
|
||||
// skip_video is always False
|
||||
// timesteps is always None
|
||||
auto time_stack = std::dynamic_pointer_cast<ResBlock>(blocks["time_stack"]);
|
||||
|
||||
x = ResnetBlock::forward(ctx, x); // [N, out_channels, h, w]
|
||||
// return x;
|
||||
|
||||
int64_t T = x->ne[3];
|
||||
int64_t B = x->ne[3] / T;
|
||||
int64_t C = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W * H, C, T, B); // (b t) c h w -> b t c (h w)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b t c (h w) -> b c t (h w)
|
||||
auto x_mix = x;
|
||||
|
||||
x = time_stack->forward(ctx, x); // b t c (h w)
|
||||
|
||||
float alpha = get_alpha();
|
||||
x = ggml_add(ctx->ggml_ctx,
|
||||
ggml_ext_scale(ctx->ggml_ctx, x, alpha),
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_mix, 1.0f - alpha));
|
||||
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // b c t (h w) -> b t c (h w)
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, C, T * B); // b t c (h w) -> (b t) c h w
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.modules.diffusionmodules.model.Encoder
|
||||
class Encoder : public GGMLBlock {
|
||||
protected:
|
||||
int ch = 128;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
int in_channels = 3;
|
||||
int z_channels = 4;
|
||||
bool double_z = true;
|
||||
|
||||
public:
|
||||
Encoder(int ch,
|
||||
std::vector<int> ch_mult,
|
||||
int num_res_blocks,
|
||||
int in_channels,
|
||||
int z_channels,
|
||||
bool double_z = true,
|
||||
bool use_linear_projection = false)
|
||||
: ch(ch),
|
||||
ch_mult(ch_mult),
|
||||
num_res_blocks(num_res_blocks),
|
||||
in_channels(in_channels),
|
||||
z_channels(z_channels),
|
||||
double_z(double_z) {
|
||||
blocks["conv_in"] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, ch, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
|
||||
int block_in = 1;
|
||||
for (int i = 0; i < num_resolutions; i++) {
|
||||
if (i == 0) {
|
||||
block_in = ch;
|
||||
} else {
|
||||
block_in = ch * ch_mult[i - 1];
|
||||
}
|
||||
int block_out = ch * ch_mult[i];
|
||||
for (int j = 0; j < num_res_blocks; j++) {
|
||||
std::string name = "down." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_out));
|
||||
block_in = block_out;
|
||||
}
|
||||
if (i != num_resolutions - 1) {
|
||||
std::string name = "down." + std::to_string(i) + ".downsample";
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new DownSampleBlock(block_in, block_in, true));
|
||||
}
|
||||
}
|
||||
|
||||
blocks["mid.block_1"] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_in));
|
||||
blocks["mid.attn_1"] = std::shared_ptr<GGMLBlock>(new AttnBlock(block_in, use_linear_projection));
|
||||
blocks["mid.block_2"] = std::shared_ptr<GGMLBlock>(new ResnetBlock(block_in, block_in));
|
||||
|
||||
blocks["norm_out"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(block_in));
|
||||
blocks["conv_out"] = std::shared_ptr<GGMLBlock>(new Conv2d(block_in, double_z ? z_channels * 2 : z_channels, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
virtual struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
// x: [N, in_channels, h, w]
|
||||
|
||||
auto conv_in = std::dynamic_pointer_cast<Conv2d>(blocks["conv_in"]);
|
||||
auto mid_block_1 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_1"]);
|
||||
auto mid_attn_1 = std::dynamic_pointer_cast<AttnBlock>(blocks["mid.attn_1"]);
|
||||
auto mid_block_2 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_2"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
||||
|
||||
auto h = conv_in->forward(ctx, x); // [N, ch, h, w]
|
||||
|
||||
// downsampling
|
||||
size_t num_resolutions = ch_mult.size();
|
||||
for (int i = 0; i < num_resolutions; i++) {
|
||||
for (int j = 0; j < num_res_blocks; j++) {
|
||||
std::string name = "down." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
auto down_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||
|
||||
h = down_block->forward(ctx, h);
|
||||
}
|
||||
if (i != num_resolutions - 1) {
|
||||
std::string name = "down." + std::to_string(i) + ".downsample";
|
||||
auto down_sample = std::dynamic_pointer_cast<DownSampleBlock>(blocks[name]);
|
||||
|
||||
h = down_sample->forward(ctx, h);
|
||||
}
|
||||
}
|
||||
|
||||
// middle
|
||||
h = mid_block_1->forward(ctx, h);
|
||||
h = mid_attn_1->forward(ctx, h);
|
||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||
|
||||
// end
|
||||
h = norm_out->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // nonlinearity/swish
|
||||
h = conv_out->forward(ctx, h); // [N, z_channels*2, h, w]
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.modules.diffusionmodules.model.Decoder
|
||||
class Decoder : public GGMLBlock {
|
||||
protected:
|
||||
int ch = 128;
|
||||
int out_ch = 3;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
int z_channels = 4;
|
||||
bool video_decoder = false;
|
||||
int video_kernel_size = 3;
|
||||
|
||||
virtual std::shared_ptr<GGMLBlock> get_conv_out(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}) {
|
||||
if (video_decoder) {
|
||||
return std::shared_ptr<GGMLBlock>(new AE3DConv(in_channels, out_channels, kernel_size, video_kernel_size, stride, padding));
|
||||
} else {
|
||||
return std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, out_channels, kernel_size, stride, padding));
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<GGMLBlock> get_resnet_block(int64_t in_channels,
|
||||
int64_t out_channels) {
|
||||
if (video_decoder) {
|
||||
return std::shared_ptr<GGMLBlock>(new VideoResnetBlock(in_channels, out_channels, video_kernel_size));
|
||||
} else {
|
||||
return std::shared_ptr<GGMLBlock>(new ResnetBlock(in_channels, out_channels));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Decoder(int ch,
|
||||
int out_ch,
|
||||
std::vector<int> ch_mult,
|
||||
int num_res_blocks,
|
||||
int z_channels,
|
||||
bool use_linear_projection = false,
|
||||
bool video_decoder = false,
|
||||
int video_kernel_size = 3)
|
||||
: ch(ch),
|
||||
out_ch(out_ch),
|
||||
ch_mult(ch_mult),
|
||||
num_res_blocks(num_res_blocks),
|
||||
z_channels(z_channels),
|
||||
video_decoder(video_decoder),
|
||||
video_kernel_size(video_kernel_size) {
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
int block_in = ch * ch_mult[num_resolutions - 1];
|
||||
|
||||
blocks["conv_in"] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, block_in, {3, 3}, {1, 1}, {1, 1}));
|
||||
|
||||
blocks["mid.block_1"] = get_resnet_block(block_in, block_in);
|
||||
blocks["mid.attn_1"] = std::shared_ptr<GGMLBlock>(new AttnBlock(block_in, use_linear_projection));
|
||||
blocks["mid.block_2"] = get_resnet_block(block_in, block_in);
|
||||
|
||||
for (int i = num_resolutions - 1; i >= 0; i--) {
|
||||
int mult = ch_mult[i];
|
||||
int block_out = ch * mult;
|
||||
for (int j = 0; j < num_res_blocks + 1; j++) {
|
||||
std::string name = "up." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
blocks[name] = get_resnet_block(block_in, block_out);
|
||||
|
||||
block_in = block_out;
|
||||
}
|
||||
if (i != 0) {
|
||||
std::string name = "up." + std::to_string(i) + ".upsample";
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new UpSampleBlock(block_in, block_in));
|
||||
}
|
||||
}
|
||||
|
||||
blocks["norm_out"] = std::shared_ptr<GGMLBlock>(new GroupNorm32(block_in));
|
||||
blocks["conv_out"] = get_conv_out(block_in, out_ch, {3, 3}, {1, 1}, {1, 1});
|
||||
}
|
||||
|
||||
virtual struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
// z: [N, z_channels, h, w]
|
||||
// alpha is always 0
|
||||
// merge_strategy is always learned
|
||||
// time_mode is always conv-only, so we need to replace conv_out_op/resnet_op to AE3DConv/VideoResBlock
|
||||
// AttnVideoBlock will not be used
|
||||
auto conv_in = std::dynamic_pointer_cast<Conv2d>(blocks["conv_in"]);
|
||||
auto mid_block_1 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_1"]);
|
||||
auto mid_attn_1 = std::dynamic_pointer_cast<AttnBlock>(blocks["mid.attn_1"]);
|
||||
auto mid_block_2 = std::dynamic_pointer_cast<ResnetBlock>(blocks["mid.block_2"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
||||
|
||||
// conv_in
|
||||
auto h = conv_in->forward(ctx, z); // [N, block_in, h, w]
|
||||
|
||||
// middle
|
||||
h = mid_block_1->forward(ctx, h);
|
||||
// return h;
|
||||
|
||||
h = mid_attn_1->forward(ctx, h);
|
||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||
|
||||
// upsampling
|
||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||
for (int i = num_resolutions - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < num_res_blocks + 1; j++) {
|
||||
std::string name = "up." + std::to_string(i) + ".block." + std::to_string(j);
|
||||
auto up_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||
|
||||
h = up_block->forward(ctx, h);
|
||||
}
|
||||
if (i != 0) {
|
||||
std::string name = "up." + std::to_string(i) + ".upsample";
|
||||
auto up_sample = std::dynamic_pointer_cast<UpSampleBlock>(blocks[name]);
|
||||
|
||||
h = up_sample->forward(ctx, h);
|
||||
}
|
||||
}
|
||||
|
||||
h = norm_out->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // nonlinearity/swish
|
||||
h = conv_out->forward(ctx, h); // [N, out_ch, h*8, w*8]
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.models.autoencoder.AutoencoderKL
|
||||
class AutoencodingEngine : public GGMLBlock {
|
||||
struct VAE : public GGMLRunner {
|
||||
protected:
|
||||
SDVersion version;
|
||||
bool decode_only = true;
|
||||
bool use_video_decoder = false;
|
||||
bool use_quant = true;
|
||||
int embed_dim = 4;
|
||||
struct {
|
||||
int z_channels = 4;
|
||||
int resolution = 256;
|
||||
int in_channels = 3;
|
||||
int out_ch = 3;
|
||||
int ch = 128;
|
||||
std::vector<int> ch_mult = {1, 2, 4, 4};
|
||||
int num_res_blocks = 2;
|
||||
bool double_z = true;
|
||||
} dd_config;
|
||||
bool scale_input = true;
|
||||
virtual bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx) = 0;
|
||||
|
||||
public:
|
||||
AutoencodingEngine(SDVersion version = VERSION_SD1,
|
||||
bool decode_only = true,
|
||||
bool use_linear_projection = false,
|
||||
bool use_video_decoder = false)
|
||||
: version(version), decode_only(decode_only), use_video_decoder(use_video_decoder) {
|
||||
if (sd_version_is_dit(version)) {
|
||||
if (sd_version_is_flux2(version)) {
|
||||
dd_config.z_channels = 32;
|
||||
embed_dim = 32;
|
||||
VAE(SDVersion version, ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: version(version), GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
|
||||
int get_scale_factor() {
|
||||
int scale_factor = 8;
|
||||
if (version == VERSION_WAN2_2_TI2V) {
|
||||
scale_factor = 16;
|
||||
} else if (sd_version_is_flux2(version)) {
|
||||
scale_factor = 16;
|
||||
} else if (version == VERSION_CHROMA_RADIANCE) {
|
||||
scale_factor = 1;
|
||||
}
|
||||
return scale_factor;
|
||||
}
|
||||
|
||||
virtual int get_encoder_output_channels(int input_channels) = 0;
|
||||
|
||||
void get_tile_sizes(int& tile_size_x,
|
||||
int& tile_size_y,
|
||||
float& tile_overlap,
|
||||
const sd_tiling_params_t& params,
|
||||
int64_t latent_x,
|
||||
int64_t latent_y,
|
||||
float encoding_factor = 1.0f) {
|
||||
tile_overlap = std::max(std::min(params.target_overlap, 0.5f), 0.0f);
|
||||
auto get_tile_size = [&](int requested_size, float factor, int64_t latent_size) {
|
||||
const int default_tile_size = 32;
|
||||
const int min_tile_dimension = 4;
|
||||
int tile_size = default_tile_size;
|
||||
// factor <= 1 means simple fraction of the latent dimension
|
||||
// factor > 1 means number of tiles across that dimension
|
||||
if (factor > 0.f) {
|
||||
if (factor > 1.0)
|
||||
factor = 1 / (factor - factor * tile_overlap + tile_overlap);
|
||||
tile_size = static_cast<int>(std::round(latent_size * factor));
|
||||
} else if (requested_size >= min_tile_dimension) {
|
||||
tile_size = requested_size;
|
||||
}
|
||||
tile_size = static_cast<int>(tile_size * encoding_factor);
|
||||
return std::max(std::min(tile_size, static_cast<int>(latent_size)), min_tile_dimension);
|
||||
};
|
||||
|
||||
tile_size_x = get_tile_size(params.tile_size_x, params.rel_size_x, latent_x);
|
||||
tile_size_y = get_tile_size(params.tile_size_y, params.rel_size_y, latent_y);
|
||||
}
|
||||
|
||||
ggml_tensor* encode(int n_threads,
|
||||
ggml_context* work_ctx,
|
||||
ggml_tensor* x,
|
||||
sd_tiling_params_t tiling_params,
|
||||
bool circular_x = false,
|
||||
bool circular_y = false) {
|
||||
int64_t t0 = ggml_time_ms();
|
||||
ggml_tensor* result = nullptr;
|
||||
const int scale_factor = get_scale_factor();
|
||||
int64_t W = x->ne[0] / scale_factor;
|
||||
int64_t H = x->ne[1] / scale_factor;
|
||||
int channel_dim = sd_version_is_wan(version) ? 3 : 2;
|
||||
int64_t C = get_encoder_output_channels(static_cast<int>(x->ne[channel_dim]));
|
||||
int64_t ne2;
|
||||
int64_t ne3;
|
||||
if (sd_version_is_wan(version)) {
|
||||
int64_t T = x->ne[2];
|
||||
ne2 = (T - 1) / 4 + 1;
|
||||
ne3 = C;
|
||||
} else {
|
||||
ne2 = C;
|
||||
ne3 = x->ne[3];
|
||||
}
|
||||
result = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, W, H, ne2, ne3);
|
||||
|
||||
if (scale_input) {
|
||||
scale_to_minus1_1(x);
|
||||
}
|
||||
|
||||
if (sd_version_is_qwen_image(version) || sd_version_is_anima(version)) {
|
||||
x = ggml_reshape_4d(work_ctx, x, x->ne[0], x->ne[1], 1, x->ne[2] * x->ne[3]);
|
||||
}
|
||||
|
||||
if (tiling_params.enabled) {
|
||||
float tile_overlap;
|
||||
int tile_size_x, tile_size_y;
|
||||
// multiply tile size for encode to keep the compute buffer size consistent
|
||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, W, H, 1.30539f);
|
||||
|
||||
LOG_DEBUG("VAE Tile size: %dx%d", tile_size_x, tile_size_y);
|
||||
|
||||
auto on_tiling = [&](ggml_tensor* in, ggml_tensor* out, bool init) {
|
||||
return _compute(n_threads, in, false, &out, work_ctx);
|
||||
};
|
||||
sd_tiling_non_square(x, result, scale_factor, tile_size_x, tile_size_y, tile_overlap, circular_x, circular_y, on_tiling);
|
||||
} else {
|
||||
_compute(n_threads, x, false, &result, work_ctx);
|
||||
}
|
||||
free_compute_buffer();
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing vae encode graph completed, taking %.2fs", (t1 - t0) * 1.0f / 1000);
|
||||
return result;
|
||||
}
|
||||
|
||||
ggml_tensor* decode(int n_threads,
|
||||
ggml_context* work_ctx,
|
||||
ggml_tensor* x,
|
||||
sd_tiling_params_t tiling_params,
|
||||
bool decode_video = false,
|
||||
bool circular_x = false,
|
||||
bool circular_y = false,
|
||||
ggml_tensor* result = nullptr,
|
||||
bool silent = false) {
|
||||
const int scale_factor = get_scale_factor();
|
||||
int64_t W = x->ne[0] * scale_factor;
|
||||
int64_t H = x->ne[1] * scale_factor;
|
||||
int64_t C = 3;
|
||||
if (result == nullptr) {
|
||||
if (decode_video) {
|
||||
int64_t T = x->ne[2];
|
||||
if (sd_version_is_wan(version)) {
|
||||
T = ((T - 1) * 4) + 1;
|
||||
}
|
||||
result = ggml_new_tensor_4d(work_ctx,
|
||||
GGML_TYPE_F32,
|
||||
W,
|
||||
H,
|
||||
T,
|
||||
3);
|
||||
} else {
|
||||
use_quant = false;
|
||||
dd_config.z_channels = 16;
|
||||
result = ggml_new_tensor_4d(work_ctx,
|
||||
GGML_TYPE_F32,
|
||||
W,
|
||||
H,
|
||||
C,
|
||||
x->ne[3]);
|
||||
}
|
||||
}
|
||||
if (use_video_decoder) {
|
||||
use_quant = false;
|
||||
int64_t t0 = ggml_time_ms();
|
||||
if (sd_version_is_qwen_image(version) || sd_version_is_anima(version)) {
|
||||
x = ggml_reshape_4d(work_ctx, x, x->ne[0], x->ne[1], 1, x->ne[2] * x->ne[3]);
|
||||
}
|
||||
blocks["decoder"] = std::shared_ptr<GGMLBlock>(new Decoder(dd_config.ch,
|
||||
dd_config.out_ch,
|
||||
dd_config.ch_mult,
|
||||
dd_config.num_res_blocks,
|
||||
dd_config.z_channels,
|
||||
use_linear_projection,
|
||||
use_video_decoder));
|
||||
if (use_quant) {
|
||||
blocks["post_quant_conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(dd_config.z_channels,
|
||||
embed_dim,
|
||||
{1, 1}));
|
||||
}
|
||||
if (!decode_only) {
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new Encoder(dd_config.ch,
|
||||
dd_config.ch_mult,
|
||||
dd_config.num_res_blocks,
|
||||
dd_config.in_channels,
|
||||
dd_config.z_channels,
|
||||
dd_config.double_z,
|
||||
use_linear_projection));
|
||||
if (use_quant) {
|
||||
int factor = dd_config.double_z ? 2 : 1;
|
||||
if (tiling_params.enabled) {
|
||||
float tile_overlap;
|
||||
int tile_size_x, tile_size_y;
|
||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, x->ne[0], x->ne[1]);
|
||||
|
||||
blocks["quant_conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(embed_dim * factor,
|
||||
dd_config.z_channels * factor,
|
||||
{1, 1}));
|
||||
if (!silent) {
|
||||
LOG_DEBUG("VAE Tile size: %dx%d", tile_size_x, tile_size_y);
|
||||
}
|
||||
|
||||
auto on_tiling = [&](ggml_tensor* in, ggml_tensor* out, bool init) {
|
||||
return _compute(n_threads, in, true, &out, nullptr);
|
||||
};
|
||||
sd_tiling_non_square(x, result, scale_factor, tile_size_x, tile_size_y, tile_overlap, circular_x, circular_y, on_tiling, silent);
|
||||
} else {
|
||||
if (!_compute(n_threads, x, true, &result, work_ctx)) {
|
||||
LOG_ERROR("Failed to decode latetnts");
|
||||
free_compute_buffer();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
free_compute_buffer();
|
||||
if (scale_input) {
|
||||
scale_to_0_1(result);
|
||||
}
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing vae decode graph completed, taking %.2fs", (t1 - t0) * 1.0f / 1000);
|
||||
ggml_ext_tensor_clamp_inplace(result, 0.0f, 1.0f);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ggml_tensor* decode(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
// z: [N, z_channels, h, w]
|
||||
if (sd_version_is_flux2(version)) {
|
||||
// [N, C*p*p, h, w] -> [N, C, h*p, w*p]
|
||||
int64_t p = 2;
|
||||
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2] / p / p;
|
||||
int64_t h = z->ne[1];
|
||||
int64_t w = z->ne[0];
|
||||
int64_t H = h * p;
|
||||
int64_t W = w * p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w * h, p * p, C, N); // [N, C, p*p, h*w]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, h*w, p*p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, p, w, h * C * N); // [N*C*h, w, p, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, p, w, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, W, H, C, N); // [N, C, h*p, w*p]
|
||||
}
|
||||
|
||||
if (use_quant) {
|
||||
auto post_quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["post_quant_conv"]);
|
||||
z = post_quant_conv->forward(ctx, z); // [N, z_channels, h, w]
|
||||
}
|
||||
auto decoder = std::dynamic_pointer_cast<Decoder>(blocks["decoder"]);
|
||||
|
||||
ggml_set_name(z, "bench-start");
|
||||
auto h = decoder->forward(ctx, z);
|
||||
ggml_set_name(h, "bench-end");
|
||||
return h;
|
||||
}
|
||||
|
||||
struct ggml_tensor* encode(GGMLRunnerContext* ctx, struct ggml_tensor* x) {
|
||||
// x: [N, in_channels, h, w]
|
||||
auto encoder = std::dynamic_pointer_cast<Encoder>(blocks["encoder"]);
|
||||
|
||||
auto z = encoder->forward(ctx, x); // [N, 2*z_channels, h/8, w/8]
|
||||
if (use_quant) {
|
||||
auto quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["quant_conv"]);
|
||||
z = quant_conv->forward(ctx, z); // [N, 2*embed_dim, h/8, w/8]
|
||||
}
|
||||
if (sd_version_is_flux2(version)) {
|
||||
z = ggml_ext_chunk(ctx->ggml_ctx, z, 2, 2)[0];
|
||||
|
||||
// [N, C, H, W] -> [N, C*p*p, H/p, W/p]
|
||||
int64_t p = 2;
|
||||
int64_t N = z->ne[3];
|
||||
int64_t C = z->ne[2];
|
||||
int64_t H = z->ne[1];
|
||||
int64_t W = z->ne[0];
|
||||
int64_t h = H / p;
|
||||
int64_t w = W / p;
|
||||
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p, w, p, h * C * N); // [N*C*h, p, w, p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 2, 1, 3)); // [N*C*h, w, p, p]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, p * p, w * h, C, N); // [N, C, h*w, p*p]
|
||||
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 1, 0, 2, 3)); // [N, C, p*p, h*w]
|
||||
z = ggml_reshape_4d(ctx->ggml_ctx, z, w, h, p * p * C, N); // [N, C*p*p, h*w]
|
||||
}
|
||||
return z;
|
||||
}
|
||||
};
|
||||
|
||||
struct VAE : public GGMLRunner {
|
||||
VAE(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
virtual bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
virtual ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) = 0;
|
||||
virtual ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) = 0;
|
||||
virtual ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
virtual void set_conv2d_scale(float scale) { SD_UNUSED(scale); };
|
||||
};
|
||||
|
||||
struct FakeVAE : public VAE {
|
||||
FakeVAE(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: VAE(backend, offload_params_to_cpu) {}
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx) override {
|
||||
FakeVAE(SDVersion version, ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: VAE(version, backend, offload_params_to_cpu) {}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return input_channels;
|
||||
}
|
||||
|
||||
bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx) override {
|
||||
if (*output == nullptr && output_ctx != nullptr) {
|
||||
*output = ggml_dup_tensor(output_ctx, z);
|
||||
}
|
||||
@@ -642,6 +213,18 @@ struct FakeVAE : public VAE {
|
||||
return true;
|
||||
}
|
||||
|
||||
ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) {
|
||||
return vae_output;
|
||||
}
|
||||
|
||||
ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
return ggml_ext_dup_and_cpy_tensor(work_ctx, latents);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) override {}
|
||||
|
||||
std::string get_desc() override {
|
||||
@@ -649,126 +232,4 @@ struct FakeVAE : public VAE {
|
||||
}
|
||||
};
|
||||
|
||||
struct AutoEncoderKL : public VAE {
|
||||
bool decode_only = true;
|
||||
AutoencodingEngine ae;
|
||||
|
||||
AutoEncoderKL(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decode_only = false,
|
||||
bool use_video_decoder = false,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decode_only), VAE(backend, offload_params_to_cpu) {
|
||||
bool use_linear_projection = false;
|
||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!starts_with(name, prefix)) {
|
||||
continue;
|
||||
}
|
||||
if (ends_with(name, "attn_1.proj_out.weight")) {
|
||||
if (tensor_storage.n_dims == 2) {
|
||||
use_linear_projection = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ae = AutoencodingEngine(version, decode_only, use_linear_projection, use_video_decoder);
|
||||
ae.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
void set_conv2d_scale(float scale) override {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
ae.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->set_scale(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "vae";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) override {
|
||||
ae.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
|
||||
z = to_backend(z);
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
|
||||
struct ggml_tensor* out = decode_graph ? ae.decode(&runner_ctx, z) : ae.encode(&runner_ctx, z);
|
||||
|
||||
ggml_build_forward_expand(gf, out);
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
GGML_ASSERT(!decode_only || decode_graph);
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
// ggml_set_f32(z, 0.5f);
|
||||
// print_ggml_tensor(z);
|
||||
return GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = static_cast<size_t>(10 * 1024 * 1024); // 10 MB
|
||||
params.mem_buffer = nullptr;
|
||||
params.no_alloc = false;
|
||||
|
||||
struct ggml_context* work_ctx = ggml_init(params);
|
||||
GGML_ASSERT(work_ctx != nullptr);
|
||||
|
||||
{
|
||||
// CPU, x{1, 3, 64, 64}: Pass
|
||||
// CUDA, x{1, 3, 64, 64}: Pass, but sill get wrong result for some image, may be due to interlnal nan
|
||||
// CPU, x{2, 3, 64, 64}: Wrong result
|
||||
// CUDA, x{2, 3, 64, 64}: Wrong result, and different from CPU result
|
||||
auto x = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 64, 64, 3, 2);
|
||||
ggml_set_f32(x, 0.5f);
|
||||
print_ggml_tensor(x);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, x, false, &out, work_ctx);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("encode test done in %lldms", t1 - t0);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// CPU, z{1, 4, 8, 8}: Pass
|
||||
// CUDA, z{1, 4, 8, 8}: Pass
|
||||
// CPU, z{3, 4, 8, 8}: Wrong result
|
||||
// CUDA, z{3, 4, 8, 8}: Wrong result, and different from CPU result
|
||||
auto z = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 8, 8, 4, 1);
|
||||
ggml_set_f32(z, 0.5f);
|
||||
print_ggml_tensor(z);
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, z, true, &out, work_ctx);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("decode test done in %lldms", t1 - t0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // __VAE_HPP__
|
||||
|
||||
+104
-8
@@ -1109,7 +1109,8 @@ namespace WAN {
|
||||
};
|
||||
|
||||
struct WanVAERunner : public VAE {
|
||||
bool decode_only = true;
|
||||
float scale_factor = 1.0f;
|
||||
bool decode_only = true;
|
||||
WanVAE ae;
|
||||
|
||||
WanVAERunner(ggml_backend_t backend,
|
||||
@@ -1118,7 +1119,7 @@ namespace WAN {
|
||||
const std::string prefix = "",
|
||||
bool decode_only = false,
|
||||
SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decode_only), ae(decode_only, version == VERSION_WAN2_2_TI2V), VAE(backend, offload_params_to_cpu) {
|
||||
: decode_only(decode_only), ae(decode_only, version == VERSION_WAN2_2_TI2V), VAE(version, backend, offload_params_to_cpu) {
|
||||
ae.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
@@ -1130,6 +1131,101 @@ namespace WAN {
|
||||
ae.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
ggml_tensor* vae_output_to_latents(ggml_context* work_ctx, ggml_tensor* vae_output, std::shared_ptr<RNG> rng) {
|
||||
return vae_output;
|
||||
}
|
||||
|
||||
void get_latents_mean_std_vec(ggml_tensor* latents, int channel_dim, std::vector<float>& latents_mean_vec, std::vector<float>& latents_std_vec) {
|
||||
GGML_ASSERT(latents->ne[channel_dim] == 16 || latents->ne[channel_dim] == 48);
|
||||
if (latents->ne[channel_dim] == 16) { // Wan2.1 VAE
|
||||
latents_mean_vec = {-0.7571f, -0.7089f, -0.9113f, 0.1075f, -0.1745f, 0.9653f, -0.1517f, 1.5508f,
|
||||
0.4134f, -0.0715f, 0.5517f, -0.3632f, -0.1922f, -0.9497f, 0.2503f, -0.2921f};
|
||||
latents_std_vec = {2.8184f, 1.4541f, 2.3275f, 2.6558f, 1.2196f, 1.7708f, 2.6052f, 2.0743f,
|
||||
3.2687f, 2.1526f, 2.8652f, 1.5579f, 1.6382f, 1.1253f, 2.8251f, 1.9160f};
|
||||
} else if (latents->ne[channel_dim] == 48) { // Wan2.2 VAE
|
||||
latents_mean_vec = {-0.2289f, -0.0052f, -0.1323f, -0.2339f, -0.2799f, 0.0174f, 0.1838f, 0.1557f,
|
||||
-0.1382f, 0.0542f, 0.2813f, 0.0891f, 0.1570f, -0.0098f, 0.0375f, -0.1825f,
|
||||
-0.2246f, -0.1207f, -0.0698f, 0.5109f, 0.2665f, -0.2108f, -0.2158f, 0.2502f,
|
||||
-0.2055f, -0.0322f, 0.1109f, 0.1567f, -0.0729f, 0.0899f, -0.2799f, -0.1230f,
|
||||
-0.0313f, -0.1649f, 0.0117f, 0.0723f, -0.2839f, -0.2083f, -0.0520f, 0.3748f,
|
||||
0.0152f, 0.1957f, 0.1433f, -0.2944f, 0.3573f, -0.0548f, -0.1681f, -0.0667f};
|
||||
latents_std_vec = {
|
||||
0.4765f, 1.0364f, 0.4514f, 1.1677f, 0.5313f, 0.4990f, 0.4818f, 0.5013f,
|
||||
0.8158f, 1.0344f, 0.5894f, 1.0901f, 0.6885f, 0.6165f, 0.8454f, 0.4978f,
|
||||
0.5759f, 0.3523f, 0.7135f, 0.6804f, 0.5833f, 1.4146f, 0.8986f, 0.5659f,
|
||||
0.7069f, 0.5338f, 0.4889f, 0.4917f, 0.4069f, 0.4999f, 0.6866f, 0.4093f,
|
||||
0.5709f, 0.6065f, 0.6415f, 0.4944f, 0.5726f, 1.2042f, 0.5458f, 1.6887f,
|
||||
0.3971f, 1.0600f, 0.3943f, 0.5537f, 0.5444f, 0.4089f, 0.7468f, 0.7744f};
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* diffusion_to_vae_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
ggml_tensor* vae_latents = ggml_dup(work_ctx, latents);
|
||||
int channel_dim = sd_version_is_wan(version) ? 3 : 2;
|
||||
std::vector<float> latents_mean_vec;
|
||||
std::vector<float> latents_std_vec;
|
||||
get_latents_mean_std_vec(latents, channel_dim, latents_mean_vec, latents_std_vec);
|
||||
|
||||
float mean;
|
||||
float std_;
|
||||
for (int i = 0; i < latents->ne[3]; i++) {
|
||||
if (channel_dim == 3) {
|
||||
mean = latents_mean_vec[i];
|
||||
std_ = latents_std_vec[i];
|
||||
}
|
||||
for (int j = 0; j < latents->ne[2]; j++) {
|
||||
if (channel_dim == 2) {
|
||||
mean = latents_mean_vec[j];
|
||||
std_ = latents_std_vec[j];
|
||||
}
|
||||
for (int k = 0; k < latents->ne[1]; k++) {
|
||||
for (int l = 0; l < latents->ne[0]; l++) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, l, k, j, i);
|
||||
value = value * std_ / scale_factor + mean;
|
||||
ggml_ext_tensor_set_f32(vae_latents, value, l, k, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vae_latents;
|
||||
}
|
||||
|
||||
ggml_tensor* vae_to_diffuison_latents(ggml_context* work_ctx, ggml_tensor* latents) {
|
||||
ggml_tensor* diffusion_latents = ggml_dup(work_ctx, latents);
|
||||
int channel_dim = sd_version_is_wan(version) ? 3 : 2;
|
||||
std::vector<float> latents_mean_vec;
|
||||
std::vector<float> latents_std_vec;
|
||||
get_latents_mean_std_vec(latents, channel_dim, latents_mean_vec, latents_std_vec);
|
||||
|
||||
float mean;
|
||||
float std_;
|
||||
for (int i = 0; i < latents->ne[3]; i++) {
|
||||
if (channel_dim == 3) {
|
||||
mean = latents_mean_vec[i];
|
||||
std_ = latents_std_vec[i];
|
||||
}
|
||||
for (int j = 0; j < latents->ne[2]; j++) {
|
||||
if (channel_dim == 2) {
|
||||
mean = latents_mean_vec[j];
|
||||
std_ = latents_std_vec[j];
|
||||
}
|
||||
for (int k = 0; k < latents->ne[1]; k++) {
|
||||
for (int l = 0; l < latents->ne[0]; l++) {
|
||||
float value = ggml_ext_tensor_get_f32(latents, l, k, j, i);
|
||||
value = (value - mean) * scale_factor / std_;
|
||||
ggml_ext_tensor_set_f32(diffusion_latents, value, l, k, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffusion_latents;
|
||||
}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return static_cast<int>(ae.z_dim);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* z, bool decode_graph) {
|
||||
struct ggml_cgraph* gf = new_graph_custom(10240 * z->ne[2]);
|
||||
|
||||
@@ -1173,11 +1269,11 @@ namespace WAN {
|
||||
return gf;
|
||||
}
|
||||
|
||||
bool compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
bool _compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = nullptr) override {
|
||||
if (true) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
@@ -1249,7 +1345,7 @@ namespace WAN {
|
||||
struct ggml_tensor* out = nullptr;
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
compute(8, z, true, &out, work_ctx);
|
||||
_compute(8, z, true, &out, work_ctx);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
|
||||
Reference in New Issue
Block a user