mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-10-03 10:48:48 +08:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2df4e1228 | ||
|
|
9838264c49 | ||
|
|
17d70b91e6 | ||
|
|
5db680c2c7 | ||
|
|
749186c0eb | ||
|
|
bdb431ad95 | ||
|
|
276025e054 | ||
|
|
8d4c7af95b | ||
|
|
9b0fceb41b | ||
|
|
563137a592 | ||
|
|
3a54597776 | ||
|
|
1365008348 | ||
|
|
1fb6b22850 | ||
|
|
c20769b2c8 | ||
|
|
1b702a51e7 |
@@ -204,6 +204,12 @@ if(SD_WEBM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (SD_RPC)
|
||||
message("-- Use RPC as backend stable-diffusion")
|
||||
set(GGML_RPC ON)
|
||||
add_definitions(-DSD_USE_RPC)
|
||||
endif ()
|
||||
|
||||
set(SD_LIB stable-diffusion)
|
||||
|
||||
file(GLOB SD_LIB_SOURCES CONFIGURE_DEPENDS
|
||||
|
||||
+29
-7
@@ -3,7 +3,7 @@
|
||||
`stable-diffusion.cpp` has two backend assignments:
|
||||
|
||||
- `--backend` selects the runtime backend used to execute model graphs.
|
||||
- `--params-backend` selects the backend used to allocate model parameters.
|
||||
- `--params-backend` selects where model parameters are kept.
|
||||
|
||||
If `--params-backend` is not set, parameters use the same backend as their module runtime backend.
|
||||
|
||||
@@ -29,6 +29,12 @@ The same syntax is used for parameter placement:
|
||||
sd-cli -m model.safetensors -p "a cat" --backend cuda0 --params-backend te=cpu,vae=cpu
|
||||
```
|
||||
|
||||
`--params-backend` also accepts the special value `disk`:
|
||||
|
||||
```shell
|
||||
sd-cli -m model.safetensors -p "a cat" --backend cuda0 --params-backend disk
|
||||
```
|
||||
|
||||
Module names are case-insensitive. Hyphens and underscores in module names are ignored, so `clip_vision`, `clip-vision`, and `clipvision` are equivalent.
|
||||
|
||||
`all=`, `default=`, and `*=` can be used to set the default backend inside a mixed assignment:
|
||||
@@ -64,9 +70,11 @@ The special values `auto`, `default`, and an empty backend name select the defau
|
||||
|
||||
The special value `gpu` selects the first GPU backend, falling back to the first integrated GPU backend.
|
||||
|
||||
The special value `disk` is accepted only by `--params-backend`. `--backend disk` is invalid because `disk` is a parameter residency mode, not a runtime compute backend.
|
||||
|
||||
## Runtime backend vs. parameter backend
|
||||
|
||||
The runtime backend controls where graph execution runs. The parameter backend controls where model weights are allocated.
|
||||
The runtime backend controls where graph execution runs. The parameter backend controls where model weights are allocated or whether they are reloaded from disk on demand.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -76,6 +84,16 @@ sd-cli -m model.safetensors -p "a cat" --backend cuda0 --params-backend cpu
|
||||
|
||||
This runs all modules on `cuda0`, but stores parameters in CPU RAM. During execution, parameters are moved to the runtime backend as needed.
|
||||
|
||||
For example:
|
||||
|
||||
```shell
|
||||
sd-cli -m model.safetensors -p "a cat" --backend cuda0 --params-backend disk
|
||||
```
|
||||
|
||||
This runs all modules on `cuda0`, reloads parameters from the model file as needed, and releases those parameter buffers after use.
|
||||
|
||||
`disk` is never selected implicitly. If `--params-backend` is not set, parameters use the runtime backend.
|
||||
|
||||
Per-module assignments can be mixed:
|
||||
|
||||
```shell
|
||||
@@ -100,23 +118,27 @@ uses one shared CPU backend for both `te` and `vae` runtime execution.
|
||||
|
||||
Runtime and parameter assignments also share the same backend cache. If `--backend diffusion=cuda0` and `--params-backend diffusion=cuda0` resolve to the same device, both use the same backend instance.
|
||||
|
||||
`--params-backend disk` does not create a separate backend instance. Parameters are loaded lazily using the module runtime backend.
|
||||
|
||||
`SDBackendManager` owns the backend instances and frees them when the context or upscaler is destroyed. Model runners receive non-owning runtime and parameter backend pointers and do not free them.
|
||||
|
||||
## Compatibility flags
|
||||
|
||||
The older CPU placement flags are still supported:
|
||||
The example CLI/server still accepts these older CPU placement flags as compatibility aliases:
|
||||
|
||||
- `--clip-on-cpu`
|
||||
- `--vae-on-cpu`
|
||||
- `--control-net-cpu`
|
||||
- `--offload-to-cpu`
|
||||
|
||||
`--clip-on-cpu`, `--vae-on-cpu`, and `--control-net-cpu` affect runtime backend assignment only when `--backend` is not set. They map to `te=cpu`, `vae=cpu`, and `controlnet=cpu`.
|
||||
`--clip-on-cpu`, `--vae-on-cpu`, and `--control-net-cpu` are deprecated. The example argument layer prepends `te=cpu`, `vae=cpu`, and `controlnet=cpu` to `--backend` before creating the context.
|
||||
|
||||
`--offload-to-cpu` affects parameter backend assignment only when `--params-backend` is not set. It is equivalent to:
|
||||
`--offload-to-cpu` prepends a CPU default to the parameter assignment in the caller before creating the context:
|
||||
|
||||
```shell
|
||||
--params-backend cpu
|
||||
--params-backend '*=cpu'
|
||||
```
|
||||
|
||||
Explicit `--backend` and `--params-backend` assignments are preferred for new commands.
|
||||
Because this default is inserted first, later explicit `--params-backend` entries can still override it, for example `--offload-to-cpu --params-backend te=disk` keeps non-TE parameters on CPU and reloads TE parameters from disk.
|
||||
|
||||
Library callers should set `backend` and `params_backend` directly. The old CPU/offload fields are no longer part of the C API. Explicit `--backend` and `--params-backend` assignments are preferred for new commands.
|
||||
|
||||
+33
-1
@@ -21,6 +21,38 @@ and the compute buffer shrink in the debug log:
|
||||
|
||||
Using `--offload-to-cpu` allows you to offload weights to the CPU, saving VRAM without reducing generation speed.
|
||||
|
||||
## Use params backend to reduce VRAM or RAM usage.
|
||||
|
||||
`--params-backend` controls where model parameters are kept. If it is not set, parameters use the same backend as `--backend`, so a GPU runtime backend also keeps parameters in VRAM.
|
||||
|
||||
Use CPU params to reduce VRAM usage:
|
||||
|
||||
```shell
|
||||
--backend cuda0 --params-backend cpu
|
||||
```
|
||||
|
||||
This keeps model weights in system RAM and moves them to the runtime backend when needed. In the example CLI/server, `--offload-to-cpu` is a compatibility shortcut that prepends `*=cpu` to `--params-backend` before creating the context, so explicit module assignments can still override it:
|
||||
|
||||
```shell
|
||||
--offload-to-cpu --params-backend te=disk
|
||||
```
|
||||
|
||||
Use disk params to reduce both VRAM and RAM usage:
|
||||
|
||||
```shell
|
||||
--backend cuda0 --params-backend disk
|
||||
```
|
||||
|
||||
This reloads parameters from the model file on demand and releases them after use. It has the lowest memory residency, but can be slower because weights must be read again. `disk` is never selected implicitly; set it explicitly when RAM usage matters more than reload cost.
|
||||
|
||||
Per-module assignments can target only the largest modules:
|
||||
|
||||
```shell
|
||||
--backend cuda0 --params-backend diffusion=disk,te=cpu,vae=cpu
|
||||
```
|
||||
|
||||
See [backend selection](./backend.md) for full syntax.
|
||||
|
||||
## Use quantization to reduce memory usage.
|
||||
|
||||
[quantization](./quantization_and_gguf.md)
|
||||
[quantization](./quantization_and_gguf.md)
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
# Building and Using the RPC Server with `stable-diffusion.cpp`
|
||||
|
||||
This guide covers how to build a version of [the RPC server from `llama.cpp`](https://github.com/ggml-org/llama.cpp/blob/master/tools/rpc/README.md) that is compatible with your version of `stable-diffusion.cpp` to manage multi-backends setups. RPC allows you to offload specific model components to a remote server.
|
||||
|
||||
> **Note on Model Location:** The model files (e.g., `.safetensors` or `.gguf`) remain on the **Client** machine. The client parses the file and transmits the necessary tensor data and computational graphs to the server. The server does not need to store the model files locally.
|
||||
|
||||
## 1. Building `stable-diffusion.cpp` with RPC client
|
||||
|
||||
First, you should build the client application from source. It requires `SD_RPC=ON` to include the RPC backend to your client.
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. \
|
||||
-DSD_RPC=ON \
|
||||
# Add other build flags here (e.g., -DSD_VULKAN=ON)
|
||||
cmake --build . --config Release -j $(nproc)
|
||||
```
|
||||
|
||||
> **Note:** Ensure you add the other flags you would normally use (e.g., `-DSD_VULKAN=ON`, `-DSD_CUDA=ON`, `-DSD_HIPBLAS=ON`, or `-DGGML_METAL=ON`), for more information about building `stable-diffusion.cpp` from source, please refer to the [build.md](build.md) documentation.
|
||||
|
||||
## 2. Ensure `llama.cpp` is at the correct commit
|
||||
|
||||
`stable-diffusion.cpp`'s RPC client is designed to work with a specific version of `llama.cpp` (compatible with the `ggml` submodule) to ensure API compatibility. The commit hash for `llama.cpp` is stored in `ggml/scripts/sync-llama.last`.
|
||||
|
||||
> **Start from Root:** Perform these steps from the root of your `stable-diffusion.cpp` directory.
|
||||
|
||||
1. Read the target commit hash from the submodule tracker:
|
||||
|
||||
```bash
|
||||
# Linux / WSL / MacOS
|
||||
HASH=$(cat ggml/scripts/sync-llama.last)
|
||||
|
||||
# Windows (PowerShell)
|
||||
$HASH = Get-Content -Path "ggml\scripts\sync-llama.last"
|
||||
```
|
||||
|
||||
2. Clone `llama.cpp` at the target commit .
|
||||
```bash
|
||||
git clone https://github.com/ggml-org/llama.cpp.git
|
||||
cd llama.cpp
|
||||
git checkout $HASH
|
||||
```
|
||||
To save on download time and storage, you can use a shallow clone to download only the target commit:
|
||||
```bash
|
||||
mkdir -p llama.cpp
|
||||
cd llama.cpp
|
||||
git init
|
||||
git remote add origin https://github.com/ggml-org/llama.cpp.git
|
||||
git fetch --depth 1 origin $HASH
|
||||
git checkout FETCH_HEAD
|
||||
```
|
||||
|
||||
## 3. Build `llama.cpp` (RPC Server)
|
||||
|
||||
The RPC server acts as the worker. You must explicitly enable the **backend** (the hardware interface, such as CUDA for Nvidia, Metal for Apple Silicon, or Vulkan) when building, otherwise the server will default to using only the CPU.
|
||||
|
||||
To find the correct flags for your system, refer to the official documentation for the [`llama.cpp`](https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md) repository.
|
||||
|
||||
> **Crucial:** You must include the compiler flags required to satisfy the API compatibility with `stable-diffusion.cpp` (`-DGGML_MAX_NAME=128`). Without this flag, `GGML_MAX_NAME` will default to `64` for the server, and data transfers between the client and server will fail. Of course, `-DGGML_RPC` must also be enabled.
|
||||
>
|
||||
> I recommend disabling the `LLAMA_CURL` flag to avoid unnecessary dependencies, and disabling shared library builds to avoid potential conflicts.
|
||||
|
||||
> **Build Target:** We are specifically building the `rpc-server` target. This prevents the build system from compiling the entire `llama.cpp` suite (like `llama-server`), making the build significantly faster.
|
||||
|
||||
### Linux / WSL (Vulkan)
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DGGML_RPC=ON \
|
||||
-DGGML_VULKAN=ON \ # Ensure backend is enabled
|
||||
-DGGML_BUILD_SHARED_LIBS=OFF \
|
||||
-DLLAMA_CURL=OFF \
|
||||
-DCMAKE_C_FLAGS=-DGGML_MAX_NAME=128 \
|
||||
-DCMAKE_CXX_FLAGS=-DGGML_MAX_NAME=128
|
||||
cmake --build . --config Release --target rpc-server -j $(nproc)
|
||||
```
|
||||
|
||||
### macOS (Metal)
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DGGML_RPC=ON \
|
||||
-DGGML_METAL=ON \
|
||||
-DGGML_BUILD_SHARED_LIBS=OFF \
|
||||
-DLLAMA_CURL=OFF \
|
||||
-DCMAKE_C_FLAGS=-DGGML_MAX_NAME=128 \
|
||||
-DCMAKE_CXX_FLAGS=-DGGML_MAX_NAME=128
|
||||
cmake --build . --config Release --target rpc-server
|
||||
```
|
||||
|
||||
### Windows (Visual Studio 2022, Vulkan)
|
||||
|
||||
```powershell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -G "Visual Studio 17 2022" -A x64 `
|
||||
-DGGML_RPC=ON `
|
||||
-DGGML_VULKAN=ON `
|
||||
-DGGML_BUILD_SHARED_LIBS=OFF `
|
||||
-DLLAMA_CURL=OFF `
|
||||
-DCMAKE_C_FLAGS=-DGGML_MAX_NAME=128 `
|
||||
-DCMAKE_CXX_FLAGS=-DGGML_MAX_NAME=128
|
||||
cmake --build . --config Release --target rpc-server
|
||||
```
|
||||
|
||||
## 4. Usage
|
||||
|
||||
Once both applications are built, you can run the server and the client to manage your GPU allocation.
|
||||
|
||||
### Step A: Run the RPC Server
|
||||
|
||||
Start the server. It listens for connections on the default address (usually `localhost:50052`). If your server is on a different machine, ensure the server binds to the correct interface and your firewall allows the connection.
|
||||
|
||||
**On the Server :**
|
||||
If running on the same machine, you can use the default address:
|
||||
|
||||
```bash
|
||||
./rpc-server
|
||||
```
|
||||
|
||||
If you want to allow connections from other machines on the network:
|
||||
|
||||
```bash
|
||||
./rpc-server --host 0.0.0.0
|
||||
```
|
||||
|
||||
> **Security Warning:** The RPC server does not currently support authentication or encryption. **Only run the server on trusted local networks**. Never expose the RPC server directly to the open internet.
|
||||
|
||||
> **Drivers & Hardware:** Ensure the Server machine has the necessary drivers installed and functional (e.g., Nvidia Drivers for CUDA, Vulkan SDK, or Metal). If no devices are found, the server will simply fallback to CPU usage.
|
||||
|
||||
<!-- ### Step B: Check if the client is able to connect to the server and see the available devices
|
||||
|
||||
We're assuming the server is running on your local machine, and listening on the default port `50052`. If it's running on a different machine, you can replace `localhost` with the IP address of the server.
|
||||
|
||||
**On the Client:**
|
||||
|
||||
```bash
|
||||
./sd-cli --rpc-servers localhost:50052 --list-devices
|
||||
```
|
||||
|
||||
If the server is running and the client is able to connect, you should see `RPC0 localhost:50052` in the list of devices.
|
||||
|
||||
Example output:
|
||||
(Client built without GPU acceleration, two GPUs available on the server)
|
||||
|
||||
```
|
||||
List of available GGML devices:
|
||||
Name Description
|
||||
-------------------
|
||||
CPU AMD Ryzen 9 5900X 12-Core Processor
|
||||
RPC0 localhost:50052
|
||||
RPC1 localhost:50052
|
||||
``` -->
|
||||
|
||||
### Step B: Run with RPC device
|
||||
|
||||
If everything is working correctly, you can now run the client while offloading some or all of the work to the RPC server.
|
||||
|
||||
Example: Setting the main backend to the RPC0 device for doing all the work on the server.
|
||||
|
||||
```bash
|
||||
./sd-cli -m models/sd1.5.safetensors -p "A cat" --rpc-servers localhost:50052 --backend RPC0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Scaling: Multiple RPC Servers
|
||||
|
||||
You can connect the client to multiple RPC servers simultaneously to scale out your hardware usage.
|
||||
|
||||
Example: A main machine (192.168.1.10) with 3 GPUs, with one GPU running CUDA and the other two running Vulkan, and a second machine (192.168.1.11) only one GPU.
|
||||
|
||||
**On the first machine (Running two server instances):**
|
||||
|
||||
**Terminal 1 (CUDA):**
|
||||
|
||||
```bash
|
||||
# Linux / WSL
|
||||
export CUDA_VISIBLE_DEVICES=0
|
||||
cd ./build_cuda/bin/Release
|
||||
./rpc-server --host 0.0.0.0
|
||||
|
||||
# Windows PowerShell
|
||||
$env:CUDA_VISIBLE_DEVICES="0"
|
||||
cd .\build_cuda\bin\Release
|
||||
./rpc-server --host 0.0.0.0
|
||||
```
|
||||
|
||||
**Terminal 2 (Vulkan):**
|
||||
|
||||
```bash
|
||||
cd ./build_vulkan/bin/Release
|
||||
# ignore the first GPU (used by CUDA server)
|
||||
./rpc-server --host 0.0.0.0 --port 50053 -d Vulkan1,Vulkan2
|
||||
```
|
||||
|
||||
**On the second machine:**
|
||||
|
||||
```bash
|
||||
cd ./build/bin/Release
|
||||
./rpc-server --host 0.0.0.0
|
||||
```
|
||||
|
||||
**On the Client:**
|
||||
Pass multiple server addresses separated by commas.
|
||||
|
||||
```bash
|
||||
./sd-cli --rpc-servers 192.168.1.10:50052,192.168.1.10:50053,192.168.1.11:50052 [...]
|
||||
```
|
||||
|
||||
The client will map these servers to sequential device IDs (e.g., RPC0 from the first server, RPC2, RPC3 from the second, and RPC4 from the third). With this setup, you could for example use RPC0 for the main backend, RPC1 and RPC2 for the text encoders, and RPC3 for the VAE.
|
||||
|
||||
---
|
||||
|
||||
## 6. Performance Considerations
|
||||
|
||||
RPC performance is heavily dependent on network bandwidth, as large weights and activations must be transferred back and forth over the network, especially for large models, or when using high resolutions. For best results, ensure your network connection is stable and has sufficient bandwidth (>1Gbps recommended). This shoumd not be a concern if you are running the server and client on the same machine, as the data transfer will happen over the loopback interface.
|
||||
+4
-199
@@ -1,204 +1,9 @@
|
||||
# Run
|
||||
# Usage
|
||||
|
||||
```
|
||||
usage: ./bin/sd-cli [options]
|
||||
For detailed command-line arguments, run:
|
||||
|
||||
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). Single-file video outputs
|
||||
support .avi, .webm, and animated .webp
|
||||
--image <string> path to the image to inspect (for metadata mode)
|
||||
--metadata-format <string> metadata output format, one of [text, json] (default: text)
|
||||
--preview-path <string> path to write preview image to (default: ./preview.png). Multi-frame previews support
|
||||
.avi, .webm, and animated .webp
|
||||
--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
|
||||
--color colors the logging tags according to level
|
||||
--taesd-preview-only prevents usage of taesd for decoding the final image. (for use with --preview tae)
|
||||
--preview-noisy enables previewing noisy inputs of the models rather than the denoised outputs
|
||||
--metadata-raw include raw hex previews for unparsed metadata payloads
|
||||
--metadata-brief truncate long metadata text values in text output
|
||||
--metadata-all include structural/container entries such as IHDR, IDAT, and non-metadata JPEG segments
|
||||
-M, --mode run mode, one of [img_gen, vid_gen, upscale, convert, metadata], default: img_gen
|
||||
--preview preview method. must be one of the following [none, proj, tae, vae] (default is none)
|
||||
-h, --help show this help message and exit
|
||||
|
||||
Context Options:
|
||||
-m, --model <string> path to full model
|
||||
--clip_l <string> path to the clip-l text encoder
|
||||
--clip_g <string> path to the clip-g text encoder
|
||||
--clip_vision <string> path to the clip-vision encoder
|
||||
--t5xxl <string> path to the t5xxl text encoder
|
||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image,
|
||||
mistral-small3.2 for flux2, ...)
|
||||
--llm_vision <string> path to the llm vit
|
||||
--qwen2vl <string> alias of --llm. Deprecated.
|
||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||
--diffusion-model <string> path to the standalone diffusion model
|
||||
--high-noise-diffusion-model <string> path to the standalone high noise diffusion model
|
||||
--uncond-diffusion-model <string> path to the standalone unconditional diffusion model, currently used by
|
||||
Ideogram4 CFG
|
||||
--vae <string> path to standalone vae model
|
||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--tae <string> alias of --taesd
|
||||
--control-net <string> path to control net model
|
||||
--embd-dir <string> embeddings directory
|
||||
--lora-model-dir <string> lora model directory
|
||||
--hires-upscalers-dir <string> highres fix upscaler model directory
|
||||
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||
--photo-maker <string> path to PHOTOMAKER model
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0,
|
||||
then threads will be set to the number of CPU physical cores
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--max-vram <float> maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables
|
||||
graph splitting; a negative value auto-detects free VRAM, sparing the
|
||||
specified value (e.g. -0.5 will keep at least 0.5 GiB free)
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM
|
||||
when needed
|
||||
--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)
|
||||
--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
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--circular enable circular padding for convolutions
|
||||
--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
|
||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
||||
--sampler-rng sampler RNG, one of [std_default, cuda, cpu]. If not specified, use --rng
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow,
|
||||
flux2_flow]
|
||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is
|
||||
auto. In auto mode, if the model weights contain any quantized parameters,
|
||||
the at_runtime mode will be used; otherwise, immediately will be used.The
|
||||
immediately mode may have precision and compatibility issues with quantized
|
||||
parameters, but it usually offers faster inference speed and, in some cases,
|
||||
lower memory usage. The at_runtime mode, on the other hand, is exactly the
|
||||
opposite.
|
||||
|
||||
Generation Options:
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--mask <string> path to the mask image
|
||||
--control-image <string> path to control image, control net
|
||||
--control-video <string> path to control video frames, It must be a directory path. The video frames
|
||||
inside should be stored as images in lexicographical (character) order. For
|
||||
example, if the control video path is `frames`, the directory contain images
|
||||
such as 00.png, 01.png, ... etc.
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
--hires-upscaler <string> highres fix upscaler, Lanczos, Nearest, Latent, Latent (nearest), Latent
|
||||
(nearest-exact), Latent (antialiased), Latent (bicubic), Latent (bicubic
|
||||
antialiased), or a model name under --hires-upscalers-dir (default: Latent)
|
||||
--extra-sample-args <string> extra sampler/scheduler/guidance args, key=value list. APG supports apg_eta,
|
||||
apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports
|
||||
slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end;
|
||||
ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma
|
||||
--extra-tiling-args <string> extra VAE tiling args, key=value list. LTX video VAE supports
|
||||
temporal_tile_frames (default: 4), temporal_tile_overlap (default: 1)
|
||||
-H, --height <int> image height, in pixel space (default: 512)
|
||||
-W, --width <int> image width, in pixel space (default: 512)
|
||||
--steps <int> number of sample steps (default: 20)
|
||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
||||
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer
|
||||
(default: -1). <= 0 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
|
||||
-b, --batch-count <int> batch count
|
||||
--video-frames <int> video frames (default: 1)
|
||||
--fps <int> fps (default: 24)
|
||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for
|
||||
NitroSD-Realism around 250 and 500 for NitroSD-Vibrant
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
||||
--hires-width <int> highres fix target width, 0 to use --hires-scale (default: 0)
|
||||
--hires-height <int> highres fix target height, 0 to use --hires-scale (default: 0)
|
||||
--hires-steps <int> highres fix second pass sample steps, 0 to reuse --steps (default: 0)
|
||||
--hires-upscale-tile-size <int> highres fix upscaler tile size, reserved for model-backed upscalers (default:
|
||||
128)
|
||||
--cfg-scale <float> unconditional guidance scale: (default: 7.0)
|
||||
--img-cfg-scale <float> image guidance scale for inpaint or image edit models: (default: same as
|
||||
--cfg-scale)
|
||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--slg-scale <float> skip layer guidance (SLG) scale, only for DiT models: (default: 0). 0 means
|
||||
disabled, a value of 2.5 is nice for sd3.5 medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> noise multiplier (default: 0 for ddim_trailing, tcd, res_multistep and
|
||||
res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
||||
--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 image edit models (default:
|
||||
same as --cfg-scale)
|
||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input
|
||||
(default: 3.5)
|
||||
--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) noise multiplier (default: 0 for ddim_trailing, tcd,
|
||||
res_multistep and res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
||||
--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
|
||||
--moe-boundary <float> timestep boundary for Wan2.2 MoE model. (default: 0.875). Only enabled if
|
||||
`--high-noise-steps` is set to -1
|
||||
--vace-strength <float> wan vace strength
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--hires-scale <float> highres fix scale when target size is not set (default: 2.0)
|
||||
--hires-denoising-strength <float> highres fix second pass denoising strength (default: 0.7)
|
||||
--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
|
||||
--disable-image-metadata do not embed generation metadata on image files
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--temporal-tiling enable temporal tiling for LTX video VAE decode
|
||||
--hires enable highres fix
|
||||
-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,
|
||||
er_sde, euler_cfg_pp, euler_a_cfg_pp] (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, er_sde, euler_cfg_pp, euler_a_cfg_pp] 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, ltx2], default:
|
||||
model-specific
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g.,
|
||||
"14.61,7.8,3.5,0.0").
|
||||
--hires-sigmas custom sigma values for the highres fix second pass, comma-separated (e.g.,
|
||||
"0.85,0.725,0.421875,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), '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=; spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=.
|
||||
Examples: "threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--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'
|
||||
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||
--vae-relative-tile-size relative tile size for vae tiling, format [X]x[Y], in fraction of image size
|
||||
if < 1, in number of tiles per dim if >=1 (overrides --vae-tile-size)
|
||||
```bash
|
||||
./bin/sd-cli -h
|
||||
```
|
||||
|
||||
Metadata mode inspects PNG/JPEG container metadata without loading any model:
|
||||
|
||||
+3
-20
@@ -623,8 +623,6 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
bool vae_decode_only = true;
|
||||
|
||||
auto load_image_and_update_size = [&](const std::string& path,
|
||||
SDImageOwner& image,
|
||||
bool resize_image = true,
|
||||
@@ -646,21 +644,18 @@ int main(int argc, const char* argv[]) {
|
||||
};
|
||||
|
||||
if (gen_params.init_image_path.size() > 0) {
|
||||
vae_decode_only = false;
|
||||
if (!load_image_and_update_size(gen_params.init_image_path, gen_params.init_image)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (gen_params.end_image_path.size() > 0) {
|
||||
vae_decode_only = false;
|
||||
if (!load_image_and_update_size(gen_params.end_image_path, gen_params.end_image)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (gen_params.ref_image_paths.size() > 0) {
|
||||
vae_decode_only = false;
|
||||
gen_params.ref_images.clear();
|
||||
for (auto& path : gen_params.ref_image_paths) {
|
||||
SDImageOwner ref_image({0, 0, 3, nullptr});
|
||||
@@ -735,18 +730,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cli_params.mode == VID_GEN) {
|
||||
vae_decode_only = false;
|
||||
}
|
||||
|
||||
if (gen_params.hires_enabled &&
|
||||
(gen_params.resolved_hires_upscaler == SD_HIRES_UPSCALER_MODEL ||
|
||||
gen_params.resolved_hires_upscaler == SD_HIRES_UPSCALER_LANCZOS ||
|
||||
gen_params.resolved_hires_upscaler == SD_HIRES_UPSCALER_NEAREST)) {
|
||||
vae_decode_only = false;
|
||||
}
|
||||
|
||||
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(vae_decode_only, true, cli_params.taesd_preview);
|
||||
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(cli_params.taesd_preview);
|
||||
|
||||
SDImageVec results;
|
||||
int num_results = 0;
|
||||
@@ -798,12 +782,11 @@ int main(int argc, const char* argv[]) {
|
||||
int upscale_factor = 4; // unused for RealESRGAN_x4plus_anime_6B.pth
|
||||
if (ctx_params.esrgan_path.size() > 0 && gen_params.upscale_repeats > 0) {
|
||||
UpscalerCtxPtr upscaler_ctx(new_upscaler_ctx(ctx_params.esrgan_path.c_str(),
|
||||
ctx_params.offload_params_to_cpu,
|
||||
ctx_params.diffusion_conv_direct,
|
||||
ctx_params.n_threads,
|
||||
gen_params.upscale_tile_size,
|
||||
ctx_params.backend.c_str(),
|
||||
ctx_params.params_backend.c_str()));
|
||||
sd_ctx_params.backend,
|
||||
sd_ctx_params.params_backend));
|
||||
|
||||
if (upscaler_ctx == nullptr) {
|
||||
LOG_ERROR("new_upscaler_ctx failed");
|
||||
|
||||
+79
-56
@@ -51,6 +51,10 @@ static sd_vae_format_t str_to_vae_format(const std::string& value) {
|
||||
return SD_VAE_FORMAT_COUNT;
|
||||
}
|
||||
|
||||
static void prepend_backend_assignment(std::string& spec, const char* assignment) {
|
||||
spec = spec.empty() ? assignment : std::string(assignment) + "," + spec;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static std::string utf16_to_utf8(const std::wstring& wstr) {
|
||||
if (wstr.empty())
|
||||
@@ -421,8 +425,12 @@ ArgOptions SDContextParams::get_options() {
|
||||
&backend},
|
||||
{"",
|
||||
"--params-backend",
|
||||
"parameter backend assignment, e.g. cpu or diffusion=cpu,clip=cpu",
|
||||
"parameter backend assignment, e.g. disk, cpu, or diffusion=disk,clip=cpu",
|
||||
¶ms_backend},
|
||||
{"",
|
||||
"--rpc-servers",
|
||||
"comma-separated list of RPC servers to connect to for offloading, in the format host:port, e.g. localhost:50052,192.168.1.3:50052",
|
||||
&rpc_servers},
|
||||
};
|
||||
|
||||
options.int_options = {
|
||||
@@ -463,15 +471,15 @@ ArgOptions SDContextParams::get_options() {
|
||||
true, &enable_mmap},
|
||||
{"",
|
||||
"--control-net-cpu",
|
||||
"keep controlnet in cpu (for low vram)",
|
||||
"deprecated; use --backend controlnet=cpu",
|
||||
true, &control_net_cpu},
|
||||
{"",
|
||||
"--clip-on-cpu",
|
||||
"keep clip in cpu (for low vram)",
|
||||
"deprecated; use --backend te=cpu",
|
||||
true, &clip_on_cpu},
|
||||
{"",
|
||||
"--vae-on-cpu",
|
||||
"keep vae in cpu (for low vram)",
|
||||
"deprecated; use --backend vae=cpu",
|
||||
true, &vae_on_cpu},
|
||||
{"",
|
||||
"--fa",
|
||||
@@ -688,6 +696,25 @@ bool SDContextParams::resolve_and_validate(SDMode mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDContextParams::prepare_backend_assignments() {
|
||||
effective_backend = backend;
|
||||
effective_params_backend = params_backend;
|
||||
|
||||
if (offload_params_to_cpu) {
|
||||
prepend_backend_assignment(effective_params_backend, "*=cpu");
|
||||
}
|
||||
|
||||
if (clip_on_cpu) {
|
||||
prepend_backend_assignment(effective_backend, "te=cpu");
|
||||
}
|
||||
if (vae_on_cpu) {
|
||||
prepend_backend_assignment(effective_backend, "vae=cpu");
|
||||
}
|
||||
if (control_net_cpu) {
|
||||
prepend_backend_assignment(effective_backend, "controlnet=cpu");
|
||||
}
|
||||
}
|
||||
|
||||
std::string SDContextParams::to_string() const {
|
||||
std::ostringstream emb_ss;
|
||||
emb_ss << "{\n";
|
||||
@@ -757,7 +784,8 @@ std::string SDContextParams::to_string() const {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
sd_ctx_params_t SDContextParams::to_sd_ctx_params_t(bool vae_decode_only, bool free_params_immediately, bool taesd_preview) {
|
||||
sd_ctx_params_t SDContextParams::to_sd_ctx_params_t(bool taesd_preview) {
|
||||
prepare_backend_assignments();
|
||||
embedding_vec.clear();
|
||||
embedding_vec.reserve(embedding_map.size());
|
||||
for (const auto& kv : embedding_map) {
|
||||
@@ -767,57 +795,52 @@ sd_ctx_params_t SDContextParams::to_sd_ctx_params_t(bool vae_decode_only, bool f
|
||||
embedding_vec.emplace_back(item);
|
||||
}
|
||||
|
||||
sd_ctx_params_t sd_ctx_params = {
|
||||
model_path.c_str(),
|
||||
clip_l_path.c_str(),
|
||||
clip_g_path.c_str(),
|
||||
clip_vision_path.c_str(),
|
||||
t5xxl_path.c_str(),
|
||||
llm_path.c_str(),
|
||||
llm_vision_path.c_str(),
|
||||
diffusion_model_path.c_str(),
|
||||
high_noise_diffusion_model_path.c_str(),
|
||||
uncond_diffusion_model_path.c_str(),
|
||||
embeddings_connectors_path.c_str(),
|
||||
vae_path.c_str(),
|
||||
audio_vae_path.c_str(),
|
||||
taesd_path.c_str(),
|
||||
control_net_path.c_str(),
|
||||
embedding_vec.data(),
|
||||
static_cast<uint32_t>(embedding_vec.size()),
|
||||
photo_maker_path.c_str(),
|
||||
tensor_type_rules.c_str(),
|
||||
vae_decode_only,
|
||||
free_params_immediately,
|
||||
n_threads,
|
||||
wtype,
|
||||
rng_type,
|
||||
sampler_rng_type,
|
||||
prediction,
|
||||
lora_apply_mode,
|
||||
offload_params_to_cpu,
|
||||
enable_mmap,
|
||||
clip_on_cpu,
|
||||
control_net_cpu,
|
||||
vae_on_cpu,
|
||||
flash_attn,
|
||||
diffusion_flash_attn,
|
||||
taesd_preview,
|
||||
diffusion_conv_direct,
|
||||
vae_conv_direct,
|
||||
circular || circular_x,
|
||||
circular || circular_y,
|
||||
force_sdxl_vae_conv_scale,
|
||||
chroma_use_dit_mask,
|
||||
chroma_use_t5_mask,
|
||||
chroma_t5_mask_pad,
|
||||
qwen_image_zero_cond_t,
|
||||
str_to_vae_format(vae_format),
|
||||
max_vram,
|
||||
stream_layers,
|
||||
backend.c_str(),
|
||||
params_backend.c_str(),
|
||||
};
|
||||
sd_ctx_params_t sd_ctx_params;
|
||||
sd_ctx_params_init(&sd_ctx_params);
|
||||
sd_ctx_params.model_path = model_path.c_str();
|
||||
sd_ctx_params.clip_l_path = clip_l_path.c_str();
|
||||
sd_ctx_params.clip_g_path = clip_g_path.c_str();
|
||||
sd_ctx_params.clip_vision_path = clip_vision_path.c_str();
|
||||
sd_ctx_params.t5xxl_path = t5xxl_path.c_str();
|
||||
sd_ctx_params.llm_path = llm_path.c_str();
|
||||
sd_ctx_params.llm_vision_path = llm_vision_path.c_str();
|
||||
sd_ctx_params.diffusion_model_path = diffusion_model_path.c_str();
|
||||
sd_ctx_params.high_noise_diffusion_model_path = high_noise_diffusion_model_path.c_str();
|
||||
sd_ctx_params.uncond_diffusion_model_path = uncond_diffusion_model_path.c_str();
|
||||
sd_ctx_params.embeddings_connectors_path = embeddings_connectors_path.c_str();
|
||||
sd_ctx_params.vae_path = vae_path.c_str();
|
||||
sd_ctx_params.audio_vae_path = audio_vae_path.c_str();
|
||||
sd_ctx_params.taesd_path = taesd_path.c_str();
|
||||
sd_ctx_params.control_net_path = control_net_path.c_str();
|
||||
sd_ctx_params.embeddings = embedding_vec.data();
|
||||
sd_ctx_params.embedding_count = static_cast<uint32_t>(embedding_vec.size());
|
||||
sd_ctx_params.photo_maker_path = photo_maker_path.c_str();
|
||||
sd_ctx_params.tensor_type_rules = tensor_type_rules.c_str();
|
||||
sd_ctx_params.n_threads = n_threads;
|
||||
sd_ctx_params.wtype = wtype;
|
||||
sd_ctx_params.rng_type = rng_type;
|
||||
sd_ctx_params.sampler_rng_type = sampler_rng_type;
|
||||
sd_ctx_params.prediction = prediction;
|
||||
sd_ctx_params.lora_apply_mode = lora_apply_mode;
|
||||
sd_ctx_params.enable_mmap = enable_mmap;
|
||||
sd_ctx_params.flash_attn = flash_attn;
|
||||
sd_ctx_params.diffusion_flash_attn = diffusion_flash_attn;
|
||||
sd_ctx_params.tae_preview_only = taesd_preview;
|
||||
sd_ctx_params.diffusion_conv_direct = diffusion_conv_direct;
|
||||
sd_ctx_params.vae_conv_direct = vae_conv_direct;
|
||||
sd_ctx_params.circular_x = circular || circular_x;
|
||||
sd_ctx_params.circular_y = circular || circular_y;
|
||||
sd_ctx_params.force_sdxl_vae_conv_scale = force_sdxl_vae_conv_scale;
|
||||
sd_ctx_params.chroma_use_dit_mask = chroma_use_dit_mask;
|
||||
sd_ctx_params.chroma_use_t5_mask = chroma_use_t5_mask;
|
||||
sd_ctx_params.chroma_t5_mask_pad = chroma_t5_mask_pad;
|
||||
sd_ctx_params.qwen_image_zero_cond_t = qwen_image_zero_cond_t;
|
||||
sd_ctx_params.vae_format = str_to_vae_format(vae_format);
|
||||
sd_ctx_params.max_vram = max_vram;
|
||||
sd_ctx_params.stream_layers = stream_layers;
|
||||
sd_ctx_params.backend = effective_backend.c_str();
|
||||
sd_ctx_params.params_backend = effective_params_backend.c_str();
|
||||
sd_ctx_params.rpc_servers = rpc_servers.c_str();
|
||||
return sd_ctx_params;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +148,9 @@ struct SDContextParams {
|
||||
bool stream_layers = false;
|
||||
std::string backend;
|
||||
std::string params_backend;
|
||||
std::string rpc_servers;
|
||||
std::string effective_backend;
|
||||
std::string effective_params_backend;
|
||||
bool enable_mmap = false;
|
||||
bool control_net_cpu = false;
|
||||
bool clip_on_cpu = false;
|
||||
@@ -175,11 +178,12 @@ struct SDContextParams {
|
||||
float flow_shift = INFINITY;
|
||||
ArgOptions get_options();
|
||||
void build_embedding_map();
|
||||
void prepare_backend_assignments();
|
||||
bool resolve(SDMode mode);
|
||||
bool validate(SDMode mode);
|
||||
bool resolve_and_validate(SDMode mode);
|
||||
std::string to_string() const;
|
||||
sd_ctx_params_t to_sd_ctx_params_t(bool vae_decode_only, bool free_params_immediately, bool taesd_preview);
|
||||
sd_ctx_params_t to_sd_ctx_params_t(bool taesd_preview);
|
||||
};
|
||||
|
||||
struct SDGenerationParams {
|
||||
|
||||
+4
-182
@@ -117,188 +117,10 @@ In this case, the server will load and serve the specified `index.html` file ins
|
||||
* using a custom UI
|
||||
* avoiding rebuilding the binary after frontend modifications
|
||||
|
||||
# Run
|
||||
# Usage
|
||||
|
||||
```
|
||||
usage: ./bin/sd-server [options]
|
||||
For detailed command-line arguments, run:
|
||||
|
||||
Svr Options:
|
||||
-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
|
||||
--clip_l <string> path to the clip-l text encoder
|
||||
--clip_g <string> path to the clip-g text encoder
|
||||
--clip_vision <string> path to the clip-vision encoder
|
||||
--t5xxl <string> path to the t5xxl text encoder
|
||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image,
|
||||
mistral-small3.2 for flux2, ...)
|
||||
--llm_vision <string> path to the llm vit
|
||||
--qwen2vl <string> alias of --llm. Deprecated.
|
||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||
--diffusion-model <string> path to the standalone diffusion model
|
||||
--high-noise-diffusion-model <string> path to the standalone high noise diffusion model
|
||||
--uncond-diffusion-model <string> path to the standalone unconditional diffusion model, currently used by
|
||||
Ideogram4 CFG
|
||||
--vae <string> path to standalone vae model
|
||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--tae <string> alias of --taesd
|
||||
--control-net <string> path to control net model
|
||||
--embd-dir <string> embeddings directory
|
||||
--lora-model-dir <string> lora model directory
|
||||
--hires-upscalers-dir <string> highres fix upscaler model directory
|
||||
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||
--photo-maker <string> path to PHOTOMAKER model
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0,
|
||||
then threads will be set to the number of CPU physical cores
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--max-vram <float> maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables
|
||||
graph splitting; a negative value auto-detects free VRAM, sparing the
|
||||
specified value (e.g. -0.5 will keep at least 0.5 GiB free)
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM
|
||||
when needed
|
||||
--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)
|
||||
--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
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--circular enable circular padding for convolutions
|
||||
--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
|
||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
||||
--sampler-rng sampler RNG, one of [std_default, cuda, cpu]. If not specified, use --rng
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow,
|
||||
flux2_flow]
|
||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is
|
||||
auto. In auto mode, if the model weights contain any quantized parameters,
|
||||
the at_runtime mode will be used; otherwise, immediately will be used.The
|
||||
immediately mode may have precision and compatibility issues with quantized
|
||||
parameters, but it usually offers faster inference speed and, in some cases,
|
||||
lower memory usage. The at_runtime mode, on the other hand, is exactly the
|
||||
opposite.
|
||||
|
||||
Default Generation Options:
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--mask <string> path to the mask image
|
||||
--control-image <string> path to control image, control net
|
||||
--control-video <string> path to control video frames, It must be a directory path. The video frames
|
||||
inside should be stored as images in lexicographical (character) order. For
|
||||
example, if the control video path is `frames`, the directory contain images
|
||||
such as 00.png, 01.png, ... etc.
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
--hires-upscaler <string> highres fix upscaler, Lanczos, Nearest, Latent, Latent (nearest), Latent
|
||||
(nearest-exact), Latent (antialiased), Latent (bicubic), Latent (bicubic
|
||||
antialiased), or a model name under --hires-upscalers-dir (default: Latent)
|
||||
--extra-sample-args <string> extra sampler/scheduler/guidance args, key=value list. APG supports apg_eta,
|
||||
apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports
|
||||
slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end;
|
||||
ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma
|
||||
--extra-tiling-args <string> extra VAE tiling args, key=value list. LTX video VAE supports
|
||||
temporal_tile_frames (default: 4), temporal_tile_overlap (default: 1)
|
||||
-H, --height <int> image height, in pixel space (default: 512)
|
||||
-W, --width <int> image width, in pixel space (default: 512)
|
||||
--steps <int> number of sample steps (default: 20)
|
||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
||||
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer
|
||||
(default: -1). <= 0 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
|
||||
-b, --batch-count <int> batch count
|
||||
--video-frames <int> video frames (default: 1)
|
||||
--fps <int> fps (default: 24)
|
||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for
|
||||
NitroSD-Realism around 250 and 500 for NitroSD-Vibrant
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
||||
--hires-width <int> highres fix target width, 0 to use --hires-scale (default: 0)
|
||||
--hires-height <int> highres fix target height, 0 to use --hires-scale (default: 0)
|
||||
--hires-steps <int> highres fix second pass sample steps, 0 to reuse --steps (default: 0)
|
||||
--hires-upscale-tile-size <int> highres fix upscaler tile size, reserved for model-backed upscalers (default:
|
||||
128)
|
||||
--cfg-scale <float> unconditional guidance scale: (default: 7.0)
|
||||
--img-cfg-scale <float> image guidance scale for inpaint or image edit models: (default: same as
|
||||
--cfg-scale)
|
||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--slg-scale <float> skip layer guidance (SLG) scale, only for DiT models: (default: 0). 0 means
|
||||
disabled, a value of 2.5 is nice for sd3.5 medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> noise multiplier (default: 0 for ddim_trailing, tcd, res_multistep and
|
||||
res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
||||
--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 image edit models (default:
|
||||
same as --cfg-scale)
|
||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input
|
||||
(default: 3.5)
|
||||
--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) noise multiplier (default: 0 for ddim_trailing, tcd,
|
||||
res_multistep and res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
||||
--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
|
||||
--moe-boundary <float> timestep boundary for Wan2.2 MoE model. (default: 0.875). Only enabled if
|
||||
`--high-noise-steps` is set to -1
|
||||
--vace-strength <float> wan vace strength
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--hires-scale <float> highres fix scale when target size is not set (default: 2.0)
|
||||
--hires-denoising-strength <float> highres fix second pass denoising strength (default: 0.7)
|
||||
--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
|
||||
--disable-image-metadata do not embed generation metadata on image files
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--temporal-tiling enable temporal tiling for LTX video VAE decode
|
||||
--hires enable highres fix
|
||||
-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,
|
||||
er_sde, euler_cfg_pp, euler_a_cfg_pp] (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, er_sde, euler_cfg_pp, euler_a_cfg_pp] 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, ltx2], default:
|
||||
model-specific
|
||||
--sigmas custom sigma values for the sampler, comma-separated (e.g.,
|
||||
"14.61,7.8,3.5,0.0").
|
||||
--hires-sigmas custom sigma values for the highres fix second pass, comma-separated (e.g.,
|
||||
"0.85,0.725,0.421875,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), '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=; spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=.
|
||||
Examples: "threshold=0.25" or "threshold=1.5,reset=0"
|
||||
--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'
|
||||
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||
--vae-relative-tile-size relative tile size for vae tiling, format [X]x[Y], in fraction of image size
|
||||
if < 1, in number of tiles per dim if >=1 (overrides --vae-tile-size)
|
||||
```bash
|
||||
./bin/sd-server -h
|
||||
```
|
||||
|
||||
@@ -85,7 +85,7 @@ int main(int argc, const char** argv) {
|
||||
LOG_DEBUG("%s", ctx_params.to_string().c_str());
|
||||
LOG_DEBUG("%s", default_gen_params.to_string().c_str());
|
||||
|
||||
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(false, false, false);
|
||||
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(false);
|
||||
SDCtxPtr sd_ctx(new_sd_ctx(&sd_ctx_params));
|
||||
|
||||
if (sd_ctx == nullptr) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
$patterns = @(
|
||||
"src/*.cpp"
|
||||
"src/*.h"
|
||||
"src/*.hpp"
|
||||
"src/conditioning/*.cpp"
|
||||
"src/conditioning/*.h"
|
||||
"src/conditioning/*.hpp"
|
||||
"src/core/*.cpp"
|
||||
"src/core/*.h"
|
||||
"src/core/*.hpp"
|
||||
"src/extensions/*.cpp"
|
||||
"src/extensions/*.h"
|
||||
"src/extensions/*.hpp"
|
||||
"src/runtime/*.cpp"
|
||||
"src/runtime/*.h"
|
||||
"src/runtime/*.hpp"
|
||||
"src/model/*/*.cpp"
|
||||
"src/model/*/*.h"
|
||||
"src/model/*/*.hpp"
|
||||
"src/tokenizers/*.h"
|
||||
"src/tokenizers/*.cpp"
|
||||
"src/tokenizers/vocab/*.h"
|
||||
"src/tokenizers/vocab/*.cpp"
|
||||
"src/model_io/*.h"
|
||||
"src/model_io/*.cpp"
|
||||
"examples/cli/*.cpp"
|
||||
"examples/cli/*.h"
|
||||
"examples/server/*.cpp"
|
||||
"examples/common/*.hpp"
|
||||
"examples/common/*.h"
|
||||
"examples/common/*.cpp"
|
||||
)
|
||||
|
||||
$root = (Get-Location).Path
|
||||
|
||||
foreach ($pattern in $patterns) {
|
||||
$files = Get-ChildItem -Path $pattern -File -ErrorAction SilentlyContinue | Sort-Object FullName
|
||||
|
||||
foreach ($file in $files) {
|
||||
$relativePath = $file.FullName.Substring($root.Length).TrimStart('\', '/') -replace '\\', '/'
|
||||
|
||||
if ($relativePath -like "vocab*") {
|
||||
continue
|
||||
}
|
||||
|
||||
Write-Host "formatting '$relativePath'"
|
||||
|
||||
# if ($relativePath -ne "stable-diffusion.h") {
|
||||
# clang-tidy -fix -p build_linux/ "$relativePath"
|
||||
# }
|
||||
|
||||
& clang-format -style=file -i $relativePath
|
||||
}
|
||||
}
|
||||
@@ -196,19 +196,13 @@ typedef struct {
|
||||
uint32_t embedding_count;
|
||||
const char* photo_maker_path;
|
||||
const char* tensor_type_rules;
|
||||
bool vae_decode_only;
|
||||
bool free_params_immediately;
|
||||
int n_threads;
|
||||
enum sd_type_t wtype;
|
||||
enum rng_type_t rng_type;
|
||||
enum rng_type_t sampler_rng_type;
|
||||
enum prediction_t prediction;
|
||||
enum lora_apply_mode_t lora_apply_mode;
|
||||
bool offload_params_to_cpu;
|
||||
bool enable_mmap;
|
||||
bool keep_clip_on_cpu;
|
||||
bool keep_control_net_on_cpu;
|
||||
bool keep_vae_on_cpu;
|
||||
bool flash_attn;
|
||||
bool diffusion_flash_attn;
|
||||
bool tae_preview_only;
|
||||
@@ -226,6 +220,7 @@ typedef struct {
|
||||
bool stream_layers; // Enable residency+prefetch streaming on top of --max-vram (no effect without --max-vram)
|
||||
const char* backend;
|
||||
const char* params_backend;
|
||||
const char* rpc_servers;
|
||||
} sd_ctx_params_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -460,7 +455,6 @@ SD_API bool generate_video(sd_ctx_t* sd_ctx,
|
||||
typedef struct upscaler_ctx_t upscaler_ctx_t;
|
||||
|
||||
SD_API upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads,
|
||||
int tile_size,
|
||||
@@ -491,6 +485,10 @@ SD_API bool preprocess_canny(sd_image_t image,
|
||||
SD_API const char* sd_commit(void);
|
||||
SD_API const char* sd_version(void);
|
||||
|
||||
// for C API, caller needs to call free_sd_images to free the memory after use
|
||||
// This helps avoid CRT problems on Windows when memory is allocated in the library but freed in the caller, which may use a different CRT.
|
||||
SD_API void free_sd_images(sd_image_t* result_images, int num_images);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+174
-258
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_CONDITIONING_CONDITIONER_HPP__
|
||||
#ifndef __SD_CONDITIONING_CONDITIONER_HPP__
|
||||
#define __SD_CONDITIONING_CONDITIONER_HPP__
|
||||
|
||||
#include <cmath>
|
||||
@@ -113,14 +113,12 @@ struct Conditioner {
|
||||
public:
|
||||
virtual SDCondition get_learned_condition(int n_threads,
|
||||
const ConditionerParams& conditioner_params) = 0;
|
||||
virtual bool alloc_params_buffer() = 0;
|
||||
virtual void free_params_buffer() = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) = 0;
|
||||
virtual size_t get_params_buffer_size() = 0;
|
||||
virtual void set_max_graph_vram_bytes(size_t max_vram_bytes) {}
|
||||
virtual void set_stream_layers_enabled(bool enabled) {}
|
||||
virtual void set_flash_attention_enabled(bool enabled) = 0;
|
||||
virtual void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) {}
|
||||
virtual void runner_done() {}
|
||||
};
|
||||
|
||||
// ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
@@ -138,10 +136,10 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
std::map<std::string, std::pair<int, int>> embedding_pos_map;
|
||||
|
||||
FrozenCLIPEmbedderWithCustomWords(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::map<std::string, std::string>& orig_embedding_map,
|
||||
SDVersion version = VERSION_SD1)
|
||||
SDVersion version = VERSION_SD1,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: version(version), tokenizer(sd_version_is_sd2(version) ? 0 : 49407) {
|
||||
for (const auto& kv : orig_embedding_map) {
|
||||
std::string name = kv.first;
|
||||
@@ -151,12 +149,12 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
bool force_clip_f32 = !embedding_map.empty();
|
||||
if (sd_version_is_sd1(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, force_clip_f32);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, force_clip_f32, weight_manager);
|
||||
} else if (sd_version_is_sd2(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPEN_CLIP_VIT_H_14, true, force_clip_f32);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPEN_CLIP_VIT_H_14, true, force_clip_f32, weight_manager);
|
||||
} else if (sd_version_is_sdxl(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, force_clip_f32);
|
||||
text_model2 = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "cond_stage_model.1.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, force_clip_f32);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, force_clip_f32, weight_manager);
|
||||
text_model2 = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "cond_stage_model.1.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, force_clip_f32, weight_manager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,33 +165,6 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!text_model->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
if (!text_model2->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
text_model->free_params_buffer();
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->free_params_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = text_model->get_params_buffer_size();
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
buffer_size += text_model2->get_params_buffer_size();
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
text_model->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
@@ -222,6 +193,13 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
text_model->runner_done();
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->runner_done();
|
||||
}
|
||||
}
|
||||
|
||||
bool load_embedding(std::string embd_name, std::string embd_path, std::vector<int32_t>& bpe_tokens) {
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file_and_convert_name(embd_path)) {
|
||||
@@ -263,7 +241,8 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
}
|
||||
return true;
|
||||
};
|
||||
model_loader.load_tensors(on_load, 1);
|
||||
model_loader.set_n_threads(1);
|
||||
model_loader.load_tensors(on_load);
|
||||
int pos_start = num_custom_embeddings;
|
||||
if (embd) {
|
||||
int64_t hidden_size = text_model->model.hidden_size;
|
||||
@@ -432,7 +411,10 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states.empty());
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
auto chunk_hidden_states2 = text_model2->compute(n_threads,
|
||||
@@ -441,7 +423,10 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states2.empty());
|
||||
chunk_hidden_states = sd::ops::concat(chunk_hidden_states, chunk_hidden_states2, 0);
|
||||
|
||||
@@ -452,7 +437,10 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!pooled.empty());
|
||||
}
|
||||
}
|
||||
@@ -523,15 +511,15 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
|
||||
struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
CLIPVisionModelProjection vision_model;
|
||||
std::string weight_prefix = "cond_stage_model.transformer";
|
||||
|
||||
FrozenCLIPVisionEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {})
|
||||
: GGMLRunner(backend, params_backend) {
|
||||
std::string prefix = "cond_stage_model.transformer";
|
||||
bool proj_in = false;
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager) {
|
||||
bool proj_in = false;
|
||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!starts_with(name, prefix)) {
|
||||
if (!starts_with(name, weight_prefix)) {
|
||||
continue;
|
||||
}
|
||||
if (contains(name, "self_attn.in_proj")) {
|
||||
@@ -540,7 +528,7 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
}
|
||||
}
|
||||
vision_model = CLIPVisionModelProjection(OPEN_CLIP_VIT_H_14, false, proj_in);
|
||||
vision_model.init(params_ctx, tensor_storage_map, prefix);
|
||||
vision_model.init(params_ctx, tensor_storage_map, weight_prefix);
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
@@ -548,7 +536,7 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
||||
vision_model.get_param_tensors(tensors, "cond_stage_model.transformer");
|
||||
vision_model.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& pixel_values_tensor, bool return_pooled, int clip_skip) {
|
||||
@@ -571,7 +559,7 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(pixel_values, return_pooled, clip_skip);
|
||||
};
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true));
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true, true, true));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -584,8 +572,8 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
std::shared_ptr<T5Runner> t5;
|
||||
|
||||
SD3CLIPEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {})
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: clip_g_tokenizer(0) {
|
||||
bool use_clip_l = false;
|
||||
bool use_clip_g = false;
|
||||
@@ -604,13 +592,13 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
return;
|
||||
}
|
||||
if (use_clip_l) {
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, false);
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, false, weight_manager);
|
||||
}
|
||||
if (use_clip_g) {
|
||||
clip_g = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "text_encoders.clip_g.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false);
|
||||
clip_g = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "text_encoders.clip_g.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, false, weight_manager);
|
||||
}
|
||||
if (use_t5) {
|
||||
t5 = std::make_shared<T5Runner>(backend, params_backend, tensor_storage_map, "text_encoders.t5xxl.transformer");
|
||||
t5 = std::make_shared<T5Runner>(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", false, weight_manager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,51 +614,6 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (clip_l) {
|
||||
if (!clip_l->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (clip_g) {
|
||||
if (!clip_g->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (t5) {
|
||||
if (!t5->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
if (clip_l) {
|
||||
clip_l->free_params_buffer();
|
||||
}
|
||||
if (clip_g) {
|
||||
clip_g->free_params_buffer();
|
||||
}
|
||||
if (t5) {
|
||||
t5->free_params_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = 0;
|
||||
if (clip_l) {
|
||||
buffer_size += clip_l->get_params_buffer_size();
|
||||
}
|
||||
if (clip_g) {
|
||||
buffer_size += clip_g->get_params_buffer_size();
|
||||
}
|
||||
if (t5) {
|
||||
buffer_size += t5->get_params_buffer_size();
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
@@ -719,6 +662,18 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
if (clip_l) {
|
||||
clip_l->runner_done();
|
||||
}
|
||||
if (clip_g) {
|
||||
clip_g->runner_done();
|
||||
}
|
||||
if (t5) {
|
||||
t5->runner_done();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> tokenize(std::string text,
|
||||
size_t min_length = 0,
|
||||
size_t max_length = 0,
|
||||
@@ -834,7 +789,10 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
nullptr,
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states_l.empty());
|
||||
chunk_hidden_states_l = ::apply_token_weights(std::move(chunk_hidden_states_l), chunk_weights);
|
||||
|
||||
@@ -847,13 +805,16 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
nullptr,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!pooled_l.empty());
|
||||
}
|
||||
} else {
|
||||
chunk_hidden_states_l = sd::Tensor<float>::zeros({768, static_cast<int64_t>(chunk_len), 1});
|
||||
if (chunk_idx == 0) {
|
||||
pooled = sd::Tensor<float>::zeros({768, 1});
|
||||
pooled_l = sd::Tensor<float>::zeros({768, 1});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,7 +836,10 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
nullptr,
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states_g.empty());
|
||||
chunk_hidden_states_g = ::apply_token_weights(std::move(chunk_hidden_states_g), chunk_weights);
|
||||
|
||||
@@ -888,7 +852,10 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
nullptr,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!pooled_g.empty());
|
||||
}
|
||||
} else {
|
||||
@@ -910,7 +877,10 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
|
||||
chunk_hidden_states_t5 = t5->compute(n_threads,
|
||||
input_ids,
|
||||
sd::Tensor<float>());
|
||||
sd::Tensor<float>(),
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states_t5.empty());
|
||||
chunk_hidden_states_t5 = ::apply_token_weights(std::move(chunk_hidden_states_t5), chunk_weights);
|
||||
} else {
|
||||
@@ -971,8 +941,8 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
size_t chunk_len = 256;
|
||||
|
||||
FluxCLIPEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {}) {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr) {
|
||||
bool use_clip_l = false;
|
||||
bool use_t5 = false;
|
||||
for (auto pair : tensor_storage_map) {
|
||||
@@ -989,12 +959,12 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
|
||||
if (use_clip_l) {
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, params_backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, true);
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, false, weight_manager);
|
||||
} else {
|
||||
LOG_WARN("clip_l text encoder not found! Prompt adherence might be degraded.");
|
||||
}
|
||||
if (use_t5) {
|
||||
t5 = std::make_shared<T5Runner>(backend, params_backend, tensor_storage_map, "text_encoders.t5xxl.transformer");
|
||||
t5 = std::make_shared<T5Runner>(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", false, weight_manager);
|
||||
} else {
|
||||
LOG_WARN("t5xxl text encoder not found! Prompt adherence might be degraded.");
|
||||
}
|
||||
@@ -1009,40 +979,6 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (clip_l) {
|
||||
if (!clip_l->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (t5) {
|
||||
if (!t5->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
if (clip_l) {
|
||||
clip_l->free_params_buffer();
|
||||
}
|
||||
if (t5) {
|
||||
t5->free_params_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = 0;
|
||||
if (clip_l) {
|
||||
buffer_size += clip_l->get_params_buffer_size();
|
||||
}
|
||||
if (t5) {
|
||||
buffer_size += t5->get_params_buffer_size();
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
@@ -1070,7 +1006,7 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) {
|
||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||
if (clip_l) {
|
||||
clip_l->set_weight_adapter(adapter);
|
||||
}
|
||||
@@ -1079,6 +1015,15 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
if (clip_l) {
|
||||
clip_l->runner_done();
|
||||
}
|
||||
if (t5) {
|
||||
t5->runner_done();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> tokenize(std::string text,
|
||||
size_t min_length = 0,
|
||||
size_t max_length = 0) {
|
||||
@@ -1177,7 +1122,10 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
nullptr,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip);
|
||||
clip_skip,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!pooled.empty());
|
||||
} else {
|
||||
pooled = sd::Tensor<float>::zeros({768});
|
||||
@@ -1195,7 +1143,10 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
sd::Tensor<int32_t> input_ids({static_cast<int64_t>(chunk_tokens.size())}, chunk_tokens);
|
||||
chunk_hidden_states = t5->compute(n_threads,
|
||||
input_ids,
|
||||
sd::Tensor<float>());
|
||||
sd::Tensor<float>(),
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states.empty());
|
||||
chunk_hidden_states = ::apply_token_weights(std::move(chunk_hidden_states), chunk_weights);
|
||||
if (zero_out_masked) {
|
||||
@@ -1239,11 +1190,11 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
bool is_umt5 = false;
|
||||
|
||||
T5CLIPEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
bool use_mask = false,
|
||||
int mask_pad = 0,
|
||||
bool is_umt5 = false)
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
bool use_mask = false,
|
||||
int mask_pad = 0,
|
||||
bool is_umt5 = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: use_mask(use_mask), mask_pad(mask_pad), t5_tokenizer(is_umt5) {
|
||||
bool use_t5 = false;
|
||||
for (auto pair : tensor_storage_map) {
|
||||
@@ -1256,7 +1207,7 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
LOG_WARN("IMPORTANT NOTICE: No text encoders provided, cannot process prompts!");
|
||||
return;
|
||||
} else {
|
||||
t5 = std::make_shared<T5Runner>(backend, params_backend, tensor_storage_map, "text_encoders.t5xxl.transformer", is_umt5);
|
||||
t5 = std::make_shared<T5Runner>(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", is_umt5, weight_manager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,29 +1217,6 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (t5) {
|
||||
if (!t5->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
if (t5) {
|
||||
t5->free_params_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = 0;
|
||||
if (t5) {
|
||||
buffer_size += t5->get_params_buffer_size();
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
if (t5) {
|
||||
t5->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
@@ -1313,6 +1241,12 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
if (t5) {
|
||||
t5->runner_done();
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
size_t min_length = 0,
|
||||
size_t max_length = 0) {
|
||||
@@ -1406,7 +1340,10 @@ struct T5CLIPEmbedder : public Conditioner {
|
||||
|
||||
auto chunk_hidden_states = t5->compute(n_threads,
|
||||
input_ids,
|
||||
t5_attn_mask_chunk);
|
||||
t5_attn_mask_chunk,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!chunk_hidden_states.empty());
|
||||
chunk_hidden_states = apply_token_weights(std::move(chunk_hidden_states), chunk_weights);
|
||||
|
||||
@@ -1450,36 +1387,21 @@ struct AnimaConditioner : public Conditioner {
|
||||
std::shared_ptr<LLM::LLMRunner> llm;
|
||||
|
||||
AnimaConditioner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {}) {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr) {
|
||||
qwen_tokenizer = std::make_shared<Qwen2Tokenizer>();
|
||||
llm = std::make_shared<LLM::LLMRunner>(LLM::LLMArch::QWEN3,
|
||||
backend,
|
||||
params_backend,
|
||||
tensor_storage_map,
|
||||
"text_encoders.llm",
|
||||
false);
|
||||
false,
|
||||
weight_manager);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
llm->get_param_tensors(tensors, "text_encoders.llm");
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!llm->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
llm->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
return llm->get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
llm->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
}
|
||||
@@ -1496,6 +1418,10 @@ struct AnimaConditioner : public Conditioner {
|
||||
llm->set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
llm->runner_done();
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<int>, std::vector<float>> tokenize(std::string text) {
|
||||
auto parsed_attention = parse_prompt_attention(text);
|
||||
|
||||
@@ -1553,7 +1479,11 @@ struct AnimaConditioner : public Conditioner {
|
||||
input_ids,
|
||||
sd::Tensor<float>(),
|
||||
{},
|
||||
{});
|
||||
{},
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!hidden_states.empty());
|
||||
hidden_states = apply_token_weights(std::move(hidden_states), qwen_weights);
|
||||
auto t5_ids_tensor = sd::Tensor<int32_t>::from_vector(t5_tokens);
|
||||
@@ -1576,11 +1506,11 @@ struct LLMEmbedder : public Conditioner {
|
||||
std::shared_ptr<LLM::LLMRunner> llm;
|
||||
|
||||
LLMEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false)
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: version(version) {
|
||||
LLM::LLMArch arch = LLM::LLMArch::QWEN2_5_VL;
|
||||
if (version == VERSION_FLUX2) {
|
||||
@@ -1607,33 +1537,16 @@ struct LLMEmbedder : public Conditioner {
|
||||
}
|
||||
llm = std::make_shared<LLM::LLMRunner>(arch,
|
||||
backend,
|
||||
params_backend,
|
||||
tensor_storage_map,
|
||||
"text_encoders.llm",
|
||||
enable_vision);
|
||||
enable_vision,
|
||||
weight_manager);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
llm->get_param_tensors(tensors, "text_encoders.llm");
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!llm->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
llm->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
size_t buffer_size = 0;
|
||||
buffer_size += llm->get_params_buffer_size();
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
|
||||
llm->set_max_graph_vram_bytes(max_vram_bytes);
|
||||
}
|
||||
@@ -1652,6 +1565,12 @@ struct LLMEmbedder : public Conditioner {
|
||||
}
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
if (llm) {
|
||||
llm->runner_done();
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
const std::pair<int, int>& attn_range,
|
||||
size_t min_length = 0,
|
||||
@@ -1747,7 +1666,11 @@ struct LLMEmbedder : public Conditioner {
|
||||
input_ids,
|
||||
attention_mask,
|
||||
image_embeds,
|
||||
out_layers);
|
||||
out_layers,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!hidden_states.empty());
|
||||
hidden_states = apply_token_weights(std::move(hidden_states), weights);
|
||||
GGML_ASSERT(hidden_states.shape()[1] > prompt_template_encode_start_idx);
|
||||
@@ -1825,7 +1748,7 @@ struct LLMEmbedder : public Conditioner {
|
||||
|
||||
auto resized_image = clip_preprocess(image, w_bar, h_bar);
|
||||
|
||||
auto image_embed = llm->encode_image(n_threads, resized_image);
|
||||
auto image_embed = llm->encode_image(n_threads, resized_image, false, true, true);
|
||||
GGML_ASSERT(!image_embed.empty());
|
||||
image_embeds.emplace_back(image_embed_idx, image_embed);
|
||||
image_embed_idx += 1 + static_cast<int>(image_embed.shape()[1]) + 6;
|
||||
@@ -1895,7 +1818,7 @@ struct LLMEmbedder : public Conditioner {
|
||||
LOG_DEBUG("resize conditioner ref image %d from %dx%d to %dx%d", i, height, width, h_bar, w_bar);
|
||||
|
||||
auto resized_image = clip_preprocess(image, w_bar, h_bar);
|
||||
auto image_embed = llm->encode_image(n_threads, resized_image);
|
||||
auto image_embed = llm->encode_image(n_threads, resized_image, false, true, true);
|
||||
GGML_ASSERT(!image_embed.empty());
|
||||
image_embeds.emplace_back(image_embed_idx, image_embed);
|
||||
image_embed_idx += 1 + static_cast<int>(image_embed.shape()[1]) + 6;
|
||||
@@ -2138,10 +2061,10 @@ struct LTXAVTextProjectionRunner : public GGMLRunner {
|
||||
LTXAVTextProjection model;
|
||||
|
||||
LTXAVTextProjectionRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "")
|
||||
: GGMLRunner(backend, params_backend),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
model(tensor_storage_map.find(prefix + ".video_aggregate_embed.weight") != tensor_storage_map.end()) {
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
@@ -2163,11 +2086,15 @@ struct LTXAVTextProjectionRunner : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads, const sd::Tensor<float>& x) {
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
const sd::Tensor<float>& x,
|
||||
bool auto_free = true,
|
||||
bool free_compute_buffer = true,
|
||||
bool free_compute_params = true) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x);
|
||||
};
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true));
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2182,22 +2109,22 @@ struct LTXAVEmbedder : public Conditioner {
|
||||
bool dual_projection = false;
|
||||
|
||||
LTXAVEmbedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& llm_prefix = "text_encoders.llm",
|
||||
const std::string& projector_prefix = "text_embedding_projection") {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& llm_prefix = "text_encoders.llm",
|
||||
const std::string& projector_prefix = "text_embedding_projection",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr) {
|
||||
tokenizer = std::make_shared<GemmaTokenizer>();
|
||||
llm = std::make_shared<LLM::LLMRunner>(LLM::LLMArch::GEMMA3_12B,
|
||||
backend,
|
||||
params_backend,
|
||||
tensor_storage_map,
|
||||
llm_prefix,
|
||||
false);
|
||||
false,
|
||||
weight_manager);
|
||||
dual_projection = tensor_storage_map.find(projector_prefix + ".video_aggregate_embed.weight") != tensor_storage_map.end();
|
||||
projector = std::make_shared<LTXAVTextProjectionRunner>(backend,
|
||||
params_backend,
|
||||
tensor_storage_map,
|
||||
projector_prefix);
|
||||
projector_prefix,
|
||||
weight_manager);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
@@ -2205,25 +2132,6 @@ struct LTXAVEmbedder : public Conditioner {
|
||||
projector->get_param_tensors(tensors, "text_embedding_projection");
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!llm->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
if (!projector->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
llm->free_params_buffer();
|
||||
projector->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
return llm->get_params_buffer_size() + projector->get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_flash_attention_enabled(bool enabled) override {
|
||||
llm->set_flash_attention_enabled(enabled);
|
||||
projector->set_flash_attention_enabled(enabled);
|
||||
@@ -2239,6 +2147,11 @@ struct LTXAVEmbedder : public Conditioner {
|
||||
projector->set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
llm->runner_done();
|
||||
projector->runner_done();
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
const std::pair<int, int>& attn_range) {
|
||||
std::vector<std::pair<std::string, float>> parsed_attention;
|
||||
@@ -2302,6 +2215,9 @@ struct LTXAVEmbedder : public Conditioner {
|
||||
attention_mask,
|
||||
{},
|
||||
{},
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true);
|
||||
GGML_ASSERT(!hidden_states.empty());
|
||||
hidden_states = apply_token_weights(std::move(hidden_states), weights);
|
||||
@@ -2361,7 +2277,7 @@ struct LTXAVEmbedder : public Conditioner {
|
||||
}
|
||||
|
||||
hidden_states.reshape_({kNumStates * kHiddenSize, valid_tokens});
|
||||
return projector->compute(n_threads, hidden_states);
|
||||
return projector->compute(n_threads, hidden_states, false, true, true);
|
||||
}
|
||||
|
||||
SDCondition get_learned_condition(int n_threads,
|
||||
|
||||
+299
-726
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,10 @@ static bool is_default_backend_token(const std::string& name) {
|
||||
return lower.empty() || lower == "default" || lower == "auto";
|
||||
}
|
||||
|
||||
static bool is_disk_backend_token(const std::string& name) {
|
||||
return lower_copy(trim_copy(name)) == "disk";
|
||||
}
|
||||
|
||||
static bool parse_backend_module(const std::string& raw_name, SDBackendModule* module) {
|
||||
std::string name = lower_copy(trim_copy(raw_name));
|
||||
name.erase(std::remove(name.begin(), name.end(), '-'), name.end());
|
||||
@@ -200,6 +204,36 @@ void ggml_ext_im_set_f32_1d(const struct ggml_tensor* tensor, int i, float value
|
||||
}
|
||||
}
|
||||
|
||||
bool add_rpc_devices(const std::string& servers) {
|
||||
const std::string in = trim_copy(servers);
|
||||
if (in.empty()) {
|
||||
return true;
|
||||
}
|
||||
auto rpc_servers = split_copy(in, ',');
|
||||
if (rpc_servers.empty()) {
|
||||
LOG_ERROR("invalid RPC servers specification: '%s'", servers.c_str());
|
||||
return false;
|
||||
}
|
||||
ggml_backend_reg_t rpc_reg = ggml_backend_reg_by_name("RPC");
|
||||
if (!rpc_reg) {
|
||||
LOG_ERROR("RPC backend not found, cannot add RPC servers");
|
||||
return false;
|
||||
}
|
||||
typedef ggml_backend_reg_t (*ggml_backend_rpc_add_server_t)(const char* endpoint);
|
||||
ggml_backend_rpc_add_server_t ggml_backend_rpc_add_server_fn = (ggml_backend_rpc_add_server_t)ggml_backend_reg_get_proc_address(rpc_reg, "ggml_backend_rpc_add_server");
|
||||
if (!ggml_backend_rpc_add_server_fn) {
|
||||
LOG_ERROR("RPC backend does not have ggml_backend_rpc_add_server function, cannot add RPC servers");
|
||||
return false;
|
||||
}
|
||||
for (const auto& server : rpc_servers) {
|
||||
LOG_INFO("Adding RPC server: %s", server.c_str());
|
||||
auto reg = ggml_backend_rpc_add_server_fn(server.c_str());
|
||||
// no return value to check for success but should print errors from the RPC backend if it fails to add the server
|
||||
ggml_backend_register(reg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ggml_backend_load_all_once() {
|
||||
// If the registry already has devices and the CPU backend is present,
|
||||
// assume either static registration or explicit host-side preloading has
|
||||
@@ -504,6 +538,9 @@ ggml_backend_t SDBackendManager::params_backend(SDBackendModule module) {
|
||||
if (name.empty()) {
|
||||
return runtime_backend(module);
|
||||
}
|
||||
if (is_disk_backend_token(name)) {
|
||||
return runtime_backend(module);
|
||||
}
|
||||
return init_cached_backend(name);
|
||||
}
|
||||
|
||||
@@ -515,6 +552,10 @@ bool SDBackendManager::params_backend_is_cpu(SDBackendModule module) {
|
||||
return sd_backend_is_cpu(params_backend(module));
|
||||
}
|
||||
|
||||
bool SDBackendManager::params_backend_is_disk(SDBackendModule module) const {
|
||||
return is_disk_backend_token(params_assignment_.get(module));
|
||||
}
|
||||
|
||||
bool SDBackendManager::runtime_backend_supports_host_buffer(SDBackendModule module) {
|
||||
ggml_backend_t backend = runtime_backend(module);
|
||||
if (backend == nullptr) {
|
||||
@@ -534,10 +575,6 @@ bool SDBackendManager::runtime_backend_supports_host_buffer(SDBackendModule modu
|
||||
|
||||
bool SDBackendManager::init(const char* backend_spec,
|
||||
const char* params_backend_spec,
|
||||
bool offload_params_to_cpu,
|
||||
bool keep_clip_on_cpu,
|
||||
bool keep_vae_on_cpu,
|
||||
bool keep_control_net_on_cpu,
|
||||
std::string* error) {
|
||||
reset();
|
||||
|
||||
@@ -548,30 +585,20 @@ bool SDBackendManager::init(const char* backend_spec,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (runtime_assignment_.empty()) {
|
||||
if (keep_clip_on_cpu) {
|
||||
runtime_assignment_.set_module(SDBackendModule::TE, "cpu");
|
||||
}
|
||||
if (keep_vae_on_cpu) {
|
||||
runtime_assignment_.set_module(SDBackendModule::VAE, "cpu");
|
||||
}
|
||||
if (keep_control_net_on_cpu) {
|
||||
runtime_assignment_.set_module(SDBackendModule::CONTROL_NET, "cpu");
|
||||
}
|
||||
}
|
||||
|
||||
if (params_assignment_.empty() && offload_params_to_cpu) {
|
||||
params_assignment_.set_default("cpu");
|
||||
}
|
||||
|
||||
return validate(error);
|
||||
}
|
||||
|
||||
bool SDBackendManager::validate(std::string* error) const {
|
||||
auto validate_name = [&](const std::string& name) -> bool {
|
||||
auto validate_runtime_name = [&](const std::string& name) -> bool {
|
||||
if (is_default_backend_token(name)) {
|
||||
return true;
|
||||
}
|
||||
if (is_disk_backend_token(name)) {
|
||||
if (error != nullptr) {
|
||||
*error = "backend 'disk' is only supported by params_backend";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!sd_resolve_backend_name(name).empty()) {
|
||||
return true;
|
||||
}
|
||||
@@ -580,18 +607,24 @@ bool SDBackendManager::validate(std::string* error) const {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto validate_params_name = [&](const std::string& name) -> bool {
|
||||
if (is_disk_backend_token(name)) {
|
||||
return true;
|
||||
}
|
||||
return validate_runtime_name(name);
|
||||
};
|
||||
|
||||
if (!validate_name(runtime_assignment_.default_name) ||
|
||||
!validate_name(params_assignment_.default_name)) {
|
||||
if (!validate_runtime_name(runtime_assignment_.default_name) ||
|
||||
!validate_params_name(params_assignment_.default_name)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& kv : runtime_assignment_.module_names) {
|
||||
if (!validate_name(kv.second)) {
|
||||
if (!validate_runtime_name(kv.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& kv : params_assignment_.module_names) {
|
||||
if (!validate_name(kv.second)) {
|
||||
if (!validate_params_name(kv.second)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,6 @@ public:
|
||||
|
||||
bool init(const char* backend_spec,
|
||||
const char* params_backend_spec,
|
||||
bool offload_params_to_cpu,
|
||||
bool keep_clip_on_cpu,
|
||||
bool keep_vae_on_cpu,
|
||||
bool keep_control_net_on_cpu,
|
||||
std::string* error);
|
||||
void reset();
|
||||
|
||||
@@ -63,6 +59,7 @@ public:
|
||||
|
||||
bool runtime_backend_is_cpu(SDBackendModule module);
|
||||
bool params_backend_is_cpu(SDBackendModule module);
|
||||
bool params_backend_is_disk(SDBackendModule module) const;
|
||||
bool runtime_backend_supports_host_buffer(SDBackendModule module);
|
||||
|
||||
private:
|
||||
@@ -76,4 +73,5 @@ ggml_backend_t sd_backend_cpu_init();
|
||||
bool sd_backend_cpu_set_n_threads(ggml_backend_t backend_cpu, int n_threads);
|
||||
const char* sd_backend_module_name(SDBackendModule module);
|
||||
void ggml_ext_im_set_f32_1d(const struct ggml_tensor* tensor, int i, float value);
|
||||
bool add_rpc_devices(const std::string& servers);
|
||||
#endif // __SD_CORE_GGML_EXTEND_BACKEND_H__
|
||||
|
||||
+73
-20
@@ -44,7 +44,9 @@ namespace sd::ggml_graph_cut {
|
||||
if (tensor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return params_tensor_set.find(tensor) != params_tensor_set.end();
|
||||
return params_tensor_set.find(tensor) != params_tensor_set.end() ||
|
||||
(tensor->view_src != nullptr &&
|
||||
params_tensor_set.find(tensor->view_src) != params_tensor_set.end());
|
||||
}
|
||||
|
||||
static int graph_node_index_by_name(ggml_cgraph* gf, const char* name) {
|
||||
@@ -135,6 +137,24 @@ namespace sd::ggml_graph_cut {
|
||||
return max_vram_bytes_to_gib(resolve_auto_max_vram_bytes(-max_vram, backend));
|
||||
}
|
||||
|
||||
static bool is_segment_output_needed_after(const Plan& plan,
|
||||
size_t end_segment_index,
|
||||
int output_node_index) {
|
||||
if (end_segment_index + 1 >= plan.segments.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t seg_idx = end_segment_index + 1; seg_idx < plan.segments.size(); ++seg_idx) {
|
||||
const auto& segment = plan.segments[seg_idx];
|
||||
for (const auto& input_ref : segment.input_refs) {
|
||||
if (input_ref.type == Segment::INPUT_PREVIOUS_CUT &&
|
||||
input_ref.node_index == output_node_index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Segment make_segment_seed(const Plan& plan,
|
||||
size_t start_segment_index,
|
||||
size_t end_segment_index) {
|
||||
@@ -147,8 +167,11 @@ namespace sd::ggml_graph_cut {
|
||||
const auto& target_segment = plan.segments[end_segment_index];
|
||||
std::unordered_set<int> seen_output_node_indices;
|
||||
for (size_t seg_idx = start_segment_index; seg_idx <= end_segment_index; ++seg_idx) {
|
||||
const bool is_boundary_segment = seg_idx == end_segment_index;
|
||||
for (int output_node_index : plan.segments[seg_idx].output_node_indices) {
|
||||
if (seen_output_node_indices.insert(output_node_index).second) {
|
||||
if ((is_boundary_segment ||
|
||||
is_segment_output_needed_after(plan, end_segment_index, output_node_index)) &&
|
||||
seen_output_node_indices.insert(output_node_index).second) {
|
||||
seed.output_node_indices.push_back(output_node_index);
|
||||
}
|
||||
}
|
||||
@@ -400,23 +423,6 @@ namespace sd::ggml_graph_cut {
|
||||
return tensors;
|
||||
}
|
||||
|
||||
std::vector<ggml_tensor*> runtime_param_tensors(ggml_cgraph* gf, const Segment& segment, const char* log_desc) {
|
||||
std::vector<ggml_tensor*> tensors = param_tensors(gf, segment);
|
||||
std::vector<ggml_tensor*> filtered_tensors;
|
||||
filtered_tensors.reserve(tensors.size());
|
||||
for (ggml_tensor* tensor : tensors) {
|
||||
if (tensor_buffer(tensor) == nullptr) {
|
||||
LOG_WARN("%s graph cut skipping param input without buffer: segment=%s tensor=%s",
|
||||
log_desc == nullptr ? "unknown" : log_desc,
|
||||
segment.group_name.c_str(),
|
||||
tensor->name);
|
||||
continue;
|
||||
}
|
||||
filtered_tensors.push_back(tensor);
|
||||
}
|
||||
return filtered_tensors;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> collect_future_input_names(ggml_cgraph* gf,
|
||||
const Plan& plan,
|
||||
size_t current_segment_index) {
|
||||
@@ -487,6 +493,44 @@ namespace sd::ggml_graph_cut {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct TensorRuntimeBinding {
|
||||
ggml_backend_buffer_t buffer = nullptr;
|
||||
void* data = nullptr;
|
||||
void* extra = nullptr;
|
||||
};
|
||||
std::unordered_map<ggml_tensor*, TensorRuntimeBinding> saved_bindings;
|
||||
auto mark_measurement_external = [&](ggml_tensor* tensor) {
|
||||
if (tensor == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto save_tensor = [&](ggml_tensor* t) {
|
||||
if (t == nullptr || saved_bindings.find(t) != saved_bindings.end()) {
|
||||
return;
|
||||
}
|
||||
saved_bindings[t] = {t->buffer, t->data, t->extra};
|
||||
// During real execution params and previous-cut inputs already
|
||||
// have backend/cache buffers, so gallocr must not reserve them.
|
||||
t->data = reinterpret_cast<void*>(static_cast<uintptr_t>(1));
|
||||
};
|
||||
save_tensor(tensor);
|
||||
save_tensor(tensor->view_src);
|
||||
};
|
||||
for (const auto& input : segment.input_refs) {
|
||||
if (input.type != Segment::INPUT_PARAM &&
|
||||
input.type != Segment::INPUT_PREVIOUS_CUT) {
|
||||
continue;
|
||||
}
|
||||
mark_measurement_external(input_tensor(gf, input));
|
||||
}
|
||||
|
||||
std::unordered_map<ggml_tensor*, int32_t> saved_output_flags;
|
||||
for (int output_node_index : segment.output_node_indices) {
|
||||
ggml_tensor* output = ggml_graph_node(gf, output_node_index);
|
||||
if (output != nullptr && saved_output_flags.find(output) == saved_output_flags.end()) {
|
||||
saved_output_flags[output] = output->flags;
|
||||
}
|
||||
}
|
||||
|
||||
ggml_context* graph_ctx = nullptr;
|
||||
ggml_cgraph* segment_graph = build_segment_graph(gf, segment, &graph_ctx);
|
||||
ggml_gallocr_t allocr = ggml_gallocr_new(ggml_backend_get_default_buffer_type(backend));
|
||||
@@ -502,6 +546,14 @@ namespace sd::ggml_graph_cut {
|
||||
|
||||
ggml_gallocr_free(allocr);
|
||||
ggml_free(graph_ctx);
|
||||
for (const auto& kv : saved_output_flags) {
|
||||
kv.first->flags = kv.second;
|
||||
}
|
||||
for (const auto& kv : saved_bindings) {
|
||||
kv.first->buffer = kv.second.buffer;
|
||||
kv.first->data = kv.second.data;
|
||||
kv.first->extra = kv.second.extra;
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
@@ -669,7 +721,8 @@ namespace sd::ggml_graph_cut {
|
||||
GGML_ASSERT(!candidate_plan.segments.empty());
|
||||
|
||||
const auto& candidate_segment = candidate_plan.segments.back();
|
||||
if (graph_cut_segment_vram_bytes(candidate_segment) > max_graph_vram_bytes) {
|
||||
const size_t candidate_bytes = graph_cut_segment_vram_bytes(candidate_segment);
|
||||
if (candidate_bytes > max_graph_vram_bytes) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ namespace sd::ggml_graph_cut {
|
||||
ggml_tensor* output_tensor(ggml_cgraph* gf, const Segment& segment, size_t output_index);
|
||||
ggml_tensor* input_tensor(ggml_cgraph* gf, const Segment::InputRef& input_ref);
|
||||
std::vector<ggml_tensor*> param_tensors(ggml_cgraph* gf, const Segment& segment);
|
||||
std::vector<ggml_tensor*> runtime_param_tensors(ggml_cgraph* gf, const Segment& segment, const char* log_desc);
|
||||
std::unordered_set<std::string> collect_future_input_names(ggml_cgraph* gf,
|
||||
const Plan& plan,
|
||||
size_t current_segment_index);
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
#include "core/layer_registry.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/util.h"
|
||||
|
||||
namespace sd::layer_registry {
|
||||
|
||||
void LayerRegistry::register_layer(const std::string& name, ggml_tensor* tensor) {
|
||||
auto& info = layers_[name];
|
||||
info.tensors.push_back(tensor);
|
||||
info.bytes += ggml_nbytes(tensor);
|
||||
}
|
||||
|
||||
bool LayerRegistry::move_layer_to_gpu(const std::string& name) {
|
||||
auto it = layers_.find(name);
|
||||
if (it == layers_.end())
|
||||
return false;
|
||||
|
||||
LayerInfo& info = it->second;
|
||||
if (info.on_gpu)
|
||||
return true;
|
||||
if (gpu_backend_ == nullptr || cpu_backend_ == nullptr) {
|
||||
LOG_ERROR("layer_registry: backends not set; cannot move '%s' to GPU",
|
||||
name.c_str());
|
||||
return false;
|
||||
}
|
||||
if (info.tensors.empty()) {
|
||||
info.on_gpu = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 1. Build a no_alloc context big enough to hold one twin tensor per CPU
|
||||
// tensor, plus a little overhead.
|
||||
const size_t ctx_size = info.tensors.size() * ggml_tensor_overhead() + 1024;
|
||||
ggml_init_params ctx_params{ctx_size, /*mem_buffer=*/nullptr, /*no_alloc=*/true};
|
||||
ggml_context* twin_ctx = ggml_init(ctx_params);
|
||||
if (twin_ctx == nullptr) {
|
||||
LOG_ERROR("layer_registry: failed to allocate twin context for '%s'",
|
||||
name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Create one GPU twin per CPU tensor. The twin shares the original
|
||||
// name so any name-based lookup keeps working.
|
||||
std::vector<ggml_tensor*> gpu_twins;
|
||||
gpu_twins.reserve(info.tensors.size());
|
||||
for (ggml_tensor* cpu_t : info.tensors) {
|
||||
ggml_tensor* twin = ggml_dup_tensor(twin_ctx, cpu_t);
|
||||
if (cpu_t->name[0] != '\0') {
|
||||
ggml_set_name(twin, cpu_t->name);
|
||||
}
|
||||
gpu_twins.push_back(twin);
|
||||
}
|
||||
|
||||
// 3. Back the twins with a GPU buffer in one alloc call.
|
||||
ggml_backend_buffer_t gpu_buffer = ggml_backend_alloc_ctx_tensors(twin_ctx, gpu_backend_);
|
||||
if (gpu_buffer == nullptr) {
|
||||
LOG_ERROR("layer_registry: failed to allocate GPU buffer for '%s'",
|
||||
name.c_str());
|
||||
ggml_free(twin_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. H2D copy + sync.
|
||||
for (size_t i = 0; i < info.tensors.size(); ++i) {
|
||||
ggml_backend_tensor_copy(info.tensors[i], gpu_twins[i]);
|
||||
}
|
||||
ggml_backend_synchronize(gpu_backend_);
|
||||
|
||||
// 5. Swap buffer/data/extra so the originals now point at GPU memory.
|
||||
for (size_t i = 0; i < info.tensors.size(); ++i) {
|
||||
std::swap(info.tensors[i]->buffer, gpu_twins[i]->buffer);
|
||||
std::swap(info.tensors[i]->data, gpu_twins[i]->data);
|
||||
std::swap(info.tensors[i]->extra, gpu_twins[i]->extra);
|
||||
}
|
||||
|
||||
info.gpu_twins = std::move(gpu_twins);
|
||||
info.twin_ctx = twin_ctx;
|
||||
info.gpu_buffer = gpu_buffer;
|
||||
info.on_gpu = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayerRegistry::move_layer_to_cpu(const std::string& name) {
|
||||
auto it = layers_.find(name);
|
||||
if (it == layers_.end())
|
||||
return false;
|
||||
|
||||
LayerInfo& info = it->second;
|
||||
if (!info.on_gpu)
|
||||
return true;
|
||||
if (info.tensors.size() != info.gpu_twins.size()) {
|
||||
LOG_ERROR("layer_registry: twin/tensor count mismatch for '%s'",
|
||||
name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. Swap back: originals point at CPU memory again.
|
||||
for (size_t i = 0; i < info.tensors.size(); ++i) {
|
||||
if (info.gpu_twins[i] == nullptr)
|
||||
continue;
|
||||
std::swap(info.tensors[i]->buffer, info.gpu_twins[i]->buffer);
|
||||
std::swap(info.tensors[i]->data, info.gpu_twins[i]->data);
|
||||
std::swap(info.tensors[i]->extra, info.gpu_twins[i]->extra);
|
||||
}
|
||||
|
||||
// 2. Free the GPU buffer + twin context.
|
||||
if (info.gpu_buffer != nullptr) {
|
||||
ggml_backend_buffer_free(info.gpu_buffer);
|
||||
info.gpu_buffer = nullptr;
|
||||
}
|
||||
if (info.twin_ctx != nullptr) {
|
||||
ggml_free(info.twin_ctx);
|
||||
info.twin_ctx = nullptr;
|
||||
}
|
||||
info.gpu_twins.clear();
|
||||
info.on_gpu = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayerRegistry::is_layer_on_gpu(const std::string& name) const {
|
||||
auto it = layers_.find(name);
|
||||
return it != layers_.end() && it->second.on_gpu;
|
||||
}
|
||||
|
||||
size_t LayerRegistry::get_layer_size(const std::string& name) const {
|
||||
auto it = layers_.find(name);
|
||||
return it != layers_.end() ? it->second.bytes : 0;
|
||||
}
|
||||
|
||||
} // namespace sd::layer_registry
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef __SD_CORE_LAYER_REGISTRY_H__
|
||||
#define __SD_CORE_LAYER_REGISTRY_H__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ggml-backend.h"
|
||||
#include "ggml.h"
|
||||
|
||||
namespace sd::layer_registry {
|
||||
|
||||
struct LayerInfo {
|
||||
std::vector<ggml_tensor*> tensors;
|
||||
std::vector<ggml_tensor*> gpu_twins;
|
||||
ggml_context* twin_ctx = nullptr;
|
||||
ggml_backend_buffer_t gpu_buffer = nullptr;
|
||||
bool on_gpu = false;
|
||||
size_t bytes = 0;
|
||||
};
|
||||
|
||||
class LayerRegistry {
|
||||
public:
|
||||
LayerRegistry() = default;
|
||||
LayerRegistry(ggml_backend_t gpu_backend, ggml_backend_t cpu_backend)
|
||||
: gpu_backend_(gpu_backend), cpu_backend_(cpu_backend) {}
|
||||
|
||||
void set_backends(ggml_backend_t gpu_backend, ggml_backend_t cpu_backend) {
|
||||
gpu_backend_ = gpu_backend;
|
||||
cpu_backend_ = cpu_backend;
|
||||
}
|
||||
void register_layer(const std::string& name, ggml_tensor* tensor);
|
||||
bool move_layer_to_gpu(const std::string& name);
|
||||
bool move_layer_to_cpu(const std::string& name);
|
||||
bool is_layer_on_gpu(const std::string& name) const;
|
||||
size_t get_layer_size(const std::string& name) const;
|
||||
size_t get_layer_count() const { return layers_.size(); }
|
||||
|
||||
const std::map<std::string, LayerInfo>& layers() const { return layers_; }
|
||||
|
||||
private:
|
||||
ggml_backend_t gpu_backend_ = nullptr;
|
||||
ggml_backend_t cpu_backend_ = nullptr;
|
||||
std::map<std::string, LayerInfo> layers_;
|
||||
};
|
||||
|
||||
} // namespace sd::layer_registry
|
||||
|
||||
#endif // __SD_CORE_LAYER_REGISTRY_H__
|
||||
+7
-7
@@ -488,7 +488,7 @@ bool parse_strict_bool(const std::string& text, bool& value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string build_progress_bar(int step, int steps) {
|
||||
static std::string build_progress_bar(int step, int steps, char progress_char = '=', bool show_head = true) {
|
||||
std::string progress = " |";
|
||||
int max_progress = 50;
|
||||
int32_t current = 0;
|
||||
@@ -498,21 +498,21 @@ static std::string build_progress_bar(int step, int steps) {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
if (i > current) {
|
||||
progress += " ";
|
||||
} else if (i == current && i != max_progress - 1) {
|
||||
} else if (show_head && i == current && i != max_progress - 1) {
|
||||
progress += ">";
|
||||
} else {
|
||||
progress += "=";
|
||||
progress += progress_char;
|
||||
}
|
||||
}
|
||||
progress += "|";
|
||||
return progress;
|
||||
}
|
||||
|
||||
static void print_progress_line(int step, int steps, const std::string& speed_text) {
|
||||
static void print_progress_line(int step, int steps, const std::string& speed_text, char progress_char = '=', bool show_head = true) {
|
||||
if (step == 0) {
|
||||
return;
|
||||
}
|
||||
std::string progress = build_progress_bar(step, steps);
|
||||
std::string progress = build_progress_bar(step, steps, progress_char, show_head);
|
||||
const char* lf = (step == steps ? "\n" : "");
|
||||
printf("\r%s %i/%i - %s\033[K%s", progress.c_str(), step, steps, speed_text.c_str(), lf);
|
||||
fflush(stdout); // for linux
|
||||
@@ -552,9 +552,9 @@ void pretty_bytes_progress(int step, int steps, uint64_t bytes_processed, float
|
||||
|
||||
double speed_mb = bytes_per_second / (1024.0 * 1024.0);
|
||||
if (speed_mb >= 1024.0) {
|
||||
print_progress_line(step, steps, sd_format("%.2fGB/s", speed_mb / 1024.0));
|
||||
print_progress_line(step, steps, sd_format("%.2fGB/s", speed_mb / 1024.0), '#', false);
|
||||
} else {
|
||||
print_progress_line(step, steps, sd_format("%.2fMB/s", speed_mb));
|
||||
print_progress_line(step, steps, sd_format("%.2fMB/s", speed_mb), '#', false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "conditioning/conditioner.hpp"
|
||||
#include "core/ggml_extend_backend.h"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
#include "stable-diffusion.h"
|
||||
|
||||
struct GenerationExtensionInitContext {
|
||||
@@ -17,27 +19,19 @@ struct GenerationExtensionInitContext {
|
||||
SDVersion version;
|
||||
const String2TensorStorage& tensor_storage_map;
|
||||
ModelLoader& model_loader;
|
||||
std::shared_ptr<ModelManager> model_manager;
|
||||
int n_threads;
|
||||
std::function<bool(SDBackendModule)> ensure_backend_pair;
|
||||
std::function<ggml_backend_t(SDBackendModule)> backend_for;
|
||||
std::function<ggml_backend_t(SDBackendModule)> params_backend_for;
|
||||
};
|
||||
|
||||
struct GenerationExtensionTensorContext {
|
||||
std::map<std::string, ggml_tensor*>& tensors;
|
||||
std::map<std::string, ggml_tensor*>& mmap_able_tensors;
|
||||
std::function<bool(SDBackendModule)> module_can_mmap;
|
||||
};
|
||||
|
||||
struct GenerationExtensionConditionContext {
|
||||
Conditioner* conditioner;
|
||||
ConditionerParams& condition_params;
|
||||
const sd_pm_params_t& pm_params;
|
||||
std::map<std::string, ggml_tensor*>& tensors;
|
||||
SDVersion version;
|
||||
int n_threads;
|
||||
int total_steps;
|
||||
bool free_params_immediately;
|
||||
};
|
||||
|
||||
struct GenerationExtension {
|
||||
@@ -50,14 +44,10 @@ struct GenerationExtension {
|
||||
virtual bool init(const GenerationExtensionInitContext&) {
|
||||
return true;
|
||||
}
|
||||
virtual void collect_param_tensors(GenerationExtensionTensorContext&) {}
|
||||
virtual void get_param_tensors(std::map<std::string, ggml_tensor*>&) {}
|
||||
virtual void collect_loras(std::vector<ModelManager::LoraSpec>&) {}
|
||||
virtual void add_ignore_tensors(std::set<std::string>&) const {}
|
||||
virtual bool alloc_params_buffer() {
|
||||
return true;
|
||||
}
|
||||
virtual size_t get_params_buffer_size() const {
|
||||
return 0;
|
||||
}
|
||||
virtual void runner_done() {}
|
||||
virtual void reset_runtime_condition() {}
|
||||
virtual bool prepare_condition(GenerationExtensionConditionContext&) {
|
||||
return false;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "core/tensor_ggml.hpp"
|
||||
#include "core/util.h"
|
||||
#include "model/adapter/lora.hpp"
|
||||
#include "model/adapter/pmid.hpp"
|
||||
|
||||
static std::tuple<std::vector<int>, std::vector<float>, std::vector<bool>>
|
||||
@@ -103,7 +102,6 @@ static std::string remove_photomaker_trigger_from_prompt(FrozenCLIPEmbedderWithC
|
||||
|
||||
struct PhotoMakerExtension : public GenerationExtension {
|
||||
std::shared_ptr<PhotoMakerIDEncoder> pmid_model;
|
||||
std::shared_ptr<LoraModel> pmid_lora;
|
||||
bool enabled = false;
|
||||
std::string model_path;
|
||||
std::string trigger_word = "img";
|
||||
@@ -129,54 +127,45 @@ struct PhotoMakerExtension : public GenerationExtension {
|
||||
}
|
||||
|
||||
PMVersion pm_version = std::strstr(model_path.c_str(), "v2") != nullptr ? PM_VERSION_2 : PM_VERSION_1;
|
||||
pmid_model = std::make_shared<PhotoMakerIDEncoder>(ctx.backend_for(SDBackendModule::PHOTOMAKER),
|
||||
ctx.params_backend_for(SDBackendModule::PHOTOMAKER),
|
||||
ctx.tensor_storage_map,
|
||||
"pmid",
|
||||
ctx.version,
|
||||
pm_version);
|
||||
if (pm_version == PM_VERSION_2) {
|
||||
LOG_INFO("using PhotoMaker Version 2");
|
||||
}
|
||||
|
||||
pmid_lora = std::make_shared<LoraModel>("pmid",
|
||||
ctx.backend_for(SDBackendModule::PHOTOMAKER),
|
||||
ctx.params_backend_for(SDBackendModule::PHOTOMAKER),
|
||||
model_path,
|
||||
"",
|
||||
ctx.version);
|
||||
auto lora_tensor_filter = [&](const std::string& tensor_name) {
|
||||
return starts_with(tensor_name, "lora.model");
|
||||
};
|
||||
if (!pmid_lora->load_from_file(ctx.n_threads, lora_tensor_filter)) {
|
||||
LOG_WARN("load photomaker lora tensors from %s failed", model_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("loading stacked ID embedding (PHOTOMAKER) model file from '%s'", model_path.c_str());
|
||||
if (!ctx.model_loader.init_from_file_and_convert_name(model_path, "pmid.")) {
|
||||
LOG_WARN("loading stacked ID embedding from '%s' failed", model_path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
pmid_model = std::make_shared<PhotoMakerIDEncoder>(ctx.backend_for(SDBackendModule::PHOTOMAKER),
|
||||
ctx.tensor_storage_map,
|
||||
"pmid",
|
||||
ctx.version,
|
||||
pm_version,
|
||||
20.f,
|
||||
ctx.model_manager);
|
||||
if (pm_version == PM_VERSION_2) {
|
||||
LOG_INFO("using PhotoMaker Version 2");
|
||||
}
|
||||
|
||||
enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void collect_param_tensors(GenerationExtensionTensorContext& ctx) override {
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
if (!enabled || pmid_model == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> temp;
|
||||
pmid_model->get_param_tensors(temp, "pmid");
|
||||
bool do_mmap = ctx.module_can_mmap(SDBackendModule::PHOTOMAKER);
|
||||
for (const auto& [key, tensor] : temp) {
|
||||
ctx.tensors[key] = tensor;
|
||||
if (do_mmap) {
|
||||
ctx.mmap_able_tensors[key] = tensor;
|
||||
}
|
||||
pmid_model->get_param_tensors(tensors, "pmid");
|
||||
}
|
||||
|
||||
void collect_loras(std::vector<ModelManager::LoraSpec>& loras) override {
|
||||
if (!enabled || model_path.empty()) {
|
||||
return;
|
||||
}
|
||||
ModelManager::LoraSpec lora;
|
||||
lora.path = model_path;
|
||||
lora.multiplier = 1.0f;
|
||||
lora.tensor_name_prefix_filter = "lora.model";
|
||||
lora.required = true;
|
||||
loras.push_back(std::move(lora));
|
||||
}
|
||||
|
||||
void add_ignore_tensors(std::set<std::string>& ignore_tensors) const override {
|
||||
@@ -186,18 +175,10 @@ struct PhotoMakerExtension : public GenerationExtension {
|
||||
ignore_tensors.insert("pmid.unet.");
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!enabled || pmid_model == nullptr) {
|
||||
return true;
|
||||
void runner_done() override {
|
||||
if (pmid_model != nullptr) {
|
||||
pmid_model->runner_done();
|
||||
}
|
||||
return pmid_model->alloc_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() const override {
|
||||
if (!enabled || pmid_model == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return pmid_model->get_params_buffer_size();
|
||||
}
|
||||
|
||||
void reset_runtime_condition() override {
|
||||
@@ -207,21 +188,10 @@ struct PhotoMakerExtension : public GenerationExtension {
|
||||
|
||||
bool prepare_condition(GenerationExtensionConditionContext& ctx) override {
|
||||
reset_runtime_condition();
|
||||
if (!enabled || pmid_model == nullptr || pmid_lora == nullptr) {
|
||||
if (!enabled || pmid_model == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pmid_lora->applied) {
|
||||
int64_t t0 = ggml_time_ms();
|
||||
pmid_lora->apply(ctx.tensors, ctx.version, ctx.n_threads);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
pmid_lora->applied = true;
|
||||
LOG_INFO("pmid_lora apply completed, taking %.2fs", (t1 - t0) * 1.0f / 1000);
|
||||
if (ctx.free_params_immediately) {
|
||||
pmid_lora->free_params_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
bool pmv2 = pmid_model->get_version() == PM_VERSION_2;
|
||||
if (ctx.pm_params.id_images_count <= 0 || ctx.pm_params.id_images == nullptr) {
|
||||
LOG_WARN("Provided PhotoMaker model file, but NO input ID images");
|
||||
@@ -305,9 +275,6 @@ struct PhotoMakerExtension : public GenerationExtension {
|
||||
LOG_INFO("Photomaker ID Stacking, taking %" PRId64 " ms", t1 - t0);
|
||||
LOG_INFO("PHOTOMAKER: start_merge_step: %d", start_merge_step);
|
||||
|
||||
if (ctx.free_params_immediately) {
|
||||
pmid_model->free_params_buffer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ enum SDVersion {
|
||||
VERSION_LONGCAT,
|
||||
VERSION_PID,
|
||||
VERSION_IDEOGRAM4,
|
||||
VERSION_ESRGAN,
|
||||
VERSION_COUNT,
|
||||
};
|
||||
|
||||
|
||||
+81
-25
@@ -4,6 +4,7 @@
|
||||
#include <mutex>
|
||||
#include "core/ggml_extend.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
|
||||
#define LORA_GRAPH_BASE_SIZE 10240
|
||||
|
||||
@@ -14,22 +15,24 @@ struct LoraModel : public GGMLRunner {
|
||||
std::map<ggml_tensor*, ggml_tensor*> original_tensor_to_final_tensor;
|
||||
std::set<std::string> applied_lora_tensors;
|
||||
std::string file_path;
|
||||
ModelLoader model_loader;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
bool tensor_preprocessed = false;
|
||||
std::shared_ptr<ModelManager> model_manager;
|
||||
ggml_backend_t params_backend = nullptr;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
bool tensor_preprocessed = false;
|
||||
|
||||
typedef std::function<bool(const std::string&)> filter_t;
|
||||
|
||||
LoraModel(const std::string& lora_id,
|
||||
ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const std::string& file_path = "",
|
||||
std::string prefix = "",
|
||||
SDVersion version = VERSION_COUNT)
|
||||
: lora_id(lora_id), file_path(file_path), GGMLRunner(backend, params_backend) {
|
||||
ggml_backend_t params_backend_,
|
||||
const std::string& file_path = "",
|
||||
std::string prefix = "",
|
||||
SDVersion version = VERSION_COUNT,
|
||||
std::shared_ptr<ModelManager> manager = std::make_shared<ModelManager>())
|
||||
: GGMLRunner(backend, manager), lora_id(lora_id), file_path(file_path), model_manager(std::move(manager)), params_backend(params_backend_) {
|
||||
prefix = "lora." + prefix;
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, prefix, version)) {
|
||||
if (model_manager == nullptr || !model_manager->loader().init_from_file_and_convert_name(file_path, prefix, version)) {
|
||||
load_failed = true;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +74,11 @@ struct LoraModel : public GGMLRunner {
|
||||
return true;
|
||||
};
|
||||
|
||||
model_loader.load_tensors(on_new_tensor_cb, n_threads);
|
||||
if (model_manager != nullptr) {
|
||||
model_manager->set_n_threads(n_threads);
|
||||
}
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
model_loader.load_tensors(on_new_tensor_cb);
|
||||
|
||||
if (tensors_to_create.empty()) {
|
||||
return true;
|
||||
@@ -87,25 +94,64 @@ struct LoraModel : public GGMLRunner {
|
||||
lora_tensors[name] = real;
|
||||
}
|
||||
|
||||
if (!alloc_params_buffer()) {
|
||||
LOG_ERROR("lora model buffer allocation failed");
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
for (const auto& pair : lora_tensors) {
|
||||
tensors[pair.first] = pair.second;
|
||||
}
|
||||
if (model_manager == nullptr ||
|
||||
!model_manager->register_param_tensors("LoRA",
|
||||
std::move(tensors),
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
runtime_backend,
|
||||
params_backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("lora model manager registration failed");
|
||||
return false;
|
||||
}
|
||||
std::vector<ggml_tensor*> lora_params;
|
||||
lora_params.reserve(lora_tensors.size());
|
||||
for (const auto& pair : lora_tensors) {
|
||||
lora_params.push_back(pair.second);
|
||||
}
|
||||
if (!model_manager->prepare_params(lora_params)) {
|
||||
LOG_ERROR("lora model manager prepare params failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
dry_run = false;
|
||||
model_loader.load_tensors(on_new_tensor_cb, n_threads);
|
||||
|
||||
LOG_DEBUG("finished loaded lora");
|
||||
return true;
|
||||
}
|
||||
|
||||
void preprocess_lora_tensors(const std::map<std::string, ggml_tensor*>& model_tensors) {
|
||||
void release_loaded_tensors() {
|
||||
runner_done();
|
||||
free_compute_buffer();
|
||||
model_manager.reset();
|
||||
free_params_ctx();
|
||||
alloc_params_ctx();
|
||||
model_manager = std::make_shared<ModelManager>();
|
||||
weight_manager = model_manager;
|
||||
lora_tensors.clear();
|
||||
original_tensor_to_final_tensor.clear();
|
||||
applied_lora_tensors.clear();
|
||||
applied = false;
|
||||
tensor_preprocessed = false;
|
||||
}
|
||||
|
||||
static std::set<std::string> tensor_names(const std::map<std::string, ggml_tensor*>& model_tensors) {
|
||||
std::set<std::string> names;
|
||||
for (const auto& item : model_tensors) {
|
||||
names.insert(item.first);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
void preprocess_lora_tensors(const std::set<std::string>& model_tensor_names) {
|
||||
if (tensor_preprocessed) {
|
||||
return;
|
||||
}
|
||||
tensor_preprocessed = true;
|
||||
// I really hate these hardcoded processes.
|
||||
if (model_tensors.find("cond_stage_model.1.transformer.text_model.encoder.layers.0.self_attn.in_proj.weight") != model_tensors.end()) {
|
||||
if (model_tensor_names.find("cond_stage_model.1.transformer.text_model.encoder.layers.0.self_attn.in_proj.weight") != model_tensor_names.end()) {
|
||||
std::unordered_map<std::string, ggml_tensor*> new_lora_tensors;
|
||||
for (auto& [old_name, tensor] : lora_tensors) {
|
||||
std::string new_name = old_name;
|
||||
@@ -612,7 +658,7 @@ struct LoraModel : public GGMLRunner {
|
||||
if (lokr_w2)
|
||||
applied_lora_tensors.insert(lokr_w2_name);
|
||||
if (lokr_w2_a)
|
||||
applied_lora_tensors.insert(lokr_w2_name);
|
||||
applied_lora_tensors.insert(lokr_w2_a_name);
|
||||
if (lokr_w2_b)
|
||||
applied_lora_tensors.insert(lokr_w2_b_name);
|
||||
applied_lora_tensors.insert(alpha_name);
|
||||
@@ -753,11 +799,13 @@ struct LoraModel : public GGMLRunner {
|
||||
return out_diff;
|
||||
}
|
||||
|
||||
ggml_cgraph* build_lora_graph(const std::map<std::string, ggml_tensor*>& model_tensors, SDVersion version) {
|
||||
ggml_cgraph* build_lora_graph(const std::map<std::string, ggml_tensor*>& model_tensors,
|
||||
const std::set<std::string>& model_tensor_names,
|
||||
SDVersion version) {
|
||||
size_t lora_graph_size = LORA_GRAPH_BASE_SIZE + lora_tensors.size() * 10;
|
||||
ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, lora_graph_size, false);
|
||||
|
||||
preprocess_lora_tensors(model_tensors);
|
||||
preprocess_lora_tensors(model_tensor_names);
|
||||
|
||||
original_tensor_to_final_tensor.clear();
|
||||
applied_lora_tensors.clear();
|
||||
@@ -794,12 +842,16 @@ struct LoraModel : public GGMLRunner {
|
||||
return gf;
|
||||
}
|
||||
|
||||
void apply(std::map<std::string, ggml_tensor*> model_tensors, SDVersion version, int n_threads) {
|
||||
void apply(std::map<std::string, ggml_tensor*> model_tensors,
|
||||
const std::set<std::string>& model_tensor_names,
|
||||
SDVersion version,
|
||||
int n_threads,
|
||||
bool warn_unused = true) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_lora_graph(model_tensors, version);
|
||||
return build_lora_graph(model_tensors, model_tensor_names, version);
|
||||
};
|
||||
GGMLRunner::compute<float>(get_graph, n_threads, false, true);
|
||||
stat();
|
||||
GGMLRunner::compute<float>(get_graph, n_threads, false, false, false, true);
|
||||
stat(!warn_unused);
|
||||
for (auto item : original_tensor_to_final_tensor) {
|
||||
ggml_tensor* original_tensor = item.first;
|
||||
ggml_tensor* final_tensor = item.second;
|
||||
@@ -810,6 +862,10 @@ struct LoraModel : public GGMLRunner {
|
||||
GGMLRunner::free_compute_buffer();
|
||||
}
|
||||
|
||||
void apply(std::map<std::string, ggml_tensor*> model_tensors, SDVersion version, int n_threads, bool warn_unused = true) {
|
||||
apply(model_tensors, tensor_names(model_tensors), version, n_threads, warn_unused);
|
||||
}
|
||||
|
||||
void stat(bool at_runntime = false) {
|
||||
size_t total_lora_tensors_count = 0;
|
||||
size_t applied_lora_tensors_count = 0;
|
||||
|
||||
+35
-21
@@ -413,13 +413,13 @@ public:
|
||||
|
||||
public:
|
||||
PhotoMakerIDEncoder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
SDVersion version = VERSION_SDXL,
|
||||
PMVersion pm_v = PM_VERSION_1,
|
||||
float sty = 20.f)
|
||||
: GGMLRunner(backend, params_backend),
|
||||
SDVersion version = VERSION_SDXL,
|
||||
PMVersion pm_v = PM_VERSION_1,
|
||||
float sty = 20.f,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
version(version),
|
||||
pm_version(pm_v),
|
||||
style_strength(sty) {
|
||||
@@ -558,24 +558,25 @@ public:
|
||||
return build_graph(id_pixel_values, prompt_embeds, class_tokens_mask, id_embeds);
|
||||
};
|
||||
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true));
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true, true, true));
|
||||
}
|
||||
};
|
||||
|
||||
struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
std::string file_path;
|
||||
ModelLoader* model_loader;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
std::shared_ptr<ModelManager> model_manager;
|
||||
ggml_backend_t params_backend = nullptr;
|
||||
bool load_failed = false;
|
||||
bool applied = false;
|
||||
|
||||
PhotoMakerIDEmbed(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
ModelLoader* ml,
|
||||
const std::string& file_path = "",
|
||||
const std::string& prefix = "")
|
||||
: file_path(file_path), GGMLRunner(backend, params_backend), model_loader(ml) {
|
||||
if (!model_loader->init_from_file_and_convert_name(file_path, prefix)) {
|
||||
ggml_backend_t params_backend_,
|
||||
std::shared_ptr<ModelManager> manager = std::make_shared<ModelManager>(),
|
||||
const std::string& file_path = "",
|
||||
const std::string& prefix = "")
|
||||
: GGMLRunner(backend, manager), file_path(file_path), model_manager(std::move(manager)), params_backend(params_backend_) {
|
||||
if (model_manager == nullptr || !model_manager->loader().init_from_file_and_convert_name(file_path, prefix)) {
|
||||
load_failed = true;
|
||||
}
|
||||
}
|
||||
@@ -616,14 +617,27 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
return true;
|
||||
};
|
||||
|
||||
model_loader->load_tensors(on_new_tensor_cb, n_threads);
|
||||
if (!alloc_params_buffer()) {
|
||||
LOG_ERROR("PhotoMaker ID embeds buffer allocation failed");
|
||||
model_manager->set_n_threads(n_threads);
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
model_loader.load_tensors(on_new_tensor_cb);
|
||||
if (!model_manager->register_param_tensors("PhotoMaker ID embeds",
|
||||
tensors,
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
runtime_backend,
|
||||
params_backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("PhotoMaker ID embeds model manager registration failed");
|
||||
return false;
|
||||
}
|
||||
std::vector<ggml_tensor*> id_embed_params;
|
||||
id_embed_params.reserve(tensors.size());
|
||||
for (const auto& pair : tensors) {
|
||||
id_embed_params.push_back(pair.second);
|
||||
}
|
||||
if (!model_manager->prepare_params(id_embed_params)) {
|
||||
LOG_ERROR("PhotoMaker ID embeds model manager prepare params failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
dry_run = false;
|
||||
model_loader->load_tensors(on_new_tensor_cb, n_threads);
|
||||
|
||||
LOG_DEBUG("finished loading PhotoMaker ID Embeds ");
|
||||
return true;
|
||||
|
||||
@@ -560,11 +560,11 @@ protected:
|
||||
params["mix_factor"] = ggml_new_tensor_1d(ctx, wtype, 1);
|
||||
}
|
||||
|
||||
float get_alpha() {
|
||||
ggml_tensor* get_alpha(GGMLRunnerContext* ctx) {
|
||||
// image_only_indicator is always tensor([0.]) and since mix_factor.shape is [1,]
|
||||
// so learned_with_images is same as learned
|
||||
float alpha = ggml_ext_backend_tensor_get_f32(params["mix_factor"]);
|
||||
return sigmoid(alpha);
|
||||
auto mix_factor = ggml_ext_cast_f32(ctx->ggml_ctx, ctx->backend, params["mix_factor"]);
|
||||
return ggml_sigmoid(ctx->ggml_ctx, mix_factor);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -578,11 +578,12 @@ public:
|
||||
ggml_tensor* x_spatial,
|
||||
ggml_tensor* x_temporal) {
|
||||
// image_only_indicator is always tensor([0.])
|
||||
float alpha = get_alpha();
|
||||
auto x = ggml_add(ctx->ggml_ctx,
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_spatial, alpha),
|
||||
ggml_ext_scale(ctx->ggml_ctx, x_temporal, 1.0f - alpha));
|
||||
return x;
|
||||
auto alpha = get_alpha(ctx);
|
||||
return ggml_add(ctx->ggml_ctx,
|
||||
x_temporal,
|
||||
ggml_mul(ctx->ggml_ctx,
|
||||
ggml_sub(ctx->ggml_ctx, x_spatial, x_temporal),
|
||||
alpha));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -253,7 +253,8 @@ namespace Rope {
|
||||
int bs,
|
||||
float theta,
|
||||
int head_dim,
|
||||
const std::vector<int>& mrope_section) {
|
||||
const std::vector<int>& mrope_section,
|
||||
const std::vector<std::vector<int>>& axis_wrap_dims = {}) {
|
||||
GGML_ASSERT(bs > 0);
|
||||
GGML_ASSERT(head_dim % 2 == 0);
|
||||
GGML_ASSERT(mrope_section.size() >= 3);
|
||||
@@ -265,7 +266,11 @@ namespace Rope {
|
||||
std::vector<std::vector<std::vector<float>>> axis_embs;
|
||||
axis_embs.reserve(3);
|
||||
for (int axis = 0; axis < 3; ++axis) {
|
||||
axis_embs.push_back(rope(trans_ids[axis], head_dim, theta));
|
||||
std::vector<int> axis_wrap;
|
||||
if (axis < static_cast<int>(axis_wrap_dims.size())) {
|
||||
axis_wrap = axis_wrap_dims[axis];
|
||||
}
|
||||
axis_embs.push_back(rope(trans_ids[axis], head_dim, theta, axis_wrap));
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> emb = axis_embs[0];
|
||||
|
||||
@@ -561,10 +561,10 @@ namespace Anima {
|
||||
AnimaNet net;
|
||||
|
||||
AnimaRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "model.diffusion_model")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(AnimaConfig::detect_from_weights(tensor_storage_map, prefix + ".net")) {
|
||||
net = AnimaNet(config);
|
||||
net.init(params_ctx, tensor_storage_map, prefix + ".net");
|
||||
@@ -697,7 +697,7 @@ namespace Anima {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, t5_ids, t5_weights);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#ifndef __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
||||
#ifndef __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
||||
#define __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
||||
|
||||
#include "model/common/block.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
|
||||
#define CONTROL_NET_GRAPH_SIZE 1536
|
||||
|
||||
@@ -309,73 +310,47 @@ public:
|
||||
struct ControlNet : public GGMLRunner {
|
||||
SDVersion version = VERSION_SD1;
|
||||
ControlNetBlock control_net;
|
||||
std::string weight_prefix;
|
||||
|
||||
ggml_backend_buffer_t control_buffer = nullptr;
|
||||
ggml_context* control_ctx = nullptr;
|
||||
std::vector<ggml_tensor*> control_outputs_ggml;
|
||||
ggml_tensor* guided_hint_output_ggml = nullptr;
|
||||
std::vector<sd::Tensor<float>> controls;
|
||||
sd::Tensor<float> guided_hint;
|
||||
bool guided_hint_cached = false;
|
||||
std::shared_ptr<ModelManager> owned_model_manager;
|
||||
ggml_backend_t params_backend = nullptr;
|
||||
|
||||
static const char* guided_hint_cache_name() {
|
||||
return "controlnet.guided_hint";
|
||||
}
|
||||
|
||||
ControlNet(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
SDVersion version = VERSION_SD1)
|
||||
: GGMLRunner(backend, params_backend), control_net(version) {
|
||||
control_net.init(params_ctx, tensor_storage_map, "");
|
||||
ggml_backend_t params_backend_,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
SDVersion version = VERSION_SD1,
|
||||
const std::string& prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager), version(version), control_net(version), weight_prefix(prefix), params_backend(params_backend_) {
|
||||
control_net.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
~ControlNet() override {
|
||||
free_control_ctx();
|
||||
}
|
||||
|
||||
void alloc_control_ctx(std::vector<ggml_tensor*> outs) {
|
||||
ggml_init_params params;
|
||||
params.mem_size = static_cast<size_t>(outs.size() * ggml_tensor_overhead()) + 1024 * 1024;
|
||||
params.mem_buffer = nullptr;
|
||||
params.no_alloc = true;
|
||||
control_ctx = ggml_init(params);
|
||||
|
||||
control_outputs_ggml.resize(outs.size() - 1);
|
||||
|
||||
size_t control_buffer_size = 0;
|
||||
|
||||
guided_hint_output_ggml = ggml_dup_tensor(control_ctx, outs[0]);
|
||||
control_buffer_size += ggml_nbytes(guided_hint_output_ggml);
|
||||
|
||||
for (int i = 0; i < outs.size() - 1; i++) {
|
||||
control_outputs_ggml[i] = ggml_dup_tensor(control_ctx, outs[i + 1]);
|
||||
control_buffer_size += ggml_nbytes(control_outputs_ggml[i]);
|
||||
}
|
||||
|
||||
control_buffer = ggml_backend_alloc_ctx_tensors(control_ctx, runtime_backend);
|
||||
|
||||
LOG_DEBUG("control buffer size %.2fMB", control_buffer_size * 1.f / 1024.f / 1024.f);
|
||||
}
|
||||
|
||||
void free_control_ctx() {
|
||||
if (control_buffer != nullptr) {
|
||||
ggml_backend_buffer_free(control_buffer);
|
||||
control_buffer = nullptr;
|
||||
}
|
||||
if (control_ctx != nullptr) {
|
||||
ggml_free(control_ctx);
|
||||
control_ctx = nullptr;
|
||||
}
|
||||
guided_hint_output_ggml = nullptr;
|
||||
guided_hint_cached = false;
|
||||
guided_hint = {};
|
||||
control_outputs_ggml.clear();
|
||||
controls.clear();
|
||||
free_cache_ctx_and_buffer();
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "control_net";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) {
|
||||
control_net.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
||||
control_net.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
||||
@@ -391,11 +366,17 @@ struct ControlNet : public GGMLRunner {
|
||||
ggml_tensor* context = make_optional_input(context_tensor);
|
||||
ggml_tensor* y = make_optional_input(y_tensor);
|
||||
|
||||
guided_hint_output_ggml = nullptr;
|
||||
control_outputs_ggml.clear();
|
||||
|
||||
ggml_tensor* guided_hint_input = nullptr;
|
||||
if (guided_hint_cached && !guided_hint.empty()) {
|
||||
guided_hint_input = make_input(guided_hint);
|
||||
hint = nullptr;
|
||||
} else {
|
||||
if (guided_hint_cached) {
|
||||
guided_hint_input = get_cache_tensor_by_name(guided_hint_cache_name());
|
||||
if (guided_hint_input == nullptr) {
|
||||
guided_hint_cached = false;
|
||||
}
|
||||
}
|
||||
if (guided_hint_input == nullptr) {
|
||||
hint = make_input(hint_tensor);
|
||||
}
|
||||
|
||||
@@ -409,13 +390,19 @@ struct ControlNet : public GGMLRunner {
|
||||
context,
|
||||
y);
|
||||
|
||||
if (control_ctx == nullptr) {
|
||||
alloc_control_ctx(outs);
|
||||
if (guided_hint_input == nullptr && !outs.empty()) {
|
||||
guided_hint_output_ggml = outs[0];
|
||||
ggml_set_output(guided_hint_output_ggml);
|
||||
cache(guided_hint_cache_name(), guided_hint_output_ggml);
|
||||
ggml_build_forward_expand(gf, guided_hint_output_ggml);
|
||||
}
|
||||
|
||||
ggml_build_forward_expand(gf, ggml_cpy(compute_ctx, outs[0], guided_hint_output_ggml));
|
||||
for (int i = 0; i < outs.size() - 1; i++) {
|
||||
ggml_build_forward_expand(gf, ggml_cpy(compute_ctx, outs[i + 1], control_outputs_ggml[i]));
|
||||
control_outputs_ggml.reserve(outs.size() > 0 ? outs.size() - 1 : 0);
|
||||
for (size_t i = 1; i < outs.size(); i++) {
|
||||
ggml_tensor* control_output = outs[i];
|
||||
ggml_set_output(control_output);
|
||||
ggml_build_forward_expand(gf, control_output);
|
||||
control_outputs_ggml.push_back(control_output);
|
||||
}
|
||||
|
||||
return gf;
|
||||
@@ -435,15 +422,12 @@ struct ControlNet : public GGMLRunner {
|
||||
return build_graph(x, hint, timesteps, context, y);
|
||||
};
|
||||
|
||||
auto compute_result = GGMLRunner::compute<float>(get_graph, n_threads, false);
|
||||
auto compute_result = GGMLRunner::compute<float>(get_graph, n_threads, false, false, false, true);
|
||||
if (!compute_result.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (guided_hint_output_ggml != nullptr) {
|
||||
guided_hint = restore_trailing_singleton_dims(sd::make_sd_tensor_from_ggml<float>(guided_hint_output_ggml),
|
||||
4);
|
||||
}
|
||||
guided_hint_cached = get_cache_tensor_by_name(guided_hint_cache_name()) != nullptr;
|
||||
controls.clear();
|
||||
controls.reserve(control_outputs_ggml.size());
|
||||
for (ggml_tensor* control : control_outputs_ggml) {
|
||||
@@ -451,36 +435,40 @@ struct ControlNet : public GGMLRunner {
|
||||
GGML_ASSERT(!control_host.empty());
|
||||
controls.push_back(std::move(control_host));
|
||||
}
|
||||
guided_hint_cached = true;
|
||||
return controls;
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading control net from '%s'", file_path.c_str());
|
||||
if (!alloc_params_buffer()) {
|
||||
LOG_ERROR("control net model buffer allocation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
control_net.get_param_tensors(tensors);
|
||||
std::set<std::string> ignore_tensors;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto manager = std::dynamic_pointer_cast<ModelManager>(weight_manager.lock());
|
||||
if (manager == nullptr) {
|
||||
owned_model_manager = std::make_shared<ModelManager>();
|
||||
weight_manager = owned_model_manager;
|
||||
manager = owned_model_manager;
|
||||
}
|
||||
|
||||
ModelLoader& model_loader = manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path)) {
|
||||
LOG_ERROR("init control net model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load control net tensors from model loader failed");
|
||||
manager->set_n_threads(n_threads);
|
||||
if (!manager->register_param_tensors("ControlNet",
|
||||
std::move(tensors),
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
runtime_backend,
|
||||
params_backend) ||
|
||||
!manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register control net tensors with model manager failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("control net model loaded");
|
||||
return success;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -387,10 +387,10 @@ namespace ErnieImage {
|
||||
std::vector<float> pe_vec;
|
||||
|
||||
ErnieImageRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(ErnieImageConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
ernie_image = ErnieImageModel(config);
|
||||
ernie_image.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -440,7 +440,7 @@ namespace ErnieImage {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -1301,12 +1301,12 @@ namespace Flux {
|
||||
bool use_mask = false;
|
||||
|
||||
FluxRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool use_mask = false)
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool use_mask = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(FluxConfig::detect_from_weights(tensor_storage_map, prefix, version)),
|
||||
version(version),
|
||||
use_mask(use_mask) {
|
||||
@@ -1500,7 +1500,7 @@ namespace Flux {
|
||||
return build_graph(x, timesteps, context, c_concat, y, guidance, ref_latents, increase_ref_index, skip_layers);
|
||||
};
|
||||
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1583,7 +1583,8 @@ namespace Flux {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_COUNT;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -1599,24 +1600,20 @@ namespace Flux {
|
||||
}
|
||||
|
||||
std::shared_ptr<FluxRunner> flux = std::make_shared<FluxRunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model",
|
||||
VERSION_FLUX2,
|
||||
false);
|
||||
false,
|
||||
model_manager);
|
||||
|
||||
if (!flux->alloc_params_buffer()) {
|
||||
LOG_ERROR("flux model allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
flux->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("Flux test",
|
||||
*flux,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register flux tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
||||
#ifndef __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
||||
#define __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
@@ -282,10 +282,10 @@ namespace HiDreamO1 {
|
||||
std::array<std::vector<float>, 4> pos_embed_weight_data_;
|
||||
|
||||
HiDreamO1VisionRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model.visual")
|
||||
: GGMLRunner(backend, params_backend),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model.visual",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
config(HiDreamO1Config::detect_from_weights(tensor_storage_map, prefix)),
|
||||
model(std::make_shared<LLM::VisionModel>(false, config.llm.vision)) {
|
||||
model->init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -323,11 +323,15 @@ namespace HiDreamO1 {
|
||||
return gf;
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads, const sd::Tensor<float>& image) {
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
const sd::Tensor<float>& image,
|
||||
bool auto_free = true,
|
||||
bool free_compute_buffer = true,
|
||||
bool free_compute_params = true) {
|
||||
auto get_graph = [&]() {
|
||||
return build_graph(image);
|
||||
};
|
||||
auto output = GGMLRunner::compute<float>(get_graph, n_threads, false);
|
||||
auto output = GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params);
|
||||
return output.has_value() ? std::move(output.value()) : sd::Tensor<float>();
|
||||
}
|
||||
};
|
||||
@@ -339,10 +343,10 @@ namespace HiDreamO1 {
|
||||
std::vector<float> attention_mask_vec;
|
||||
|
||||
HiDreamO1Runner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(HiDreamO1Config::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
model = HiDreamO1Model(config);
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -455,7 +459,7 @@ namespace HiDreamO1 {
|
||||
auto get_graph = [&]() {
|
||||
return build_graph(x, timestep, input_ids, input_pos, token_types, vinput_mask, image_embeds, ref_images);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
@@ -486,29 +490,14 @@ namespace HiDreamO1 {
|
||||
std::shared_ptr<HiDreamO1VisionRunner> vision_runner;
|
||||
|
||||
HiDreamO1Conditioner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {})
|
||||
: vision_runner(std::make_shared<HiDreamO1VisionRunner>(backend, params_backend, tensor_storage_map)) {}
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: vision_runner(std::make_shared<HiDreamO1VisionRunner>(backend, tensor_storage_map, "model.visual", weight_manager)) {}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
vision_runner->get_param_tensors(tensors);
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() override {
|
||||
if (!vision_runner->alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_params_buffer() override {
|
||||
vision_runner->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() override {
|
||||
return vision_runner->get_params_buffer_size();
|
||||
}
|
||||
|
||||
void set_max_graph_vram_bytes(size_t max_graph_vram_bytes) override {
|
||||
vision_runner->set_max_graph_vram_bytes(max_graph_vram_bytes);
|
||||
}
|
||||
@@ -521,6 +510,10 @@ namespace HiDreamO1 {
|
||||
vision_runner->set_weight_adapter(adapter);
|
||||
}
|
||||
|
||||
void runner_done() override {
|
||||
vision_runner->runner_done();
|
||||
}
|
||||
|
||||
SDCondition get_learned_condition(int n_threads,
|
||||
const ConditionerParams& conditioner_params) override {
|
||||
SDCondition result;
|
||||
@@ -666,7 +659,7 @@ namespace HiDreamO1 {
|
||||
result.c_vinput_mask = sd::Tensor<int32_t>(vinput_mask_shape, std::move(vinput_mask));
|
||||
result.c_image_embeds.reserve(vlm_images.size());
|
||||
for (const auto& vlm_image : vlm_images) {
|
||||
auto image_embed = vision_runner->compute(n_threads, vlm_image.second);
|
||||
auto image_embed = vision_runner->compute(n_threads, vlm_image.second, false, true, true);
|
||||
if (image_embed.empty()) {
|
||||
LOG_ERROR("hidream_o1 conditioner: encode VLM image failed");
|
||||
return SDCondition();
|
||||
|
||||
@@ -151,7 +151,9 @@ namespace Ideogram4 {
|
||||
int context_len,
|
||||
int head_dim,
|
||||
int rope_theta,
|
||||
const std::vector<int>& mrope_section) {
|
||||
const std::vector<int>& mrope_section,
|
||||
bool circular_x = false,
|
||||
bool circular_y = false) {
|
||||
GGML_ASSERT(bs == 1);
|
||||
std::vector<std::vector<float>> ids(static_cast<size_t>(bs) * (context_len + grid_h * grid_w),
|
||||
std::vector<float>(3, 0.f));
|
||||
@@ -169,7 +171,29 @@ namespace Ideogram4 {
|
||||
}
|
||||
}
|
||||
|
||||
return Rope::embed_interleaved_mrope(ids, bs, static_cast<float>(rope_theta), head_dim, mrope_section);
|
||||
std::vector<std::vector<int>> axis_wrap_dims(3);
|
||||
if (circular_y || circular_x) {
|
||||
size_t total_len = static_cast<size_t>(bs) * (context_len + grid_h * grid_w);
|
||||
axis_wrap_dims[1].assign(total_len, 0);
|
||||
axis_wrap_dims[2].assign(total_len, 0);
|
||||
if (circular_y) {
|
||||
for (size_t idx = static_cast<size_t>(context_len); idx < total_len; ++idx) {
|
||||
axis_wrap_dims[1][idx] = grid_h;
|
||||
}
|
||||
}
|
||||
if (circular_x) {
|
||||
for (size_t idx = static_cast<size_t>(context_len); idx < total_len; ++idx) {
|
||||
axis_wrap_dims[2][idx] = grid_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Rope::embed_interleaved_mrope(ids,
|
||||
bs,
|
||||
static_cast<float>(rope_theta),
|
||||
head_dim,
|
||||
mrope_section,
|
||||
axis_wrap_dims);
|
||||
}
|
||||
|
||||
class Ideogram4Attention : public GGMLBlock {
|
||||
@@ -425,10 +449,10 @@ namespace Ideogram4 {
|
||||
std::vector<int32_t> image_indicator_vec;
|
||||
|
||||
Ideogram4Runner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(Ideogram4Config::detect_from_weights(tensor_storage_map, prefix)),
|
||||
uncond_prefix(prefix + ".uncond") {
|
||||
model = Ideogram4Transformer(config);
|
||||
@@ -480,14 +504,17 @@ namespace Ideogram4 {
|
||||
int64_t pos_len = context_len + grid_h * grid_w;
|
||||
int64_t head_dim = config.emb_dim / config.num_heads;
|
||||
|
||||
pe_vec = gen_ideogram4_pe(static_cast<int>(grid_h),
|
||||
static_cast<int>(grid_w),
|
||||
static_cast<int>(x->ne[3]),
|
||||
static_cast<int>(context_len),
|
||||
static_cast<int>(head_dim),
|
||||
static_cast<int>(config.rope_theta),
|
||||
config.mrope_section);
|
||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, head_dim / 2, pos_len);
|
||||
auto runner_ctx = get_context();
|
||||
pe_vec = gen_ideogram4_pe(static_cast<int>(grid_h),
|
||||
static_cast<int>(grid_w),
|
||||
static_cast<int>(x->ne[3]),
|
||||
static_cast<int>(context_len),
|
||||
static_cast<int>(head_dim),
|
||||
static_cast<int>(config.rope_theta),
|
||||
config.mrope_section,
|
||||
runner_ctx.circular_x_enabled,
|
||||
runner_ctx.circular_y_enabled);
|
||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, head_dim / 2, pos_len);
|
||||
set_backend_tensor_data(pe, pe_vec.data());
|
||||
|
||||
image_indicator_vec.assign(static_cast<size_t>(pos_len), 1);
|
||||
@@ -497,7 +524,6 @@ namespace Ideogram4 {
|
||||
auto indicator = ggml_new_tensor_2d(compute_ctx, GGML_TYPE_I32, pos_len, x->ne[3]);
|
||||
set_backend_tensor_data(indicator, image_indicator_vec.data());
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
ggml_tensor* out = active_model.forward(&runner_ctx, x, timesteps, context, pe, indicator);
|
||||
ggml_build_forward_expand(gf, out);
|
||||
return gf;
|
||||
@@ -511,7 +537,7 @@ namespace Ideogram4 {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, use_uncond_model);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -356,10 +356,10 @@ namespace Lens {
|
||||
std::vector<float> pe_vec;
|
||||
|
||||
LensRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(LensConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
lens = LensModel(config);
|
||||
lens.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -408,7 +408,7 @@ namespace Lens {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -1686,10 +1686,10 @@ namespace LTXV {
|
||||
sd::Tensor<float> ax_input_cache;
|
||||
|
||||
LTXAVRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model.diffusion_model")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "model.diffusion_model",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(LTXAVConfig::detect_from_weights(tensor_storage_map, prefix)),
|
||||
model(config) {
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -1939,7 +1939,7 @@ namespace LTXV {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, audio_x, audio_timesteps, audio_length, frame_rate, video_positions);
|
||||
};
|
||||
auto out = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
auto out = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -2025,7 +2025,8 @@ namespace LTXV {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
LOG_INFO("loading ltxav from '%s'", model_path.c_str());
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(model_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", model_path.c_str());
|
||||
return;
|
||||
@@ -2040,19 +2041,18 @@ namespace LTXV {
|
||||
|
||||
auto& tensor_storage_map = model_loader.get_tensor_storage_map();
|
||||
std::shared_ptr<LTXAVRunner> ltxav = std::make_shared<LTXAVRunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model");
|
||||
"model.diffusion_model",
|
||||
model_manager);
|
||||
|
||||
if (!ltxav->alloc_params_buffer()) {
|
||||
LOG_ERROR("ltxav buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
ltxav->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
if (!model_loader.load_tensors(tensors)) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("LTXAV test",
|
||||
*ltxav,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register ltxav tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -879,10 +879,10 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
||||
MMDiT mmdit;
|
||||
|
||||
MMDiTRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(MMDiTConfig::detect_from_weights(tensor_storage_map, prefix)),
|
||||
mmdit(config) {
|
||||
mmdit.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -935,7 +935,7 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
||||
return build_graph(x, timesteps, context, y, skip_layers);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
@@ -1001,28 +1001,25 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
std::shared_ptr<MMDiTRunner> mmdit = std::make_shared<MMDiTRunner>(backend, backend);
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
std::shared_ptr<MMDiTRunner> mmdit = std::make_shared<MMDiTRunner>(backend, String2TensorStorage{}, "", model_manager);
|
||||
{
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
|
||||
if (!mmdit->alloc_params_buffer()) {
|
||||
LOG_ERROR("mmdit embeds buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
mmdit->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
ModelLoader model_loader;
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("MMDiT test",
|
||||
*mmdit,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register mmdit tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_DIFFUSION_MODEL_HPP__
|
||||
#ifndef __SD_MODEL_DIFFUSION_MODEL_HPP__
|
||||
#define __SD_MODEL_DIFFUSION_MODEL_HPP__
|
||||
|
||||
#include <string>
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/ggml_extend.hpp"
|
||||
#include "core/tensor_ggml.hpp"
|
||||
#include "model_manager.h"
|
||||
|
||||
struct UNetDiffusionExtra {
|
||||
int num_video_frames = -1;
|
||||
@@ -88,9 +89,9 @@ protected:
|
||||
|
||||
public:
|
||||
DiffusionModelRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const std::string& prefix)
|
||||
: GGMLRunner(backend, params_backend),
|
||||
const std::string& prefix,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
prefix(prefix) {}
|
||||
|
||||
virtual sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -710,10 +710,10 @@ namespace Pid {
|
||||
std::vector<float> pixel_pos_comp_vec;
|
||||
|
||||
PiDRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix = "model.diffusion_model")
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(PixelDiTConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
model = PixelDiT(config);
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -823,7 +823,7 @@ namespace Pid {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, lq_latent, degrade_sigma);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
@@ -518,12 +518,12 @@ namespace Qwen {
|
||||
SDVersion version;
|
||||
|
||||
QwenImageRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool zero_cond_t = false)
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool zero_cond_t = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(QwenImageConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
config.zero_cond_t = config.zero_cond_t || zero_cond_t;
|
||||
qwen_image = QwenImageModel(config);
|
||||
@@ -627,7 +627,7 @@ namespace Qwen {
|
||||
return build_graph(x, timesteps, context, ref_latents, increase_ref_index);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
@@ -691,7 +691,8 @@ namespace Qwen {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_Q8_0;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -705,23 +706,20 @@ namespace Qwen {
|
||||
}
|
||||
|
||||
std::shared_ptr<QwenImageRunner> qwen_image = std::make_shared<QwenImageRunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model",
|
||||
VERSION_QWEN_IMAGE);
|
||||
VERSION_QWEN_IMAGE,
|
||||
false,
|
||||
model_manager);
|
||||
|
||||
if (!qwen_image->alloc_params_buffer()) {
|
||||
LOG_ERROR("qwen_image buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
qwen_image->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("Qwen image test",
|
||||
*qwen_image,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register qwen_image tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -694,11 +694,11 @@ struct UNetModelRunner : public DiffusionModelRunner {
|
||||
UnetModelBlock unet;
|
||||
|
||||
UNetModelRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
SDVersion version = VERSION_SD1,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(UNetConfig::detect_from_weights(tensor_storage_map, prefix, version)),
|
||||
unet(config) {
|
||||
unet.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -772,7 +772,7 @@ struct UNetModelRunner : public DiffusionModelRunner {
|
||||
return build_graph(x, timesteps, context, c_concat, y, num_video_frames, controls, control_strength);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
|
||||
+18
-21
@@ -799,11 +799,11 @@ namespace WAN {
|
||||
SDVersion version;
|
||||
|
||||
WanRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_WAN2)
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_WAN2,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(WanConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
if (config.num_layers == 30) {
|
||||
if (version == VERSION_WAN2_2_TI2V) {
|
||||
@@ -950,7 +950,7 @@ namespace WAN {
|
||||
return build_graph(x, timesteps, context, clip_fea, c_concat, time_dim_concat, vace_context, vace_strength);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
@@ -1017,7 +1017,8 @@ namespace WAN {
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -1031,23 +1032,19 @@ namespace WAN {
|
||||
}
|
||||
|
||||
std::shared_ptr<WanRunner> wan = std::make_shared<WanRunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model",
|
||||
VERSION_WAN2_2_TI2V);
|
||||
VERSION_WAN2_2_TI2V,
|
||||
model_manager);
|
||||
|
||||
if (!wan->alloc_params_buffer()) {
|
||||
LOG_ERROR("wan buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
wan->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("Wan test",
|
||||
*wan,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register wan tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -553,11 +553,11 @@ namespace ZImage {
|
||||
SDVersion version;
|
||||
|
||||
ZImageRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_Z_IMAGE)
|
||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_Z_IMAGE,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(ZImageConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
z_image = ZImageModel(config);
|
||||
z_image.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -634,7 +634,7 @@ namespace ZImage {
|
||||
return build_graph(x, timesteps, context, ref_latents, increase_ref_index);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
@@ -698,7 +698,8 @@ namespace ZImage {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_Q8_0;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -714,22 +715,19 @@ namespace ZImage {
|
||||
}
|
||||
|
||||
std::shared_ptr<ZImageRunner> z_image = std::make_shared<ZImageRunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model",
|
||||
VERSION_QWEN_IMAGE);
|
||||
VERSION_QWEN_IMAGE,
|
||||
model_manager);
|
||||
|
||||
if (!z_image->alloc_params_buffer()) {
|
||||
LOG_ERROR("z_image buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
z_image->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("ZImage test",
|
||||
*z_image,
|
||||
"model.diffusion_model",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register z_image tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+11
-8
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_TE_CLIP_HPP__
|
||||
#ifndef __SD_MODEL_TE_CLIP_HPP__
|
||||
#define __SD_MODEL_TE_CLIP_HPP__
|
||||
|
||||
#include "core/ggml_extend.hpp"
|
||||
@@ -469,13 +469,13 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
std::vector<float> attention_mask_vec;
|
||||
|
||||
CLIPTextModelRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
||||
bool with_final_ln = true,
|
||||
bool force_clip_f32 = false)
|
||||
: GGMLRunner(backend, params_backend) {
|
||||
CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
||||
bool with_final_ln = true,
|
||||
bool force_clip_f32 = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager) {
|
||||
bool proj_in = false;
|
||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (!starts_with(name, prefix)) {
|
||||
@@ -567,11 +567,14 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
void* custom_embeddings_data,
|
||||
size_t max_token_idx,
|
||||
bool return_pooled,
|
||||
int clip_skip) {
|
||||
int clip_skip,
|
||||
bool auto_free = true,
|
||||
bool free_compute_buffer = true,
|
||||
bool free_compute_params = true) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(input_ids, num_custom_embeddings, custom_embeddings_data, max_token_idx, return_pooled, clip_skip);
|
||||
};
|
||||
auto result = GGMLRunner::compute<float>(get_graph, n_threads, true);
|
||||
auto result = GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params);
|
||||
if (return_pooled) {
|
||||
return take_or_empty(std::move(result));
|
||||
}
|
||||
|
||||
+32
-35
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_TE_LLM_HPP__
|
||||
#ifndef __SD_MODEL_TE_LLM_HPP__
|
||||
#define __SD_MODEL_TE_LLM_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "json.hpp"
|
||||
#include "model/common/rope.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
#include "tokenizers/bpe_tokenizer.h"
|
||||
#include "tokenizers/gemma_tokenizer.h"
|
||||
#include "tokenizers/gpt_oss_tokenizer.h"
|
||||
@@ -1571,11 +1572,11 @@ namespace LLM {
|
||||
public:
|
||||
LLMRunner(LLMArch arch,
|
||||
ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool enable_vision_ = false)
|
||||
: GGMLRunner(backend, params_backend),
|
||||
bool enable_vision_ = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
config(LLMConfig::detect_from_weights(tensor_storage_map, prefix, arch)),
|
||||
enable_vision(enable_vision_) {
|
||||
if (enable_vision && !config.have_vision_weight) {
|
||||
@@ -1733,7 +1734,10 @@ namespace LLM {
|
||||
const sd::Tensor<float>& attention_mask,
|
||||
const std::vector<std::pair<int, sd::Tensor<float>>>& image_embeds,
|
||||
std::set<int> out_layers,
|
||||
bool return_all_hidden_states = false) {
|
||||
bool return_all_hidden_states = false,
|
||||
bool auto_free = true,
|
||||
bool free_compute_buffer = true,
|
||||
bool free_compute_params = true) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(input_ids,
|
||||
attention_mask,
|
||||
@@ -1741,7 +1745,7 @@ namespace LLM {
|
||||
out_layers,
|
||||
return_all_hidden_states);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true),
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params),
|
||||
input_ids.dim() + 1);
|
||||
}
|
||||
|
||||
@@ -1802,11 +1806,14 @@ namespace LLM {
|
||||
}
|
||||
|
||||
sd::Tensor<float> encode_image(const int n_threads,
|
||||
const sd::Tensor<float>& image) {
|
||||
const sd::Tensor<float>& image,
|
||||
bool auto_free = false,
|
||||
bool free_compute_buffer = false,
|
||||
bool free_compute_params = false) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_encode_image_graph(image);
|
||||
};
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, false));
|
||||
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1816,11 +1823,11 @@ namespace LLM {
|
||||
|
||||
LLMEmbedder(LLMArch arch,
|
||||
ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false)
|
||||
: model(arch, backend, params_backend, tensor_storage_map, prefix, enable_vision) {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: model(arch, backend, tensor_storage_map, prefix, enable_vision, weight_manager) {
|
||||
if (arch == LLMArch::MISTRAL_SMALL_3_2 || arch == LLMArch::MINISTRAL_3_3B) {
|
||||
tokenizer = std::make_shared<MistralTokenizer>();
|
||||
} else if (arch == LLMArch::GPT_OSS_20B) {
|
||||
@@ -1834,13 +1841,6 @@ namespace LLM {
|
||||
model.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() {
|
||||
if (!model.alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>> tokenize(std::string text,
|
||||
std::pair<int, int> attn_range,
|
||||
size_t max_length = 0,
|
||||
@@ -2056,7 +2056,8 @@ namespace LLM {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_COUNT;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "text_encoders.llm.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -2074,24 +2075,20 @@ namespace LLM {
|
||||
LLMArch arch = LLMArch::QWEN3;
|
||||
|
||||
std::shared_ptr<LLMEmbedder> llm = std::make_shared<LLMEmbedder>(arch,
|
||||
backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"text_encoders.llm",
|
||||
true);
|
||||
true,
|
||||
model_manager);
|
||||
|
||||
if (!llm->alloc_params_buffer()) {
|
||||
LOG_ERROR("llm model allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
llm->get_param_tensors(tensors, "text_encoders.llm");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("LLM test",
|
||||
*llm,
|
||||
"text_encoders.llm",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register llm tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+26
-31
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_TE_T5_HPP__
|
||||
#ifndef __SD_MODEL_TE_T5_HPP__
|
||||
#define __SD_MODEL_TE_T5_HPP__
|
||||
|
||||
#include <cfloat>
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "core/ggml_extend.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
#include "tokenizers/t5_unigram_tokenizer.h"
|
||||
|
||||
struct T5Config {
|
||||
@@ -334,11 +335,11 @@ struct T5Runner : public GGMLRunner {
|
||||
std::vector<int> relative_position_bucket_vec;
|
||||
|
||||
T5Runner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool is_umt5 = false)
|
||||
: GGMLRunner(backend, params_backend),
|
||||
bool is_umt5 = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
config(T5Config::detect_from_weights(tensor_storage_map, prefix, is_umt5)) {
|
||||
model = T5(config);
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -394,11 +395,14 @@ struct T5Runner : public GGMLRunner {
|
||||
|
||||
sd::Tensor<float> compute(const int n_threads,
|
||||
const sd::Tensor<int32_t>& input_ids,
|
||||
const sd::Tensor<float>& attention_mask) {
|
||||
const sd::Tensor<float>& attention_mask,
|
||||
bool auto_free = true,
|
||||
bool free_compute_buffer = true,
|
||||
bool free_compute_params = true) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(input_ids, attention_mask);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true), 3);
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, auto_free, free_compute_buffer, free_compute_params), 3);
|
||||
}
|
||||
|
||||
static std::vector<int> _relative_position_bucket(const std::vector<int>& relative_position,
|
||||
@@ -474,24 +478,17 @@ struct T5Embedder {
|
||||
T5Runner model;
|
||||
|
||||
T5Embedder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool is_umt5 = false)
|
||||
: model(backend, params_backend, tensor_storage_map, prefix, is_umt5), tokenizer(is_umt5) {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool is_umt5 = false,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: model(backend, tensor_storage_map, prefix, is_umt5, weight_manager), tokenizer(is_umt5) {
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) {
|
||||
model.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
bool alloc_params_buffer() {
|
||||
if (!model.alloc_params_buffer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
@@ -576,7 +573,8 @@ struct T5Embedder {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
@@ -589,19 +587,16 @@ struct T5Embedder {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<T5Embedder> t5 = std::make_shared<T5Embedder>(backend, backend, tensor_storage_map, "", true);
|
||||
std::shared_ptr<T5Embedder> t5 = std::make_shared<T5Embedder>(backend, tensor_storage_map, "", true, model_manager);
|
||||
|
||||
if (!t5->alloc_params_buffer()) {
|
||||
LOG_ERROR("t5 params buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
t5->get_param_tensors(tensors, "");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("T5 test",
|
||||
*t5,
|
||||
"",
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register t5 tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+106
-208
@@ -1,8 +1,14 @@
|
||||
#ifndef __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
||||
#ifndef __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
||||
#define __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "core/ggml_extend.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "core/util.h"
|
||||
|
||||
/*
|
||||
=================================== ESRGAN ===================================
|
||||
@@ -12,6 +18,74 @@
|
||||
|
||||
*/
|
||||
|
||||
struct ESRGANConfig {
|
||||
int scale = 4;
|
||||
int num_block = 23;
|
||||
int num_in_ch = 3;
|
||||
int num_out_ch = 3;
|
||||
int num_feat = 64;
|
||||
int num_grow_ch = 32;
|
||||
|
||||
static ESRGANConfig detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& prefix = "") {
|
||||
ESRGANConfig config;
|
||||
auto find_weight = [&](const std::string& suffix) -> const TensorStorage* {
|
||||
std::string name = prefix.empty() ? suffix : prefix + "." + suffix;
|
||||
auto iter = tensor_storage_map.find(name);
|
||||
if (iter == tensor_storage_map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &iter->second;
|
||||
};
|
||||
|
||||
int detected_num_block = 0;
|
||||
const std::string body_prefix = prefix.empty() ? "body." : prefix + ".body.";
|
||||
for (const auto& [name, _] : tensor_storage_map) {
|
||||
if (!starts_with(name, body_prefix)) {
|
||||
continue;
|
||||
}
|
||||
size_t pos = name.find('.', body_prefix.size());
|
||||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
int idx = std::stoi(name.substr(body_prefix.size(), pos - body_prefix.size()));
|
||||
detected_num_block = std::max(detected_num_block, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (detected_num_block > 0) {
|
||||
config.num_block = detected_num_block;
|
||||
}
|
||||
|
||||
bool has_conv_up2 = find_weight("conv_up2.weight") != nullptr;
|
||||
bool has_conv_up1 = find_weight("conv_up1.weight") != nullptr;
|
||||
bool has_model_tensor =
|
||||
detected_num_block > 0 ||
|
||||
find_weight("conv_first.weight") != nullptr ||
|
||||
find_weight("conv_hr.weight") != nullptr ||
|
||||
find_weight("conv_last.weight") != nullptr;
|
||||
if (has_conv_up2) {
|
||||
config.scale = 4;
|
||||
} else if (has_conv_up1) {
|
||||
config.scale = 2;
|
||||
} else if (has_model_tensor) {
|
||||
config.scale = 1;
|
||||
}
|
||||
|
||||
if (has_model_tensor || has_conv_up1 || has_conv_up2) {
|
||||
LOG_DEBUG("esrgan: scale = %d, num_block = %d, num_in_ch = %d, num_out_ch = %d, num_feat = %d, num_grow_ch = %d",
|
||||
config.scale,
|
||||
config.num_block,
|
||||
config.num_in_ch,
|
||||
config.num_out_ch,
|
||||
config.num_feat,
|
||||
config.num_grow_ch);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
class ResidualDenseBlock : public GGMLBlock {
|
||||
protected:
|
||||
int num_feat;
|
||||
@@ -83,34 +157,29 @@ public:
|
||||
|
||||
class RRDBNet : public GGMLBlock {
|
||||
protected:
|
||||
int scale = 4;
|
||||
int num_block = 23;
|
||||
int num_in_ch = 3;
|
||||
int num_out_ch = 3;
|
||||
int num_feat = 64;
|
||||
int num_grow_ch = 32;
|
||||
ESRGANConfig config;
|
||||
|
||||
public:
|
||||
RRDBNet(int scale, int num_block, int num_in_ch, int num_out_ch, int num_feat, int num_grow_ch)
|
||||
: scale(scale), num_block(num_block), num_in_ch(num_in_ch), num_out_ch(num_out_ch), num_feat(num_feat), num_grow_ch(num_grow_ch) {
|
||||
blocks["conv_first"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_in_ch, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
for (int i = 0; i < num_block; i++) {
|
||||
explicit RRDBNet(ESRGANConfig config)
|
||||
: config(std::move(config)) {
|
||||
blocks["conv_first"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_in_ch, this->config.num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
for (int i = 0; i < this->config.num_block; i++) {
|
||||
std::string name = "body." + std::to_string(i);
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new RRDB(num_feat, num_grow_ch));
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new RRDB(this->config.num_feat, this->config.num_grow_ch));
|
||||
}
|
||||
blocks["conv_body"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (scale >= 2) {
|
||||
blocks["conv_up1"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_body"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_feat, this->config.num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (this->config.scale >= 2) {
|
||||
blocks["conv_up1"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_feat, this->config.num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
if (scale == 4) {
|
||||
blocks["conv_up2"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (this->config.scale == 4) {
|
||||
blocks["conv_up2"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_feat, this->config.num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
blocks["conv_hr"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_last"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_out_ch, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_hr"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_feat, this->config.num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_last"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.num_feat, this->config.num_out_ch, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
int get_scale() { return scale; }
|
||||
int get_num_block() { return num_block; }
|
||||
int get_scale() { return config.scale; }
|
||||
int get_num_block() { return config.num_block; }
|
||||
|
||||
ggml_tensor* lrelu(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
return ggml_leaky_relu(ctx->ggml_ctx, x, 0.2f, true);
|
||||
@@ -127,7 +196,7 @@ public:
|
||||
auto feat = conv_first->forward(ctx, x);
|
||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.prelude", "feat");
|
||||
auto body_feat = feat;
|
||||
for (int i = 0; i < num_block; i++) {
|
||||
for (int i = 0; i < config.num_block; i++) {
|
||||
std::string name = "body." + std::to_string(i);
|
||||
auto block = std::dynamic_pointer_cast<RRDB>(blocks[name]);
|
||||
|
||||
@@ -138,11 +207,11 @@ public:
|
||||
feat = ggml_add(ctx->ggml_ctx, feat, body_feat);
|
||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.body.out", "feat");
|
||||
// upsample
|
||||
if (scale >= 2) {
|
||||
if (config.scale >= 2) {
|
||||
auto conv_up1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up1"]);
|
||||
feat = lrelu(ctx, conv_up1->forward(ctx, ggml_upscale(ctx->ggml_ctx, feat, 2, GGML_SCALE_MODE_NEAREST)));
|
||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.up1", "feat");
|
||||
if (scale == 4) {
|
||||
if (config.scale == 4) {
|
||||
auto conv_up2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up2"]);
|
||||
feat = lrelu(ctx, conv_up2->forward(ctx, ggml_upscale(ctx->ggml_ctx, feat, 2, GGML_SCALE_MODE_NEAREST)));
|
||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.up2", "feat");
|
||||
@@ -156,199 +225,28 @@ public:
|
||||
};
|
||||
|
||||
struct ESRGAN : public GGMLRunner {
|
||||
ESRGANConfig config;
|
||||
std::unique_ptr<RRDBNet> rrdb_net;
|
||||
int scale = 4;
|
||||
int tile_size = 128; // avoid cuda OOM for 4gb VRAM
|
||||
|
||||
ESRGAN(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
int tile_size = 128,
|
||||
const String2TensorStorage& tensor_storage_map = {})
|
||||
: GGMLRunner(backend, params_backend) {
|
||||
this->tile_size = tile_size;
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
config(ESRGANConfig::detect_from_weights(tensor_storage_map)),
|
||||
rrdb_net(std::make_unique<RRDBNet>(config)) {
|
||||
rrdb_net->init(params_ctx, tensor_storage_map, "");
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "esrgan";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading esrgan from '%s'", file_path.c_str());
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path)) {
|
||||
LOG_ERROR("init esrgan model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
||||
if (!rrdb_net) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get tensor names
|
||||
auto tensor_names = model_loader.get_tensor_names();
|
||||
|
||||
// Detect if it's ESRGAN format
|
||||
bool is_ESRGAN = std::find(tensor_names.begin(), tensor_names.end(), "model.0.weight") != tensor_names.end();
|
||||
|
||||
// Detect parameters from tensor names
|
||||
int detected_num_block = 0;
|
||||
if (is_ESRGAN) {
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("model.1.sub.") == 0) {
|
||||
size_t first_dot = name.find('.', 12);
|
||||
if (first_dot != std::string::npos) {
|
||||
size_t second_dot = name.find('.', first_dot + 1);
|
||||
if (second_dot != std::string::npos && name.substr(first_dot + 1, 3) == "RDB") {
|
||||
try {
|
||||
int idx = std::stoi(name.substr(12, first_dot - 12));
|
||||
detected_num_block = std::max(detected_num_block, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Original format
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("body.") == 0) {
|
||||
size_t pos = name.find('.', 5);
|
||||
if (pos != std::string::npos) {
|
||||
try {
|
||||
int idx = std::stoi(name.substr(5, pos - 5));
|
||||
detected_num_block = std::max(detected_num_block, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int detected_scale = 4; // default
|
||||
if (is_ESRGAN) {
|
||||
// For ESRGAN format, detect scale by highest model number
|
||||
int max_model_num = 0;
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("model.") == 0) {
|
||||
size_t dot_pos = name.find('.', 6);
|
||||
if (dot_pos != std::string::npos) {
|
||||
try {
|
||||
int num = std::stoi(name.substr(6, dot_pos - 6));
|
||||
max_model_num = std::max(max_model_num, num);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_model_num <= 4) {
|
||||
detected_scale = 1;
|
||||
} else if (max_model_num <= 7) {
|
||||
detected_scale = 2;
|
||||
} else {
|
||||
detected_scale = 4;
|
||||
}
|
||||
} else {
|
||||
// Original format
|
||||
bool has_conv_up2 = std::any_of(tensor_names.begin(), tensor_names.end(), [](const std::string& name) {
|
||||
return name == "conv_up2.weight";
|
||||
});
|
||||
bool has_conv_up1 = std::any_of(tensor_names.begin(), tensor_names.end(), [](const std::string& name) {
|
||||
return name == "conv_up1.weight";
|
||||
});
|
||||
if (has_conv_up2) {
|
||||
detected_scale = 4;
|
||||
} else if (has_conv_up1) {
|
||||
detected_scale = 2;
|
||||
} else {
|
||||
detected_scale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int detected_num_in_ch = 3;
|
||||
int detected_num_out_ch = 3;
|
||||
int detected_num_feat = 64;
|
||||
int detected_num_grow_ch = 32;
|
||||
|
||||
// Create RRDBNet with detected parameters
|
||||
rrdb_net = std::make_unique<RRDBNet>(detected_scale, detected_num_block, detected_num_in_ch, detected_num_out_ch, detected_num_feat, detected_num_grow_ch);
|
||||
rrdb_net->init(params_ctx, {}, "");
|
||||
|
||||
if (!alloc_params_buffer()) {
|
||||
LOG_ERROR("esrgan model buffer allocation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> esrgan_tensors;
|
||||
rrdb_net->get_param_tensors(esrgan_tensors);
|
||||
|
||||
bool success;
|
||||
if (is_ESRGAN) {
|
||||
// Build name mapping for ESRGAN format
|
||||
std::map<std::string, std::string> expected_to_model;
|
||||
expected_to_model["conv_first.weight"] = "model.0.weight";
|
||||
expected_to_model["conv_first.bias"] = "model.0.bias";
|
||||
|
||||
for (int i = 0; i < detected_num_block; i++) {
|
||||
for (int j = 1; j <= 3; j++) {
|
||||
for (int k = 1; k <= 5; k++) {
|
||||
std::string expected_weight = "body." + std::to_string(i) + ".rdb" + std::to_string(j) + ".conv" + std::to_string(k) + ".weight";
|
||||
std::string model_weight = "model.1.sub." + std::to_string(i) + ".RDB" + std::to_string(j) + ".conv" + std::to_string(k) + ".0.weight";
|
||||
expected_to_model[expected_weight] = model_weight;
|
||||
|
||||
std::string expected_bias = "body." + std::to_string(i) + ".rdb" + std::to_string(j) + ".conv" + std::to_string(k) + ".bias";
|
||||
std::string model_bias = "model.1.sub." + std::to_string(i) + ".RDB" + std::to_string(j) + ".conv" + std::to_string(k) + ".0.bias";
|
||||
expected_to_model[expected_bias] = model_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (detected_scale == 1) {
|
||||
expected_to_model["conv_body.weight"] = "model.1.sub." + std::to_string(detected_num_block) + ".weight";
|
||||
expected_to_model["conv_body.bias"] = "model.1.sub." + std::to_string(detected_num_block) + ".bias";
|
||||
expected_to_model["conv_hr.weight"] = "model.2.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.2.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.4.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.4.bias";
|
||||
} else {
|
||||
expected_to_model["conv_body.weight"] = "model.1.sub." + std::to_string(detected_num_block) + ".weight";
|
||||
expected_to_model["conv_body.bias"] = "model.1.sub." + std::to_string(detected_num_block) + ".bias";
|
||||
if (detected_scale >= 2) {
|
||||
expected_to_model["conv_up1.weight"] = "model.3.weight";
|
||||
expected_to_model["conv_up1.bias"] = "model.3.bias";
|
||||
}
|
||||
if (detected_scale == 4) {
|
||||
expected_to_model["conv_up2.weight"] = "model.6.weight";
|
||||
expected_to_model["conv_up2.bias"] = "model.6.bias";
|
||||
expected_to_model["conv_hr.weight"] = "model.8.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.8.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.10.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.10.bias";
|
||||
} else if (detected_scale == 2) {
|
||||
expected_to_model["conv_hr.weight"] = "model.5.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.5.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.7.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.7.bias";
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> model_tensors;
|
||||
for (auto& p : esrgan_tensors) {
|
||||
auto it = expected_to_model.find(p.first);
|
||||
if (it != expected_to_model.end()) {
|
||||
model_tensors[it->second] = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
success = model_loader.load_tensors(model_tensors, {}, n_threads);
|
||||
} else {
|
||||
success = model_loader.load_tensors(esrgan_tensors, {}, n_threads);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load esrgan tensors from model loader failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
scale = rrdb_net->get_scale();
|
||||
LOG_INFO("esrgan model loaded with scale=%d, num_block=%d", scale, detected_num_block);
|
||||
return success;
|
||||
rrdb_net->get_param_tensors(tensors);
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor) {
|
||||
@@ -367,7 +265,7 @@ struct ESRGAN : public GGMLRunner {
|
||||
sd::Tensor<float> compute(const int n_threads,
|
||||
const sd::Tensor<float>& x) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* { return build_graph(x); };
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), x.dim());
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), x.dim());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
||||
#ifndef __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
||||
#define __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -32,90 +32,100 @@ namespace LTXVUpsampler {
|
||||
int spatial_up_num = 2;
|
||||
int spatial_down_den = 1;
|
||||
int temporal_up_factor = 1;
|
||||
};
|
||||
|
||||
static inline bool has_tensor(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& name) {
|
||||
return tensor_storage_map.find(name) != tensor_storage_map.end();
|
||||
}
|
||||
static LatentUpsamplerConfig detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& prefix = "") {
|
||||
LatentUpsamplerConfig config;
|
||||
auto find_weight = [&](const std::string& suffix) -> const TensorStorage* {
|
||||
std::string name = prefix.empty() ? suffix : prefix + "." + suffix;
|
||||
auto iter = tensor_storage_map.find(name);
|
||||
if (iter == tensor_storage_map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &iter->second;
|
||||
};
|
||||
|
||||
static inline int64_t get_tensor_ne(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& name,
|
||||
int axis,
|
||||
int64_t fallback) {
|
||||
auto it = tensor_storage_map.find(name);
|
||||
if (it == tensor_storage_map.end() || axis < 0 || axis >= GGML_MAX_DIMS) {
|
||||
return fallback;
|
||||
}
|
||||
return it->second.ne[axis];
|
||||
}
|
||||
bool inferred = false;
|
||||
|
||||
static inline int64_t get_tensor_ne0(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& name,
|
||||
int64_t fallback) {
|
||||
return get_tensor_ne(tensor_storage_map, name, 0, fallback);
|
||||
}
|
||||
|
||||
static inline int count_module_blocks(const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& module_name) {
|
||||
int max_block = -1;
|
||||
const std::string prefix = module_name + ".";
|
||||
for (const auto& pair : tensor_storage_map) {
|
||||
const std::string& name = pair.first;
|
||||
if (name.find(prefix) != 0) {
|
||||
continue;
|
||||
const TensorStorage* initial_norm = find_weight("initial_norm.weight");
|
||||
if (initial_norm != nullptr) {
|
||||
config.mid_channels = initial_norm->ne[0];
|
||||
inferred = true;
|
||||
}
|
||||
size_t begin = prefix.size();
|
||||
size_t end = name.find('.', begin);
|
||||
if (end == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
int index = atoi(name.substr(begin, end - begin).c_str());
|
||||
max_block = std::max(max_block, index);
|
||||
}
|
||||
return max_block + 1;
|
||||
}
|
||||
|
||||
static inline LatentUpsamplerConfig detect_config_from_weights(const String2TensorStorage& tensor_storage_map) {
|
||||
LatentUpsamplerConfig config;
|
||||
config.mid_channels = get_tensor_ne0(tensor_storage_map, "initial_norm.weight", config.mid_channels);
|
||||
config.in_channels = get_tensor_ne0(tensor_storage_map, "final_conv.bias", config.in_channels);
|
||||
int detected_blocks = count_module_blocks(tensor_storage_map, "res_blocks");
|
||||
if (detected_blocks > 0) {
|
||||
config.num_blocks_per_stage = detected_blocks;
|
||||
}
|
||||
config.rational_resampler = has_tensor(tensor_storage_map, "upsampler.conv.weight");
|
||||
int64_t upsampler_out_channels = get_tensor_ne0(tensor_storage_map, "upsampler.0.bias", 0);
|
||||
config.spatial_upsample = config.rational_resampler || upsampler_out_channels == 4 * config.mid_channels;
|
||||
config.temporal_upsample = upsampler_out_channels == 2 * config.mid_channels;
|
||||
if (config.temporal_upsample) {
|
||||
config.temporal_up_factor = 2;
|
||||
}
|
||||
if (config.rational_resampler) {
|
||||
int64_t out_channels = get_tensor_ne(tensor_storage_map,
|
||||
"upsampler.conv.weight",
|
||||
3,
|
||||
config.mid_channels * 9);
|
||||
if (config.mid_channels > 0 && out_channels % config.mid_channels == 0) {
|
||||
int64_t ratio = out_channels / config.mid_channels;
|
||||
int num = static_cast<int>(std::round(std::sqrt(static_cast<double>(ratio))));
|
||||
if (num > 0 && static_cast<int64_t>(num) * num == ratio) {
|
||||
config.spatial_up_num = num;
|
||||
const TensorStorage* final_conv = find_weight("final_conv.bias");
|
||||
if (final_conv != nullptr) {
|
||||
config.in_channels = final_conv->ne[0];
|
||||
inferred = true;
|
||||
}
|
||||
|
||||
int detected_blocks = 0;
|
||||
const std::string res_blocks_prefix = prefix.empty() ? "res_blocks." : prefix + ".res_blocks.";
|
||||
for (const auto& [name, _] : tensor_storage_map) {
|
||||
if (!starts_with(name, res_blocks_prefix)) {
|
||||
continue;
|
||||
}
|
||||
size_t begin = res_blocks_prefix.size();
|
||||
size_t end = name.find('.', begin);
|
||||
if (end == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
int idx = std::stoi(name.substr(begin, end - begin));
|
||||
detected_blocks = std::max(detected_blocks, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (config.spatial_up_num == 3) {
|
||||
config.spatial_down_den = 2;
|
||||
config.spatial_scale = 1.5f;
|
||||
} else if (config.spatial_up_num == 4) {
|
||||
config.spatial_down_den = 1;
|
||||
config.spatial_scale = 4.f;
|
||||
} else {
|
||||
config.spatial_down_den = 1;
|
||||
config.spatial_scale = static_cast<float>(config.spatial_up_num);
|
||||
if (detected_blocks > 0) {
|
||||
config.num_blocks_per_stage = detected_blocks;
|
||||
inferred = true;
|
||||
}
|
||||
|
||||
const TensorStorage* rational_upsampler_weight = find_weight("upsampler.conv.weight");
|
||||
const TensorStorage* upsampler_bias = find_weight("upsampler.0.bias");
|
||||
config.rational_resampler = rational_upsampler_weight != nullptr;
|
||||
int64_t upsampler_out_channels = upsampler_bias == nullptr ? 0 : upsampler_bias->ne[0];
|
||||
config.spatial_upsample = config.rational_resampler || upsampler_out_channels == 4 * config.mid_channels;
|
||||
config.temporal_upsample = upsampler_out_channels == 2 * config.mid_channels;
|
||||
if (config.rational_resampler || upsampler_out_channels > 0) {
|
||||
inferred = true;
|
||||
}
|
||||
if (config.temporal_upsample) {
|
||||
config.temporal_up_factor = 2;
|
||||
}
|
||||
if (rational_upsampler_weight != nullptr) {
|
||||
int64_t out_channels = rational_upsampler_weight->ne[3];
|
||||
if (config.mid_channels > 0 && out_channels % config.mid_channels == 0) {
|
||||
int64_t ratio = out_channels / config.mid_channels;
|
||||
int num = static_cast<int>(std::round(std::sqrt(static_cast<double>(ratio))));
|
||||
if (num > 0 && static_cast<int64_t>(num) * num == ratio) {
|
||||
config.spatial_up_num = num;
|
||||
}
|
||||
}
|
||||
if (config.spatial_up_num == 3) {
|
||||
config.spatial_down_den = 2;
|
||||
config.spatial_scale = 1.5f;
|
||||
} else if (config.spatial_up_num == 4) {
|
||||
config.spatial_down_den = 1;
|
||||
config.spatial_scale = 4.f;
|
||||
} else {
|
||||
config.spatial_down_den = 1;
|
||||
config.spatial_scale = static_cast<float>(config.spatial_up_num);
|
||||
}
|
||||
}
|
||||
|
||||
if (inferred) {
|
||||
LOG_DEBUG("ltx latent upsampler: in_channels = %" PRId64 ", mid_channels = %" PRId64 ", num_blocks_per_stage = %d, spatial_scale = %.3f, temporal_up_factor = %d, rational_resampler = %d",
|
||||
config.in_channels,
|
||||
config.mid_channels,
|
||||
config.num_blocks_per_stage,
|
||||
config.spatial_scale,
|
||||
config.temporal_up_factor,
|
||||
config.rational_resampler);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoGroupNorm : public GGMLBlock {
|
||||
protected:
|
||||
@@ -240,20 +250,25 @@ namespace LTXVUpsampler {
|
||||
protected:
|
||||
int64_t channels;
|
||||
int stride;
|
||||
ggml_tensor* kernel = nullptr;
|
||||
std::vector<float> kernel_data;
|
||||
std::string kernel_name;
|
||||
|
||||
void init_params(ggml_context* ctx,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "") override {
|
||||
SD_UNUSED(ctx);
|
||||
SD_UNUSED(tensor_storage_map);
|
||||
if (stride == 1) {
|
||||
return;
|
||||
}
|
||||
kernel = ggml_new_tensor_4d(ctx, GGML_TYPE_F32, 5, 5, 1, channels);
|
||||
std::string name = prefix + "kernel";
|
||||
ggml_set_name(kernel, name.c_str());
|
||||
kernel_name = prefix + "kernel";
|
||||
}
|
||||
|
||||
public:
|
||||
BlurDownsample(int64_t channels, int stride)
|
||||
: channels(channels),
|
||||
stride(stride) {
|
||||
GGML_ASSERT(stride >= 1);
|
||||
static const float binomial[5] = {1.f, 4.f, 6.f, 4.f, 1.f};
|
||||
kernel_data.resize(static_cast<size_t>(5 * 5 * channels));
|
||||
for (int64_t c = 0; c < channels; ++c) {
|
||||
@@ -266,26 +281,16 @@ namespace LTXVUpsampler {
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BlurDownsample(int64_t channels, int stride)
|
||||
: channels(channels),
|
||||
stride(stride) {
|
||||
GGML_ASSERT(stride >= 1);
|
||||
}
|
||||
|
||||
void load_fixed_tensors() {
|
||||
if (kernel == nullptr || kernel_data.empty()) {
|
||||
return;
|
||||
}
|
||||
ggml_backend_tensor_set(kernel, kernel_data.data(), 0, kernel_data.size() * sizeof(float));
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
if (stride == 1) {
|
||||
return x;
|
||||
}
|
||||
GGML_ASSERT(kernel != nullptr);
|
||||
GGML_ASSERT(ctx != nullptr);
|
||||
GGML_ASSERT(!kernel_data.empty());
|
||||
GGML_ASSERT(x->ne[2] == channels);
|
||||
ggml_tensor* kernel = ggml_new_tensor_4d(ctx->ggml_ctx, GGML_TYPE_F32, 5, 5, 1, channels);
|
||||
ggml_set_name(kernel, kernel_name.empty() ? "blur_down.kernel" : kernel_name.c_str());
|
||||
ctx->bind_backend_tensor_data(kernel, kernel_data.data());
|
||||
if (ctx->conv2d_direct_enabled) {
|
||||
return ggml_conv_2d_dw_direct(ctx->ggml_ctx, kernel, x, stride, stride, 2, 2, 1, 1);
|
||||
}
|
||||
@@ -311,11 +316,6 @@ namespace LTXVUpsampler {
|
||||
blocks["blur_down"] = std::shared_ptr<GGMLBlock>(new BlurDownsample(mid_channels, den));
|
||||
}
|
||||
|
||||
void load_fixed_tensors() {
|
||||
auto blur_down = std::dynamic_pointer_cast<BlurDownsample>(blocks["blur_down"]);
|
||||
blur_down->load_fixed_tensors();
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
auto conv = std::dynamic_pointer_cast<Conv2d>(blocks["conv"]);
|
||||
auto pixel_shuffle = std::dynamic_pointer_cast<PixelShuffleND>(blocks["pixel_shuffle"]);
|
||||
@@ -426,45 +426,17 @@ namespace LTXVUpsampler {
|
||||
sd::ggml_graph_cut::mark_graph_cut(x, "ltx_latent_upsampler.final", "x");
|
||||
return x;
|
||||
}
|
||||
|
||||
void load_fixed_tensors() {
|
||||
if (!config.rational_resampler) {
|
||||
return;
|
||||
}
|
||||
auto upsampler = std::dynamic_pointer_cast<SpatialRationalResampler>(blocks["upsampler"]);
|
||||
upsampler->load_fixed_tensors();
|
||||
}
|
||||
};
|
||||
|
||||
struct LatentUpsamplerRunner : public GGMLRunner {
|
||||
LatentUpsamplerConfig config;
|
||||
std::unique_ptr<LatentUpsampler> model;
|
||||
|
||||
LatentUpsamplerRunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend)
|
||||
: GGMLRunner(backend, params_backend) {}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "ltx_latent_upsampler";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading LTX latent upsampler from '%s'", file_path.c_str());
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init LTX latent upsampler model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& tensor_storage_map = model_loader.get_tensor_storage_map();
|
||||
bool has_regular_upsampler = has_tensor(tensor_storage_map, "upsampler.0.weight");
|
||||
bool has_rational_spatial = has_tensor(tensor_storage_map, "upsampler.conv.weight");
|
||||
if (!has_tensor(tensor_storage_map, "post_upsample_res_blocks.0.conv2.bias") ||
|
||||
(!has_regular_upsampler && !has_rational_spatial)) {
|
||||
LOG_ERROR("unsupported LTX latent upsampler weights: expected upsampler tensors");
|
||||
return false;
|
||||
}
|
||||
|
||||
LatentUpsamplerConfig config = detect_config_from_weights(tensor_storage_map);
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
config(LatentUpsamplerConfig::detect_from_weights(tensor_storage_map)) {
|
||||
if (config.dims != 3 || (!config.spatial_upsample && !config.temporal_upsample) ||
|
||||
config.spatial_up_num < 1 || config.spatial_down_den < 1 || config.temporal_up_factor < 1) {
|
||||
LOG_ERROR("unsupported LTX latent upsampler config: dims=%d spatial=%d temporal=%d rational=%d scale=%.3f temporal_factor=%d",
|
||||
@@ -474,36 +446,21 @@ namespace LTXVUpsampler {
|
||||
config.rational_resampler,
|
||||
config.spatial_scale,
|
||||
config.temporal_up_factor);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
model = std::make_unique<LatentUpsampler>(config);
|
||||
model->init(params_ctx, tensor_storage_map, "");
|
||||
if (!alloc_params_buffer()) {
|
||||
LOG_ERROR("LTX latent upsampler params buffer allocation failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
model->get_param_tensors(tensors);
|
||||
std::set<std::string> ignore_tensors;
|
||||
if (config.rational_resampler) {
|
||||
ignore_tensors.insert("upsampler.blur_down.kernel");
|
||||
}
|
||||
if (!model_loader.load_tensors(tensors, ignore_tensors, n_threads)) {
|
||||
LOG_ERROR("load LTX latent upsampler tensors failed");
|
||||
return false;
|
||||
}
|
||||
model->load_fixed_tensors();
|
||||
std::string get_desc() override {
|
||||
return "ltx_latent_upsampler";
|
||||
}
|
||||
|
||||
LOG_INFO("LTX latent upsampler loaded: in_channels=%" PRId64 ", mid_channels=%" PRId64 ", blocks=%d, scale=%.3f, temporal_factor=%d, rational=%d",
|
||||
config.in_channels,
|
||||
config.mid_channels,
|
||||
config.num_blocks_per_stage,
|
||||
config.spatial_scale,
|
||||
config.temporal_up_factor,
|
||||
config.rational_resampler);
|
||||
return true;
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
||||
if (model) {
|
||||
model->get_param_tensors(tensors);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor) {
|
||||
@@ -534,15 +491,15 @@ namespace LTXVUpsampler {
|
||||
(long long)x.shape()[4]);
|
||||
return {};
|
||||
}
|
||||
if (x.shape()[3] != model->config.in_channels) {
|
||||
if (x.shape()[3] != config.in_channels) {
|
||||
LOG_ERROR("LTX latent upsampler expected %" PRId64 " channels, got %lld",
|
||||
model->config.in_channels,
|
||||
config.in_channels,
|
||||
(long long)x.shape()[3]);
|
||||
return {};
|
||||
}
|
||||
size_t expected_dim = static_cast<size_t>(x.dim());
|
||||
auto get_graph = [&]() -> ggml_cgraph* { return build_graph(x); };
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), expected_dim);
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), expected_dim);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -213,9 +213,9 @@ protected:
|
||||
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);
|
||||
ggml_tensor* get_alpha(GGMLRunnerContext* ctx) {
|
||||
auto mix_factor = ggml_ext_cast_f32(ctx->ggml_ctx, ctx->backend, params["mix_factor"]);
|
||||
return ggml_sigmoid(ctx->ggml_ctx, mix_factor);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -250,10 +250,12 @@ public:
|
||||
|
||||
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));
|
||||
auto alpha = get_alpha(ctx);
|
||||
x = ggml_add(ctx->ggml_ctx,
|
||||
x_mix,
|
||||
ggml_mul(ctx->ggml_ctx,
|
||||
ggml_sub(ctx->ggml_ctx, x, x_mix),
|
||||
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
|
||||
@@ -664,13 +666,13 @@ struct AutoEncoderKL : public VAE {
|
||||
AutoEncoderKLModel ae;
|
||||
|
||||
AutoEncoderKL(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
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, params_backend) {
|
||||
bool decode_only = false,
|
||||
bool use_video_decoder = false,
|
||||
SDVersion version = VERSION_SD1,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, prefix, weight_manager), decode_only(decode_only) {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
scale_factor = 0.18215f;
|
||||
shift_factor = 0.f;
|
||||
@@ -718,8 +720,8 @@ struct AutoEncoderKL : public VAE {
|
||||
return "vae";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) override {
|
||||
ae.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
ae.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& z_tensor, bool decode_graph) {
|
||||
@@ -742,7 +744,7 @@ struct AutoEncoderKL : public VAE {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), z.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), z.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> gaussian_latent_sample(const sd::Tensor<float>& moments, std::shared_ptr<RNG> rng) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef __SD_MODEL_VAE_LTX_AUDIO_VAE_HPP__
|
||||
#ifndef __SD_MODEL_VAE_LTX_AUDIO_VAE_HPP__
|
||||
#define __SD_MODEL_VAE_LTX_AUDIO_VAE_HPP__
|
||||
|
||||
#include <cmath>
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "core/ggml_extend.hpp"
|
||||
#include "model_loader.h"
|
||||
#include "model_manager.h"
|
||||
|
||||
namespace LTXV {
|
||||
|
||||
@@ -997,13 +998,15 @@ namespace LTXV {
|
||||
struct LTXAudioVAERunner : public GGMLRunner {
|
||||
LTXAudioVAEConfig config;
|
||||
LTXAudioVAE model;
|
||||
std::string weight_prefix;
|
||||
sd::Tensor<float> bwe_skip_filter_tensor;
|
||||
|
||||
LTXAudioVAERunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& prefix = "")
|
||||
: GGMLRunner(backend, params_backend),
|
||||
const std::string& prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: GGMLRunner(backend, weight_manager),
|
||||
weight_prefix(prefix),
|
||||
config(LTXAudioVAEConfig::detect_from_weights(tensor_storage_map)),
|
||||
model(config) {
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
@@ -1013,11 +1016,11 @@ namespace LTXV {
|
||||
}
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) {
|
||||
model.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
||||
model.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() {
|
||||
size_t get_params_mem_size() {
|
||||
return model.get_params_mem_size();
|
||||
}
|
||||
|
||||
@@ -1037,7 +1040,7 @@ namespace LTXV {
|
||||
ggml_build_forward_expand(gf, waveform);
|
||||
return gf;
|
||||
};
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), 4);
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), 4);
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_INFO("ltx audio vae decode completed, taking %.2fs", (t1 - t0) * 1.0f / 1000);
|
||||
return result;
|
||||
@@ -1064,7 +1067,8 @@ namespace LTXV {
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
LOG_INFO("loading ltx audio vae from '%s'", model_path.c_str());
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file(model_path)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", model_path.c_str());
|
||||
return;
|
||||
@@ -1072,20 +1076,17 @@ namespace LTXV {
|
||||
|
||||
auto& tensor_storage_map = model_loader.get_tensor_storage_map();
|
||||
auto ltx_audio_vae = std::make_shared<LTXAudioVAERunner>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
prefix);
|
||||
prefix,
|
||||
model_manager);
|
||||
|
||||
if (!ltx_audio_vae->alloc_params_buffer()) {
|
||||
LOG_ERROR("ltx audio vae buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
ltx_audio_vae->get_param_tensors(tensors, "");
|
||||
|
||||
if (!model_loader.load_tensors(tensors)) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("LTX audio VAE test",
|
||||
*ltx_audio_vae,
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register ltx audio vae tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+25
-27
@@ -957,8 +957,8 @@ namespace LTXVAE {
|
||||
|
||||
ggml_tensor* scaled_timestep = timestep;
|
||||
if (timestep_conditioning) {
|
||||
auto multiplier = ggml_ext_backend_tensor_get_f32(params["timestep_scale_multiplier"]);
|
||||
scaled_timestep = ggml_ext_scale(ctx->ggml_ctx, timestep, multiplier);
|
||||
auto multiplier = ggml_ext_cast_f32(ctx->ggml_ctx, ctx->backend, params["timestep_scale_multiplier"]);
|
||||
scaled_timestep = ggml_mul(ctx->ggml_ctx, timestep, multiplier);
|
||||
}
|
||||
|
||||
x = conv_in->forward(ctx, x, causal_decoder);
|
||||
@@ -1008,8 +1008,8 @@ namespace LTXVAE {
|
||||
|
||||
ggml_tensor* scaled_timestep = timestep;
|
||||
if (timestep_conditioning && timestep != nullptr) {
|
||||
auto multiplier = ggml_ext_backend_tensor_get_f32(params["timestep_scale_multiplier"]);
|
||||
scaled_timestep = ggml_ext_scale(ctx->ggml_ctx, timestep, multiplier);
|
||||
auto multiplier = ggml_ext_cast_f32(ctx->ggml_ctx, ctx->backend, params["timestep_scale_multiplier"]);
|
||||
scaled_timestep = ggml_mul(ctx->ggml_ctx, timestep, multiplier);
|
||||
}
|
||||
|
||||
// conv_in with feat_map for left temporal context
|
||||
@@ -1223,11 +1223,11 @@ struct LTXVideoVAE : public VAE {
|
||||
LTXVAE::VideoVAE vae;
|
||||
|
||||
LTXVideoVAE(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& prefix,
|
||||
bool decode_only = true,
|
||||
SDVersion version = VERSION_LTXAV)
|
||||
bool decode_only = true,
|
||||
SDVersion version = VERSION_LTXAV,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: decode_only(decode_only),
|
||||
ltx_vae_version(LTXVAE::detect_ltx_vae_version(tensor_storage_map, prefix)),
|
||||
timestep_conditioning(LTXVAE::detect_ltx_vae_timestep_conditioning(tensor_storage_map, prefix)),
|
||||
@@ -1239,7 +1239,7 @@ struct LTXVideoVAE : public VAE {
|
||||
patch_size,
|
||||
tensor_storage_map,
|
||||
prefix),
|
||||
VAE(version, backend, params_backend) {
|
||||
VAE(version, backend, prefix, weight_manager) {
|
||||
vae.init(params_ctx, tensor_storage_map, prefix);
|
||||
decode_timestep_tensor.values()[0] = vae.decode_timestep;
|
||||
}
|
||||
@@ -1271,8 +1271,8 @@ struct LTXVideoVAE : public VAE {
|
||||
}
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) override {
|
||||
vae.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
vae.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
struct TemporalTilePlan {
|
||||
@@ -1396,7 +1396,7 @@ struct LTXVideoVAE : public VAE {
|
||||
static_cast<int>(start),
|
||||
chunk_overlap);
|
||||
};
|
||||
auto chunk = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true),
|
||||
auto chunk = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true, true, true),
|
||||
expected_dim);
|
||||
if (chunk.empty()) {
|
||||
free_cache_ctx_and_buffer();
|
||||
@@ -1426,7 +1426,7 @@ struct LTXVideoVAE : public VAE {
|
||||
const sd::Tensor<float>& z,
|
||||
bool decode_graph) override {
|
||||
if (!decode_graph && decode_only) {
|
||||
LOG_ERROR("LTX video VAE encode requires encoder weights; create the context with vae_decode_only=false");
|
||||
LOG_ERROR("LTX video VAE encode requires encoder weights");
|
||||
return {};
|
||||
}
|
||||
sd::Tensor<float> input = z;
|
||||
@@ -1452,7 +1452,7 @@ struct LTXVideoVAE : public VAE {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(input, decode_graph);
|
||||
};
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), expected_dim);
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), expected_dim);
|
||||
if (result.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -1465,7 +1465,7 @@ struct LTXVideoVAE : public VAE {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_latent_statistics_graph(z, normalize);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false),
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false),
|
||||
static_cast<size_t>(z.dim()));
|
||||
}
|
||||
|
||||
@@ -1521,7 +1521,8 @@ struct LTXVideoVAE : public VAE {
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
LOG_INFO("loading ltx vae from '%s'", model_path.c_str());
|
||||
|
||||
ModelLoader model_loader;
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(model_path, "vae.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", model_path.c_str());
|
||||
return;
|
||||
@@ -1529,22 +1530,19 @@ struct LTXVideoVAE : public VAE {
|
||||
|
||||
auto& tensor_storage_map = model_loader.get_tensor_storage_map();
|
||||
std::shared_ptr<LTXVideoVAE> vae = std::make_shared<LTXVideoVAE>(backend,
|
||||
backend,
|
||||
tensor_storage_map,
|
||||
"first_stage_model",
|
||||
true,
|
||||
VERSION_LTXAV);
|
||||
VERSION_LTXAV,
|
||||
model_manager);
|
||||
|
||||
if (!vae->alloc_params_buffer()) {
|
||||
LOG_ERROR("vae buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
vae->get_param_tensors(tensors, "first_stage_model");
|
||||
|
||||
if (!model_loader.load_tensors(tensors)) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("LTX VAE test",
|
||||
*vae,
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register ltx vae tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -623,14 +623,14 @@ struct TinyImageAutoEncoder : public VAE {
|
||||
bool decode_only = false;
|
||||
|
||||
TinyImageAutoEncoder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decoder_only),
|
||||
taesd(decoder_only, version),
|
||||
VAE(version, backend, params_backend) {
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_SD1,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, "tae", weight_manager),
|
||||
decode_only(decoder_only),
|
||||
taesd(decoder_only, version) {
|
||||
scale_input = false;
|
||||
taesd.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
@@ -639,8 +639,8 @@ struct TinyImageAutoEncoder : public VAE {
|
||||
return "taesd";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taesd.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
taesd.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
sd::Tensor<float> vae_output_to_latents(const sd::Tensor<float>& vae_output, std::shared_ptr<RNG> rng) override {
|
||||
@@ -676,7 +676,7 @@ struct TinyImageAutoEncoder : public VAE {
|
||||
return build_graph(z_tensor, decode_graph);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), z_tensor.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), z_tensor.dim());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -686,13 +686,13 @@ struct TinyVideoAutoEncoder : public VAE {
|
||||
bool is_wide = false;
|
||||
|
||||
TinyVideoAutoEncoder(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_WAN2)
|
||||
: decode_only(decoder_only),
|
||||
VAE(version, backend, params_backend) {
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_WAN2,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, "tae", weight_manager),
|
||||
decode_only(decoder_only) {
|
||||
for (auto tensor_storage : tensor_storage_map) {
|
||||
if (tensor_storage.first.find(prefix + ".3.conv.6.weight") != std::string::npos) {
|
||||
is_wide = true;
|
||||
@@ -708,8 +708,8 @@ struct TinyVideoAutoEncoder : public VAE {
|
||||
return "taehv";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) {
|
||||
taehv.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
taehv.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
sd::Tensor<float> vae_output_to_latents(const sd::Tensor<float>& vae_output, std::shared_ptr<RNG> rng) override {
|
||||
@@ -746,7 +746,7 @@ struct TinyVideoAutoEncoder : public VAE {
|
||||
return build_graph(z_tensor, decode_graph);
|
||||
};
|
||||
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), z_tensor.dim());
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false, false, false), z_tensor.dim());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+14
-7
@@ -1,12 +1,14 @@
|
||||
#ifndef __SD_MODEL_VAE_VAE_HPP__
|
||||
#ifndef __SD_MODEL_VAE_VAE_HPP__
|
||||
#define __SD_MODEL_VAE_VAE_HPP__
|
||||
|
||||
#include "core/tensor_ggml.hpp"
|
||||
#include "model/common/block.hpp"
|
||||
#include "model_manager.h"
|
||||
|
||||
struct VAE : public GGMLRunner {
|
||||
protected:
|
||||
SDVersion version;
|
||||
std::string weight_prefix;
|
||||
bool scale_input = true;
|
||||
virtual sd::Tensor<float> _compute(const int n_threads,
|
||||
const sd::Tensor<float>& z,
|
||||
@@ -62,8 +64,11 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
VAE(SDVersion version, ggml_backend_t backend, ggml_backend_t params_backend)
|
||||
: version(version), GGMLRunner(backend, params_backend) {}
|
||||
VAE(SDVersion version,
|
||||
ggml_backend_t backend,
|
||||
const std::string& weight_prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: version(version), weight_prefix(weight_prefix), GGMLRunner(backend, weight_manager) {}
|
||||
|
||||
int get_scale_factor() {
|
||||
int scale_factor = 8;
|
||||
@@ -214,7 +219,7 @@ public:
|
||||
virtual sd::Tensor<float> vae_output_to_latents(const sd::Tensor<float>& vae_output, std::shared_ptr<RNG> rng) = 0;
|
||||
virtual sd::Tensor<float> diffusion_to_vae_latents(const sd::Tensor<float>& latents) = 0;
|
||||
virtual sd::Tensor<float> vae_to_diffusion_latents(const sd::Tensor<float>& latents) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) = 0;
|
||||
virtual void set_conv2d_scale(float scale) { SD_UNUSED(scale); };
|
||||
virtual void set_temporal_tiling_enabled(bool enabled) { SD_UNUSED(enabled); };
|
||||
virtual void set_tiling_params(const sd_tiling_params_t& params) {
|
||||
@@ -223,8 +228,10 @@ public:
|
||||
};
|
||||
|
||||
struct FakeVAE : public VAE {
|
||||
FakeVAE(SDVersion version, ggml_backend_t backend, ggml_backend_t params_backend)
|
||||
: VAE(version, backend, params_backend) {}
|
||||
FakeVAE(SDVersion version,
|
||||
ggml_backend_t backend,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, "", weight_manager) {}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) {
|
||||
return input_channels;
|
||||
@@ -251,7 +258,7 @@ struct FakeVAE : public VAE {
|
||||
return latents;
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) override {}
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "fake_vae";
|
||||
|
||||
+21
-24
@@ -1124,12 +1124,12 @@ namespace WAN {
|
||||
WanVAE ae;
|
||||
|
||||
WanVAERunner(ggml_backend_t backend,
|
||||
ggml_backend_t params_backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
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(version, backend, params_backend) {
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
bool decode_only = false,
|
||||
SDVersion version = VERSION_WAN2,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, prefix, weight_manager), decode_only(decode_only), ae(decode_only, version == VERSION_WAN2_2_TI2V) {
|
||||
ae.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
@@ -1137,8 +1137,8 @@ namespace WAN {
|
||||
return "wan_vae";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string prefix) override {
|
||||
ae.get_param_tensors(tensors, prefix);
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
ae.get_param_tensors(tensors, weight_prefix);
|
||||
}
|
||||
|
||||
sd::Tensor<float> vae_output_to_latents(const sd::Tensor<float>& vae_output, std::shared_ptr<RNG> rng) override {
|
||||
@@ -1255,7 +1255,7 @@ namespace WAN {
|
||||
return build_graph(input, decode_graph);
|
||||
}
|
||||
};
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true),
|
||||
auto result = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true, true, true),
|
||||
input.empty() ? z.dim() : input.dim());
|
||||
if (!result.empty() && z.dim() == 4) {
|
||||
result.squeeze_(2);
|
||||
@@ -1268,7 +1268,7 @@ namespace WAN {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph_partial(z, decode_graph, i);
|
||||
};
|
||||
auto out_opt = GGMLRunner::compute<float>(get_graph, n_threads, true);
|
||||
auto out_opt = GGMLRunner::compute<float>(get_graph, n_threads, true, true, true);
|
||||
if (!out_opt.has_value()) {
|
||||
return {};
|
||||
}
|
||||
@@ -1281,7 +1281,7 @@ namespace WAN {
|
||||
sd::Tensor<float> output = std::move(out);
|
||||
|
||||
for (i = 1; i < t; i++) {
|
||||
auto chunk_opt = GGMLRunner::compute<float>(get_graph, n_threads, true);
|
||||
auto chunk_opt = GGMLRunner::compute<float>(get_graph, n_threads, true, true, true);
|
||||
if (!chunk_opt.has_value()) {
|
||||
return {};
|
||||
}
|
||||
@@ -1327,27 +1327,24 @@ namespace WAN {
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = sd_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
std::shared_ptr<WanVAERunner> vae = std::make_shared<WanVAERunner>(backend, backend, String2TensorStorage{}, "", false, VERSION_WAN2_2_TI2V);
|
||||
auto model_manager = std::make_shared<ModelManager>();
|
||||
std::shared_ptr<WanVAERunner> vae = std::make_shared<WanVAERunner>(backend, String2TensorStorage{}, "first_stage_model", false, VERSION_WAN2_2_TI2V, model_manager);
|
||||
{
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
|
||||
if (!vae->alloc_params_buffer()) {
|
||||
LOG_ERROR("vae buffer allocation failed");
|
||||
return;
|
||||
}
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
vae->get_param_tensors(tensors, "first_stage_model");
|
||||
|
||||
ModelLoader model_loader;
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(file_path, "vae.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
if (!model_manager->register_runner_params("Wan VAE test",
|
||||
*vae,
|
||||
ModelManager::ResidencyMode::ParamBackend,
|
||||
backend,
|
||||
backend) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register wan vae tensors with model manager failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+167
-42
@@ -1,6 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cinttypes>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
@@ -204,10 +205,28 @@ void convert_tensor(void* src,
|
||||
|
||||
/*================================================= ModelLoader ==================================================*/
|
||||
|
||||
ModelLoader::ModelLoader()
|
||||
: n_threads_(sd_get_num_physical_cores()) {
|
||||
}
|
||||
|
||||
size_t ModelLoader::add_file_path(const std::string& file_path) {
|
||||
if (model_files_processed) {
|
||||
file_data.clear();
|
||||
model_files_processed = false;
|
||||
}
|
||||
file_paths_.push_back(file_path);
|
||||
return file_paths_.size() - 1;
|
||||
}
|
||||
|
||||
void ModelLoader::add_tensor_storage(const TensorStorage& tensor_storage) {
|
||||
tensor_storage_map[tensor_storage.name] = tensor_storage;
|
||||
}
|
||||
|
||||
void ModelLoader::set_n_threads(int n_threads) {
|
||||
n_threads_ = n_threads > 0 ? n_threads : sd_get_num_physical_cores();
|
||||
LOG_DEBUG("using %d threads for model loading", n_threads_);
|
||||
}
|
||||
|
||||
bool ModelLoader::init_from_file(const std::string& file_path, const std::string& prefix) {
|
||||
if (is_directory(file_path)) {
|
||||
LOG_INFO("load %s using diffusers format", file_path.c_str());
|
||||
@@ -271,8 +290,7 @@ bool ModelLoader::init_from_gguf_file(const std::string& file_path, const std::s
|
||||
return false;
|
||||
}
|
||||
|
||||
file_paths_.push_back(file_path);
|
||||
size_t file_index = file_paths_.size() - 1;
|
||||
size_t file_index = add_file_path(file_path);
|
||||
|
||||
for (auto& tensor_storage : tensor_storages) {
|
||||
// LOG_DEBUG("%s", tensor_storage.name.c_str());
|
||||
@@ -300,8 +318,7 @@ bool ModelLoader::init_from_safetensors_file(const std::string& file_path, const
|
||||
return false;
|
||||
}
|
||||
|
||||
file_paths_.push_back(file_path);
|
||||
size_t file_index = file_paths_.size() - 1;
|
||||
size_t file_index = add_file_path(file_path);
|
||||
|
||||
for (auto& tensor_storage : tensor_storages) {
|
||||
if (is_unused_tensor(tensor_storage.name)) {
|
||||
@@ -335,8 +352,7 @@ bool ModelLoader::init_from_torch_legacy_file(const std::string& file_path, cons
|
||||
return false;
|
||||
}
|
||||
|
||||
file_paths_.push_back(file_path);
|
||||
size_t file_index = file_paths_.size() - 1;
|
||||
size_t file_index = add_file_path(file_path);
|
||||
|
||||
for (auto& tensor_storage : tensor_storages) {
|
||||
if (is_unused_tensor(tensor_storage.name)) {
|
||||
@@ -366,8 +382,7 @@ bool ModelLoader::init_from_torch_zip_file(const std::string& file_path, const s
|
||||
return false;
|
||||
}
|
||||
|
||||
file_paths_.push_back(file_path);
|
||||
size_t file_index = file_paths_.size() - 1;
|
||||
size_t file_index = add_file_path(file_path);
|
||||
|
||||
for (auto& tensor_storage : tensor_storages) {
|
||||
if (!starts_with(tensor_storage.name, prefix)) {
|
||||
@@ -760,8 +775,6 @@ void ModelLoader::process_model_files(bool enable_mmap, bool writable_mmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t start_time = ggml_time_ms();
|
||||
|
||||
std::vector<TensorStorage> processed_tensor_storages;
|
||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||
if (is_unused_tensor(tensor_storage.name)) {
|
||||
@@ -812,20 +825,12 @@ void ModelLoader::process_model_files(bool enable_mmap, bool writable_mmap) {
|
||||
} else {
|
||||
LOG_WARN("failed to memory-map '%s' (falling back to read())", file_path.c_str());
|
||||
}
|
||||
} else if (!is_zip) {
|
||||
LOG_INFO("NOT using mmap for '%s' (mmap disabled by caller)",
|
||||
file_path.c_str());
|
||||
}
|
||||
|
||||
file_data.push_back(std::move(fdata));
|
||||
}
|
||||
|
||||
model_files_processed = true;
|
||||
|
||||
int64_t end_time = ggml_time_ms();
|
||||
int64_t process_time_ms = end_time - start_time;
|
||||
|
||||
LOG_INFO("model files processing completed in %.2fs", process_time_ms / 1000.f);
|
||||
}
|
||||
|
||||
std::vector<MmapTensorStore> ModelLoader::mmap_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||
@@ -919,7 +924,9 @@ std::vector<MmapTensorStore> ModelLoader::mmap_tensors(std::map<std::string, ggm
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads_p, bool enable_mmap) {
|
||||
bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb,
|
||||
bool enable_mmap,
|
||||
const std::set<std::string>* target_tensor_names) {
|
||||
process_model_files(enable_mmap, false);
|
||||
|
||||
std::atomic<int64_t> read_time_ms(0);
|
||||
@@ -928,14 +935,26 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
std::atomic<int64_t> convert_time_ms(0);
|
||||
std::atomic<uint64_t> bytes_processed(0);
|
||||
|
||||
int num_threads_to_use = n_threads_p > 0 ? n_threads_p : sd_get_num_physical_cores();
|
||||
LOG_DEBUG("using %d threads for model loading", num_threads_to_use);
|
||||
int num_threads_to_use = n_threads_;
|
||||
|
||||
int64_t start_time = ggml_time_ms();
|
||||
|
||||
size_t total_tensors_to_process = 0;
|
||||
std::vector<size_t> file_tensors_to_process_counts;
|
||||
file_tensors_to_process_counts.reserve(file_data.size());
|
||||
for (const auto& fdata : file_data) {
|
||||
total_tensors_to_process += fdata.tensors.size();
|
||||
size_t file_tensors_to_process = 0;
|
||||
if (target_tensor_names == nullptr) {
|
||||
file_tensors_to_process = fdata.tensors.size();
|
||||
} else {
|
||||
for (const TensorStorage& tensor_storage : fdata.tensors) {
|
||||
if (target_tensor_names->find(tensor_storage.name) != target_tensor_names->end()) {
|
||||
file_tensors_to_process++;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_tensors_to_process_counts.push_back(file_tensors_to_process);
|
||||
total_tensors_to_process += file_tensors_to_process;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
@@ -943,17 +962,38 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
const int64_t t_start = start_time;
|
||||
int last_n_threads = 1;
|
||||
|
||||
for (auto& fdata : file_data) {
|
||||
for (size_t file_index = 0; file_index < file_data.size(); ++file_index) {
|
||||
auto& fdata = file_data[file_index];
|
||||
const std::string& file_path = fdata.path;
|
||||
LOG_DEBUG("loading tensors from %s", file_path.c_str());
|
||||
|
||||
const std::vector<TensorStorage>& file_tensors = fdata.tensors;
|
||||
std::vector<const TensorStorage*> tensors_to_process;
|
||||
size_t file_tensors_to_process = file_tensors_to_process_counts[file_index];
|
||||
tensors_to_process.reserve(file_tensors_to_process);
|
||||
if (target_tensor_names == nullptr) {
|
||||
for (const TensorStorage& tensor_storage : file_tensors) {
|
||||
tensors_to_process.push_back(&tensor_storage);
|
||||
}
|
||||
} else {
|
||||
for (const TensorStorage& tensor_storage : file_tensors) {
|
||||
if (target_tensor_names->find(tensor_storage.name) != target_tensor_names->end()) {
|
||||
tensors_to_process.push_back(&tensor_storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tensors_to_process.empty()) {
|
||||
continue;
|
||||
}
|
||||
LOG_DEBUG("loading %zu/%zu tensors from %s",
|
||||
tensors_to_process.size(),
|
||||
file_tensors.size(),
|
||||
file_path.c_str());
|
||||
|
||||
bool is_zip = fdata.is_zip;
|
||||
|
||||
std::shared_ptr<MmapWrapper> mmapped = fdata.mmapped;
|
||||
|
||||
int n_threads = is_zip ? 1 : std::min(num_threads_to_use, (int)file_tensors.size());
|
||||
int n_threads = is_zip ? 1 : std::min(num_threads_to_use, (int)tensors_to_process.size());
|
||||
if (n_threads < 1) {
|
||||
n_threads = 1;
|
||||
}
|
||||
@@ -962,6 +1002,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
std::atomic<size_t> tensor_idx(0);
|
||||
std::atomic<bool> failed(false);
|
||||
std::vector<std::thread> workers;
|
||||
std::mutex rpc_backend_mutex;
|
||||
|
||||
for (int i = 0; i < n_threads; ++i) {
|
||||
workers.emplace_back([&, file_path, is_zip]() {
|
||||
@@ -989,11 +1030,11 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
while (true) {
|
||||
int64_t t0, t1;
|
||||
size_t idx = tensor_idx.fetch_add(1);
|
||||
if (idx >= file_tensors.size() || failed) {
|
||||
if (idx >= tensors_to_process.size() || failed) {
|
||||
break;
|
||||
}
|
||||
|
||||
const TensorStorage& tensor_storage = file_tensors[idx];
|
||||
const TensorStorage& tensor_storage = *tensors_to_process[idx];
|
||||
ggml_tensor* dst_tensor = nullptr;
|
||||
|
||||
t0 = ggml_time_ms();
|
||||
@@ -1118,7 +1159,19 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
|
||||
if (dst_tensor->buffer != nullptr && !ggml_backend_buffer_is_host(dst_tensor->buffer)) {
|
||||
t0 = ggml_time_ms();
|
||||
ggml_backend_tensor_set(dst_tensor, convert_buf, 0, ggml_nbytes(dst_tensor));
|
||||
|
||||
// RPC backends require serialized access to prevent concurrency issues
|
||||
const char* buffer_type_name = ggml_backend_buft_name(ggml_backend_buffer_get_type(dst_tensor->buffer));
|
||||
bool is_rpc_buffer = buffer_type_name != nullptr &&
|
||||
std::string(buffer_type_name).find("RPC") != std::string::npos;
|
||||
|
||||
if (is_rpc_buffer) {
|
||||
std::lock_guard<std::mutex> lock(rpc_backend_mutex);
|
||||
ggml_backend_tensor_set(dst_tensor, convert_buf, 0, ggml_nbytes(dst_tensor));
|
||||
} else {
|
||||
ggml_backend_tensor_set(dst_tensor, convert_buf, 0, ggml_nbytes(dst_tensor));
|
||||
}
|
||||
|
||||
t1 = ggml_time_ms();
|
||||
copy_to_backend_time_ms.fetch_add(t1 - t0);
|
||||
}
|
||||
@@ -1133,16 +1186,18 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
|
||||
while (true) {
|
||||
size_t current_idx = tensor_idx.load();
|
||||
if (current_idx >= file_tensors.size() || failed) {
|
||||
if (current_idx >= tensors_to_process.size() || failed) {
|
||||
break;
|
||||
}
|
||||
size_t curr_num = total_tensors_processed + current_idx;
|
||||
float elapsed_seconds = (ggml_time_ms() - t_start) / 1000.0f;
|
||||
pretty_bytes_progress(static_cast<int>(curr_num),
|
||||
static_cast<int>(total_tensors_to_process),
|
||||
bytes_processed.load(),
|
||||
elapsed_seconds);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
if (total_tensors_to_process > 0) {
|
||||
pretty_bytes_progress(static_cast<int>(curr_num),
|
||||
static_cast<int>(total_tensors_to_process),
|
||||
bytes_processed.load(),
|
||||
elapsed_seconds);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(total_tensors_to_process <= 4 ? 10 : 200));
|
||||
}
|
||||
|
||||
for (auto& w : workers) {
|
||||
@@ -1153,12 +1208,14 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
total_tensors_processed += file_tensors.size();
|
||||
pretty_bytes_progress(static_cast<int>(total_tensors_processed),
|
||||
static_cast<int>(total_tensors_to_process),
|
||||
bytes_processed.load(),
|
||||
(ggml_time_ms() - t_start) / 1000.0f);
|
||||
if (total_tensors_processed < total_tensors_to_process) {
|
||||
total_tensors_processed += tensors_to_process.size();
|
||||
if (total_tensors_to_process > 0) {
|
||||
pretty_bytes_progress(static_cast<int>(total_tensors_processed),
|
||||
static_cast<int>(total_tensors_to_process),
|
||||
bytes_processed.load(),
|
||||
(ggml_time_ms() - t_start) / 1000.0f);
|
||||
}
|
||||
if (total_tensors_processed < total_tensors_to_process && total_tensors_to_process > 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -1173,9 +1230,77 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ModelLoader::load_float_tensor(const std::string& name,
|
||||
std::vector<float>& data,
|
||||
int n_threads,
|
||||
bool use_mmap) {
|
||||
data.clear();
|
||||
|
||||
auto tensor_storage_it = tensor_storage_map.find(name);
|
||||
if (tensor_storage_it == tensor_storage_map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const TensorStorage& tensor_storage = tensor_storage_it->second;
|
||||
int64_t n_elements = tensor_storage.nelements();
|
||||
if (n_elements <= 0) {
|
||||
LOG_ERROR("tensor '%s' has invalid element count: %" PRId64, name.c_str(), n_elements);
|
||||
return false;
|
||||
}
|
||||
if (tensor_storage.n_dims <= 0 || tensor_storage.n_dims > GGML_MAX_DIMS) {
|
||||
LOG_ERROR("tensor '%s' has unsupported dims: %d", name.c_str(), tensor_storage.n_dims);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<float> loaded_data(static_cast<size_t>(n_elements));
|
||||
ggml_init_params params;
|
||||
params.mem_size = ggml_tensor_overhead();
|
||||
params.mem_buffer = nullptr;
|
||||
params.no_alloc = true;
|
||||
|
||||
ggml_context* ctx = ggml_init(params);
|
||||
if (ctx == nullptr) {
|
||||
LOG_ERROR("failed to create context for tensor '%s'", name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ggml_tensor* tensor = ggml_new_tensor(ctx, GGML_TYPE_F32, tensor_storage.n_dims, tensor_storage.ne);
|
||||
ggml_set_name(tensor, name.c_str());
|
||||
tensor->data = loaded_data.data();
|
||||
|
||||
bool loaded = false;
|
||||
auto on_new_tensor_cb = [&](const TensorStorage& current_tensor_storage, ggml_tensor** dst_tensor) -> bool {
|
||||
*dst_tensor = nullptr;
|
||||
if (current_tensor_storage.name != name) {
|
||||
return true;
|
||||
}
|
||||
if (current_tensor_storage.nelements() != n_elements) {
|
||||
LOG_ERROR("tensor '%s' element count changed during load", name.c_str());
|
||||
return false;
|
||||
}
|
||||
*dst_tensor = tensor;
|
||||
loaded = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
std::set<std::string> target_tensor_names{name};
|
||||
if (n_threads > 0) {
|
||||
set_n_threads(n_threads);
|
||||
}
|
||||
bool success = load_tensors(on_new_tensor_cb, use_mmap, &target_tensor_names);
|
||||
ggml_free(ctx);
|
||||
|
||||
if (!success || !loaded) {
|
||||
data.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
data = std::move(loaded_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelLoader::load_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||
std::set<std::string> ignore_tensors,
|
||||
int n_threads,
|
||||
bool enable_mmap) {
|
||||
std::set<std::string> tensor_names_in_file;
|
||||
std::mutex tensor_names_mutex;
|
||||
@@ -1219,7 +1344,7 @@ bool ModelLoader::load_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||
return true;
|
||||
};
|
||||
|
||||
bool success = load_tensors(on_new_tensor_cb, n_threads, enable_mmap);
|
||||
bool success = load_tensors(on_new_tensor_cb, enable_mmap);
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from file failed");
|
||||
return false;
|
||||
|
||||
+13
-2
@@ -34,7 +34,9 @@ protected:
|
||||
std::vector<ModelFileData> file_data;
|
||||
bool model_files_processed = false;
|
||||
String2TensorStorage tensor_storage_map;
|
||||
int n_threads_;
|
||||
|
||||
size_t add_file_path(const std::string& file_path);
|
||||
void add_tensor_storage(const TensorStorage& tensor_storage);
|
||||
|
||||
bool init_from_gguf_file(const std::string& file_path, const std::string& prefix = "");
|
||||
@@ -44,6 +46,8 @@ protected:
|
||||
bool init_from_diffusers_file(const std::string& file_path, const std::string& prefix = "");
|
||||
|
||||
public:
|
||||
ModelLoader();
|
||||
|
||||
bool init_from_file(const std::string& file_path, const std::string& prefix = "");
|
||||
void convert_tensors_name();
|
||||
bool init_from_file_and_convert_name(const std::string& file_path,
|
||||
@@ -55,16 +59,23 @@ public:
|
||||
std::map<ggml_type, uint32_t> get_diffusion_model_wtype_stat();
|
||||
std::map<ggml_type, uint32_t> get_vae_wtype_stat();
|
||||
String2TensorStorage& get_tensor_storage_map() { return tensor_storage_map; }
|
||||
const String2TensorStorage& get_tensor_storage_map() const { return tensor_storage_map; }
|
||||
void set_n_threads(int n_threads);
|
||||
void set_wtype_override(ggml_type wtype, std::string tensor_type_rules = "");
|
||||
void process_model_files(bool enable_mmap = false, bool writable_mmap = true);
|
||||
std::vector<MmapTensorStore> mmap_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||
std::set<std::string> ignore_tensors = {},
|
||||
bool writable = true);
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0, bool use_mmap = false);
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb,
|
||||
bool use_mmap = false,
|
||||
const std::set<std::string>* target_tensor_names = nullptr);
|
||||
bool load_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||
std::set<std::string> ignore_tensors = {},
|
||||
int n_threads = 0,
|
||||
bool use_mmap = false);
|
||||
bool load_float_tensor(const std::string& name,
|
||||
std::vector<float>& data,
|
||||
int n_threads = 0,
|
||||
bool use_mmap = false);
|
||||
|
||||
std::vector<std::string> get_tensor_names() const {
|
||||
std::vector<std::string> names;
|
||||
|
||||
@@ -0,0 +1,944 @@
|
||||
#include "model_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "core/ggml_extend_backend.h"
|
||||
#include "core/util.h"
|
||||
#include "model/adapter/lora.hpp"
|
||||
|
||||
static size_t aligned_offset(const void* buffer, size_t offset, size_t alignment) {
|
||||
GGML_ASSERT(alignment != 0 && (alignment & (alignment - 1)) == 0);
|
||||
size_t align = (alignment - ((reinterpret_cast<uintptr_t>(buffer) + offset) % alignment)) % alignment;
|
||||
return offset + align;
|
||||
}
|
||||
|
||||
static bool lora_specs_equal(const std::vector<ModelManager::LoraSpec>& lhs,
|
||||
const std::vector<ModelManager::LoraSpec>& rhs) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < lhs.size(); ++i) {
|
||||
if (lhs[i].path != rhs[i].path ||
|
||||
lhs[i].multiplier != rhs[i].multiplier ||
|
||||
lhs[i].is_high_noise != rhs[i].is_high_noise ||
|
||||
lhs[i].tensor_name_prefix_filter != rhs[i].tensor_name_prefix_filter ||
|
||||
lhs[i].required != rhs[i].required) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string lora_id(const ModelManager::LoraSpec& lora) {
|
||||
return lora.is_high_noise ? "|high_noise|" + lora.path : lora.path;
|
||||
}
|
||||
|
||||
static bool backend_supports_host_buffer(ggml_backend_t backend) {
|
||||
if (backend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (sd_backend_is_cpu(backend)) {
|
||||
return true;
|
||||
}
|
||||
ggml_backend_dev_t dev = ggml_backend_get_device(backend);
|
||||
if (dev == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ggml_backend_dev_props props;
|
||||
ggml_backend_dev_get_props(dev, &props);
|
||||
return props.caps.buffer_from_host_ptr;
|
||||
}
|
||||
|
||||
ModelManager::~ModelManager() {
|
||||
release_all();
|
||||
}
|
||||
|
||||
void ModelManager::set_common_ignore_tensors(std::set<std::string> ignore_tensors) {
|
||||
common_ignore_tensors_ = std::move(ignore_tensors);
|
||||
}
|
||||
|
||||
void ModelManager::set_loras(std::vector<LoraSpec> loras, SDVersion version) {
|
||||
if (loras.empty() && loras_.empty()) {
|
||||
lora_version_ = version;
|
||||
return;
|
||||
}
|
||||
if (lora_version_ == version && lora_specs_equal(loras_, loras)) {
|
||||
return;
|
||||
}
|
||||
|
||||
loras_ = std::move(loras);
|
||||
lora_version_ = version;
|
||||
current_lora_epoch_++;
|
||||
reset_lora_applied_params();
|
||||
}
|
||||
|
||||
std::set<std::string> ModelManager::tensor_names() const {
|
||||
std::set<std::string> names;
|
||||
for (const auto& state : tensor_states_) {
|
||||
if (state != nullptr) {
|
||||
names.insert(state->name);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
size_t estimate_tensors_size(const std::map<std::string, ggml_tensor*>& tensors) {
|
||||
size_t size = 0;
|
||||
std::unordered_set<ggml_tensor*> seen;
|
||||
for (const auto& pair : tensors) {
|
||||
ggml_tensor* tensor = pair.second;
|
||||
if (tensor == nullptr || seen.find(tensor) != seen.end()) {
|
||||
continue;
|
||||
}
|
||||
seen.insert(tensor);
|
||||
size += ggml_nbytes(tensor);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool ModelManager::register_param_tensors(const std::string& desc,
|
||||
std::map<std::string, ggml_tensor*> tensors,
|
||||
ResidencyMode residency_mode,
|
||||
ggml_backend_t compute_backend,
|
||||
ggml_backend_t params_backend,
|
||||
size_t* registered_tensor_size) {
|
||||
if (desc.empty()) {
|
||||
LOG_ERROR("model manager tensor desc is empty");
|
||||
return false;
|
||||
}
|
||||
if (registered_tensor_size != nullptr) {
|
||||
*registered_tensor_size += estimate_tensors_size(tensors);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<TensorState>> new_states;
|
||||
new_states.reserve(tensors.size());
|
||||
|
||||
for (const auto& pair : tensors) {
|
||||
const std::string& name = pair.first;
|
||||
ggml_tensor* tensor = pair.second;
|
||||
if (tensor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (tensor_states_by_name_.find(name) != tensor_states_by_name_.end()) {
|
||||
LOG_ERROR("model manager tensor name '%s' is already registered", name.c_str());
|
||||
return false;
|
||||
}
|
||||
ggml_set_name(tensor, name.c_str());
|
||||
|
||||
auto state = std::make_unique<TensorState>();
|
||||
state->name = name;
|
||||
state->tensor = tensor;
|
||||
state->desc = desc;
|
||||
state->residency_mode = residency_mode;
|
||||
state->compute_backend = compute_backend;
|
||||
state->params_backend = params_backend;
|
||||
new_states.push_back(std::move(state));
|
||||
}
|
||||
|
||||
for (auto& state : new_states) {
|
||||
TensorState* registered_state = state.get();
|
||||
tensor_states_by_name_[registered_state->name] = registered_state;
|
||||
tensor_states_.push_back(std::move(state));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::validate_registered_tensors() {
|
||||
bool ok = true;
|
||||
for (const auto& state : tensor_states_) {
|
||||
if (state == nullptr) {
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
bool state_ok = validate_tensor(*state);
|
||||
if (state_ok) {
|
||||
state->metadata_validated = true;
|
||||
}
|
||||
ok = state_ok && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ModelManager::load_tensors_to_params_backend(const std::vector<TensorState*>& states) {
|
||||
std::vector<TensorState*> need_load;
|
||||
need_load.reserve(states.size());
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || should_ignore(*state) || is_optional_missing_tensor(state->name)) {
|
||||
continue;
|
||||
}
|
||||
if (!state->metadata_validated) {
|
||||
if (!validate_tensor(*state)) {
|
||||
return false;
|
||||
}
|
||||
state->metadata_validated = true;
|
||||
}
|
||||
if (!state->loaded_to_params_backend) {
|
||||
need_load.push_back(state);
|
||||
}
|
||||
}
|
||||
if (need_load.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<ParamsStorageBlock*> created_storage_blocks;
|
||||
if (!mmap_params(need_load, created_storage_blocks)) {
|
||||
for (ParamsStorageBlock* block : created_storage_blocks) {
|
||||
if (block != nullptr) {
|
||||
free_params_storage_block(*block);
|
||||
erase_params_storage_block(block);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<TensorState*> need_alloc;
|
||||
need_alloc.reserve(need_load.size());
|
||||
for (TensorState* state : need_load) {
|
||||
if (state->tensor != nullptr && state->tensor->data == nullptr && state->tensor->view_src == nullptr) {
|
||||
need_alloc.push_back(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!alloc_params_buffers(need_alloc, created_storage_blocks) ||
|
||||
!load_tensors(need_load)) {
|
||||
for (ParamsStorageBlock* block : created_storage_blocks) {
|
||||
if (block != nullptr) {
|
||||
free_params_storage_block(*block);
|
||||
erase_params_storage_block(block);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (ParamsStorageBlock* block : created_storage_blocks) {
|
||||
if (block != nullptr && block->buffer != nullptr) {
|
||||
LOG_DEBUG("model manager prepared params backend buffer (%6.2f MB, %zu tensors, %s)",
|
||||
ggml_backend_buffer_get_size(block->buffer) / (1024.f * 1024.f),
|
||||
block->states.size(),
|
||||
ggml_backend_buffer_is_host(block->buffer) ? "RAM" : "VRAM");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::stage_tensors_to_compute_backend(const std::vector<TensorState*>& states) {
|
||||
std::map<ggml_backend_t, std::vector<TensorState*>> states_by_compute_backend;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || should_ignore(*state) || is_optional_missing_tensor(state->name)) {
|
||||
continue;
|
||||
}
|
||||
if (state->compute_backend == nullptr) {
|
||||
LOG_ERROR("model manager compute backend is null for tensor '%s'", state->name.c_str());
|
||||
return false;
|
||||
}
|
||||
if (state->params_backend == nullptr) {
|
||||
LOG_ERROR("model manager params backend is null for tensor '%s'", state->name.c_str());
|
||||
return false;
|
||||
}
|
||||
if (state->compute_backend == state->params_backend || state->staged_to_compute_backend) {
|
||||
continue;
|
||||
}
|
||||
if (!state->loaded_to_params_backend || state->tensor == nullptr || state->tensor->data == nullptr) {
|
||||
LOG_ERROR("model manager tensor '%s' is not loaded to params backend", state->name.c_str());
|
||||
return false;
|
||||
}
|
||||
states_by_compute_backend[state->compute_backend].push_back(state);
|
||||
}
|
||||
|
||||
for (const auto& pair : states_by_compute_backend) {
|
||||
ggml_backend_t compute_backend = pair.first;
|
||||
const std::vector<TensorState*>& states = pair.second;
|
||||
if (states.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
|
||||
ggml_init_params init_params;
|
||||
init_params.mem_size = std::max<size_t>(1, states.size()) * ggml_tensor_overhead();
|
||||
init_params.mem_buffer = nullptr;
|
||||
init_params.no_alloc = true;
|
||||
|
||||
ggml_context* staging_ctx = ggml_init(init_params);
|
||||
GGML_ASSERT(staging_ctx != nullptr);
|
||||
|
||||
std::vector<std::pair<TensorState*, ggml_tensor*>> staged_tensors;
|
||||
staged_tensors.reserve(states.size());
|
||||
for (TensorState* state : states) {
|
||||
ggml_tensor* staging_tensor = ggml_dup_tensor(staging_ctx, state->tensor);
|
||||
ggml_set_name(staging_tensor, state->tensor->name);
|
||||
staged_tensors.push_back({state, staging_tensor});
|
||||
}
|
||||
|
||||
ggml_backend_buffer_t compute_buffer = ggml_backend_alloc_ctx_tensors(staging_ctx, compute_backend);
|
||||
if (compute_buffer == nullptr) {
|
||||
LOG_ERROR("model manager alloc compute params backend buffer failed, num_tensors = %zu",
|
||||
staged_tensors.size());
|
||||
ggml_free(staging_ctx);
|
||||
return false;
|
||||
}
|
||||
ggml_backend_buffer_set_usage(compute_buffer, GGML_BACKEND_BUFFER_USAGE_WEIGHTS);
|
||||
|
||||
for (auto& staged_tensor : staged_tensors) {
|
||||
TensorState* state = staged_tensor.first;
|
||||
ggml_tensor* managed_tensor = state->tensor;
|
||||
ggml_tensor* staging_tensor = staged_tensor.second;
|
||||
ggml_backend_tensor_copy(managed_tensor, staging_tensor);
|
||||
std::swap(managed_tensor->buffer, staging_tensor->buffer);
|
||||
std::swap(managed_tensor->data, staging_tensor->data);
|
||||
std::swap(managed_tensor->extra, staging_tensor->extra);
|
||||
}
|
||||
ggml_backend_synchronize(compute_backend);
|
||||
|
||||
auto block = std::make_unique<ComputeStagingBlock>();
|
||||
block->compute_backend = compute_backend;
|
||||
block->buffer = compute_buffer;
|
||||
block->staging_ctx = staging_ctx;
|
||||
block->staged_tensors = std::move(staged_tensors);
|
||||
for (auto& staged_tensor : block->staged_tensors) {
|
||||
TensorState* state = staged_tensor.first;
|
||||
state->staged_to_compute_backend = true;
|
||||
}
|
||||
compute_staging_blocks_.push_back(std::move(block));
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("model manager staged compute params (%6.2f MB, %zu tensors) to %s, taking %.2fs",
|
||||
ggml_backend_buffer_get_size(compute_buffer) / (1024.f * 1024.f),
|
||||
states.size(),
|
||||
ggml_backend_name(compute_backend),
|
||||
(t1 - t0) * 1.0f / 1000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::apply_loras_to_params(const std::vector<TensorState*>& states) {
|
||||
if (loras_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct LoraApplyGroup {
|
||||
std::map<std::string, ggml_tensor*> model_tensors;
|
||||
std::vector<TensorState*> states;
|
||||
};
|
||||
|
||||
std::map<ggml_backend_t, LoraApplyGroup> groups;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || state->tensor == nullptr ||
|
||||
should_ignore(*state) || is_optional_missing_tensor(state->name)) {
|
||||
continue;
|
||||
}
|
||||
if (state->applied_lora_epoch == current_lora_epoch_) {
|
||||
continue;
|
||||
}
|
||||
if (state->compute_backend == nullptr) {
|
||||
LOG_ERROR("model manager compute backend is null for lora target tensor '%s'", state->name.c_str());
|
||||
return false;
|
||||
}
|
||||
if (state->tensor->data == nullptr) {
|
||||
LOG_ERROR("model manager lora target tensor '%s' is not prepared", state->name.c_str());
|
||||
return false;
|
||||
}
|
||||
LoraApplyGroup& group = groups[state->compute_backend];
|
||||
group.model_tensors[state->name] = state->tensor;
|
||||
group.states.push_back(state);
|
||||
}
|
||||
|
||||
if (groups.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::set<std::string> all_tensor_names = tensor_names();
|
||||
for (auto& group_pair : groups) {
|
||||
ggml_backend_t compute_backend = group_pair.first;
|
||||
LoraApplyGroup& group = group_pair.second;
|
||||
for (const LoraSpec& lora_spec : loras_) {
|
||||
if (group.model_tensors.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string id = lora_id(lora_spec);
|
||||
auto lora = std::make_shared<LoraModel>(id,
|
||||
compute_backend,
|
||||
compute_backend,
|
||||
lora_spec.path,
|
||||
lora_spec.is_high_noise ? "model.high_noise_" : "",
|
||||
lora_version_);
|
||||
|
||||
LoraModel::filter_t lora_tensor_filter = nullptr;
|
||||
if (!lora_spec.tensor_name_prefix_filter.empty()) {
|
||||
lora_tensor_filter = [&](const std::string& tensor_name) {
|
||||
return starts_with(tensor_name, lora_spec.tensor_name_prefix_filter);
|
||||
};
|
||||
}
|
||||
if (!lora->load_from_file(n_threads_, lora_tensor_filter)) {
|
||||
LOG_WARN("load lora tensors from %s failed", lora_spec.path.c_str());
|
||||
if (lora_spec.required) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (lora->lora_tensors.empty()) {
|
||||
if (lora_spec.required) {
|
||||
LOG_ERROR("required lora has no tensors: %s", lora_spec.path.c_str());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
lora->multiplier = lora_spec.multiplier;
|
||||
lora->apply(group.model_tensors, all_tensor_names, lora_version_, n_threads_, false);
|
||||
lora->release_loaded_tensors();
|
||||
}
|
||||
|
||||
for (TensorState* state : group.states) {
|
||||
if (state != nullptr) {
|
||||
state->applied_lora_epoch = current_lora_epoch_;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelManager::reset_lora_applied_params() {
|
||||
release_compute_staging_blocks(true);
|
||||
release_params_storage_blocks(true);
|
||||
for (auto& state : tensor_states_) {
|
||||
state->applied_lora_epoch = UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelManager::should_ignore(const TensorState& state) const {
|
||||
for (const auto& ignore_prefix : common_ignore_tensors_) {
|
||||
if (starts_with(state.name, ignore_prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelManager::is_optional_missing_tensor(const std::string& name) const {
|
||||
return name.find("cond_stage_model.transformer.text_model.encoder.layers.23") != std::string::npos ||
|
||||
name.find("alphas_cumprod") != std::string::npos;
|
||||
}
|
||||
|
||||
bool ModelManager::validate_tensor(const TensorState& state) const {
|
||||
if (state.tensor == nullptr || should_ignore(state) || is_optional_missing_tensor(state.name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& tensor_storage_map = model_loader_.get_tensor_storage_map();
|
||||
auto ts_it = tensor_storage_map.find(state.name);
|
||||
if (ts_it == tensor_storage_map.end()) {
|
||||
LOG_ERROR("%s tensor '%s' not in model metadata", state.desc.c_str(), state.name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const TensorStorage& tensor_storage = ts_it->second;
|
||||
if (state.tensor->ne[0] != tensor_storage.ne[0] ||
|
||||
state.tensor->ne[1] != tensor_storage.ne[1] ||
|
||||
state.tensor->ne[2] != tensor_storage.ne[2] ||
|
||||
state.tensor->ne[3] != tensor_storage.ne[3]) {
|
||||
LOG_ERROR(
|
||||
"%s tensor '%s' has wrong shape in model metadata: got [%d, %d, %d, %d], expected [%d, %d, %d, %d]",
|
||||
state.desc.c_str(),
|
||||
state.name.c_str(),
|
||||
(int)tensor_storage.ne[0], (int)tensor_storage.ne[1], (int)tensor_storage.ne[2], (int)tensor_storage.ne[3],
|
||||
(int)state.tensor->ne[0], (int)state.tensor->ne[1], (int)state.tensor->ne[2], (int)state.tensor->ne[3]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::mmap_params(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks) {
|
||||
std::map<std::string, ggml_tensor*> mmap_candidates;
|
||||
std::map<std::string, TensorState*> mmap_states;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || !can_mmap_storage(*state) || state->tensor == nullptr ||
|
||||
state->tensor->data != nullptr || state->tensor->view_src != nullptr) {
|
||||
continue;
|
||||
}
|
||||
mmap_candidates[state->name] = state->tensor;
|
||||
mmap_states[state->name] = state;
|
||||
}
|
||||
if (mmap_candidates.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto mmap_store = model_loader_.mmap_tensors(mmap_candidates, {}, true);
|
||||
if (mmap_store.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto block = std::make_unique<ParamsStorageBlock>();
|
||||
block->mmap_tensor_stores = std::move(mmap_store);
|
||||
ParamsStorageBlock* raw = block.get();
|
||||
for (const auto& pair : mmap_states) {
|
||||
TensorState* state = pair.second;
|
||||
if (state != nullptr && state->tensor != nullptr && state->tensor->data != nullptr) {
|
||||
block->states.push_back(state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!block->states.empty()) {
|
||||
params_storage_blocks_.push_back(std::move(block));
|
||||
created_storage_blocks.push_back(raw);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::can_mmap_storage(const TensorState& state) const {
|
||||
if (!enable_mmap_ || state.residency_mode != ResidencyMode::ParamBackend) {
|
||||
return false;
|
||||
}
|
||||
if (state.compute_backend == nullptr || state.params_backend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return sd_backend_is_cpu(state.compute_backend) ||
|
||||
sd_backend_is_cpu(state.params_backend) ||
|
||||
backend_supports_host_buffer(state.compute_backend);
|
||||
}
|
||||
|
||||
bool ModelManager::alloc_params_buffers(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks) {
|
||||
std::map<std::pair<ggml_backend_buffer_type_t, int>, std::vector<TensorState*>> states_by_buffer_type;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || state->tensor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ggml_backend_buffer_type_t params_buft = params_buffer_type_for(*state);
|
||||
if (params_buft == nullptr) {
|
||||
return false;
|
||||
}
|
||||
states_by_buffer_type[{params_buft, static_cast<int>(state->residency_mode)}].push_back(state);
|
||||
}
|
||||
|
||||
for (const auto& pair : states_by_buffer_type) {
|
||||
ggml_backend_buffer_type_t params_buft = pair.first.first;
|
||||
const std::vector<TensorState*>& states = pair.second;
|
||||
size_t alignment = ggml_backend_buft_get_alignment(params_buft);
|
||||
size_t max_size = ggml_backend_buft_get_max_size(params_buft);
|
||||
|
||||
auto alloc_chunk = [&](const std::vector<TensorState*>& chunk, size_t chunk_size) -> bool {
|
||||
if (chunk.empty() || chunk_size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ggml_backend_buffer_t buffer = ggml_backend_buft_alloc_buffer(params_buft, chunk_size);
|
||||
if (buffer == nullptr) {
|
||||
LOG_ERROR("model manager alloc params backend buffer failed, size = %.2fMB",
|
||||
chunk_size / (1024.0 * 1024.0));
|
||||
return false;
|
||||
}
|
||||
ggml_backend_buffer_set_usage(buffer, GGML_BACKEND_BUFFER_USAGE_WEIGHTS);
|
||||
|
||||
std::vector<ggml_tensor*> initialized_tensors;
|
||||
void* base = ggml_backend_buffer_get_base(buffer);
|
||||
size_t offset = aligned_offset(base, 0, ggml_backend_buffer_get_alignment(buffer));
|
||||
for (TensorState* state : chunk) {
|
||||
ggml_tensor* tensor = state->tensor;
|
||||
size_t tensor_size = GGML_PAD(ggml_backend_buffer_get_alloc_size(buffer, tensor),
|
||||
ggml_backend_buffer_get_alignment(buffer));
|
||||
enum ggml_status status = ggml_backend_tensor_alloc(buffer, tensor, static_cast<char*>(base) + offset);
|
||||
if (status != GGML_STATUS_SUCCESS) {
|
||||
LOG_ERROR("model manager failed to initialize params tensor '%s'", ggml_get_name(tensor));
|
||||
for (ggml_tensor* initialized : initialized_tensors) {
|
||||
initialized->buffer = nullptr;
|
||||
initialized->data = nullptr;
|
||||
initialized->extra = nullptr;
|
||||
}
|
||||
LOG_DEBUG("model manager releasing params backend buffer (%6.2f MB, %zu tensors, %s)",
|
||||
ggml_backend_buffer_get_size(buffer) / (1024.f * 1024.f),
|
||||
initialized_tensors.size(),
|
||||
ggml_backend_buffer_is_host(buffer) ? "RAM" : "VRAM");
|
||||
ggml_backend_buffer_free(buffer);
|
||||
return false;
|
||||
}
|
||||
initialized_tensors.push_back(tensor);
|
||||
offset += tensor_size;
|
||||
}
|
||||
|
||||
auto block = std::make_unique<ParamsStorageBlock>();
|
||||
block->buffer = buffer;
|
||||
block->states = chunk;
|
||||
ParamsStorageBlock* raw = block.get();
|
||||
params_storage_blocks_.push_back(std::move(block));
|
||||
created_storage_blocks.push_back(raw);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<TensorState*> chunk;
|
||||
size_t chunk_size = 0;
|
||||
for (TensorState* state : states) {
|
||||
ggml_tensor* tensor = state->tensor;
|
||||
size_t tensor_size = GGML_PAD(ggml_backend_buft_get_alloc_size(params_buft, tensor), alignment);
|
||||
if (max_size > 0 && tensor_size > max_size) {
|
||||
LOG_ERROR("model manager tensor '%s' is too large for params buffer: %zu > %zu",
|
||||
ggml_get_name(tensor),
|
||||
tensor_size,
|
||||
max_size);
|
||||
return false;
|
||||
}
|
||||
if (!chunk.empty() && max_size > 0 && chunk_size + tensor_size > max_size) {
|
||||
if (!alloc_chunk(chunk, chunk_size)) {
|
||||
return false;
|
||||
}
|
||||
chunk.clear();
|
||||
chunk_size = 0;
|
||||
}
|
||||
chunk.push_back(state);
|
||||
chunk_size += tensor_size;
|
||||
}
|
||||
|
||||
if (!alloc_chunk(chunk, chunk_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::load_tensors(const std::vector<TensorState*>& states) {
|
||||
std::map<std::string, TensorState*> states_by_name;
|
||||
std::set<std::string> target_tensor_names;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr) {
|
||||
continue;
|
||||
}
|
||||
states_by_name[state->name] = state;
|
||||
target_tensor_names.insert(state->name);
|
||||
}
|
||||
if (states_by_name.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::set<std::string> loaded_names;
|
||||
std::mutex loaded_names_mutex;
|
||||
auto on_new_tensor_cb = [&](const TensorStorage& tensor_storage, ggml_tensor** dst_tensor) -> bool {
|
||||
const std::string& name = tensor_storage.name;
|
||||
*dst_tensor = nullptr;
|
||||
|
||||
auto state_it = states_by_name.find(name);
|
||||
if (state_it == states_by_name.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TensorState* state = state_it->second;
|
||||
if (state == nullptr || state->tensor == nullptr) {
|
||||
LOG_ERROR("model manager tensor '%s' is null", name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state->tensor->ne[0] != tensor_storage.ne[0] ||
|
||||
state->tensor->ne[1] != tensor_storage.ne[1] ||
|
||||
state->tensor->ne[2] != tensor_storage.ne[2] ||
|
||||
state->tensor->ne[3] != tensor_storage.ne[3]) {
|
||||
LOG_ERROR(
|
||||
"model manager tensor '%s' has wrong shape in model file: got [%d, %d, %d, %d], expected [%d, %d, %d, %d]",
|
||||
name.c_str(),
|
||||
(int)tensor_storage.ne[0], (int)tensor_storage.ne[1], (int)tensor_storage.ne[2], (int)tensor_storage.ne[3],
|
||||
(int)state->tensor->ne[0], (int)state->tensor->ne[1], (int)state->tensor->ne[2], (int)state->tensor->ne[3]);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(loaded_names_mutex);
|
||||
loaded_names.insert(name);
|
||||
}
|
||||
*dst_tensor = state->tensor;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!model_loader_.load_tensors(on_new_tensor_cb, enable_mmap_, &target_tensor_names)) {
|
||||
LOG_ERROR("model manager load tensors failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool missing = false;
|
||||
for (const auto& pair : states_by_name) {
|
||||
const std::string& name = pair.first;
|
||||
if (loaded_names.find(name) == loaded_names.end()) {
|
||||
LOG_ERROR("model manager tensor '%s' was not loaded", name.c_str());
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
if (missing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& pair : states_by_name) {
|
||||
pair.second->loaded_to_params_backend = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ggml_backend_buffer_type_t ModelManager::params_buffer_type_for(const TensorState& state) const {
|
||||
if (state.params_backend == nullptr) {
|
||||
LOG_ERROR("model manager params backend is null for tensor '%s'", state.name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
ggml_backend_buffer_type_t params_buft = nullptr;
|
||||
if (state.compute_backend != nullptr && state.params_backend != state.compute_backend) {
|
||||
ggml_backend_dev_t compute_dev = ggml_backend_get_device(state.compute_backend);
|
||||
if (compute_dev != nullptr) {
|
||||
params_buft = ggml_backend_dev_host_buffer_type(compute_dev);
|
||||
}
|
||||
}
|
||||
if (params_buft == nullptr) {
|
||||
params_buft = ggml_backend_get_default_buffer_type(state.params_backend);
|
||||
}
|
||||
return params_buft;
|
||||
}
|
||||
|
||||
void ModelManager::free_compute_staging_block(ComputeStagingBlock& block) {
|
||||
for (auto& staged_tensor : block.staged_tensors) {
|
||||
TensorState* state = staged_tensor.first;
|
||||
ggml_tensor* staging_tensor = staged_tensor.second;
|
||||
if (state == nullptr || state->tensor == nullptr || staging_tensor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ggml_tensor* managed_tensor = state->tensor;
|
||||
managed_tensor->buffer = staging_tensor->buffer;
|
||||
managed_tensor->data = staging_tensor->data;
|
||||
managed_tensor->extra = staging_tensor->extra;
|
||||
staging_tensor->buffer = nullptr;
|
||||
staging_tensor->data = nullptr;
|
||||
staging_tensor->extra = nullptr;
|
||||
|
||||
state->staged_to_compute_backend = false;
|
||||
state->applied_lora_epoch = UINT64_MAX;
|
||||
}
|
||||
|
||||
if (block.buffer != nullptr) {
|
||||
LOG_DEBUG("model manager releasing compute params (%6.2f MB, %zu tensors) from %s",
|
||||
ggml_backend_buffer_get_size(block.buffer) / (1024.f * 1024.f),
|
||||
block.staged_tensors.size(),
|
||||
block.compute_backend != nullptr ? ggml_backend_name(block.compute_backend) : "unknown");
|
||||
ggml_backend_buffer_free(block.buffer);
|
||||
block.buffer = nullptr;
|
||||
}
|
||||
if (block.staging_ctx != nullptr) {
|
||||
ggml_free(block.staging_ctx);
|
||||
block.staging_ctx = nullptr;
|
||||
}
|
||||
block.staged_tensors.clear();
|
||||
}
|
||||
|
||||
void ModelManager::release_compute_staging_blocks(bool force,
|
||||
const std::unordered_set<TensorState*>* target_states) {
|
||||
for (auto it = compute_staging_blocks_.begin(); it != compute_staging_blocks_.end();) {
|
||||
ComputeStagingBlock* block = it->get();
|
||||
bool can_release = force;
|
||||
if (!can_release) {
|
||||
can_release = std::all_of(block->staged_tensors.begin(),
|
||||
block->staged_tensors.end(),
|
||||
[target_states](const std::pair<TensorState*, ggml_tensor*>& pair) {
|
||||
TensorState* state = pair.first;
|
||||
if (state == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (target_states != nullptr &&
|
||||
target_states->find(state) == target_states->end()) {
|
||||
return false;
|
||||
}
|
||||
return state->active_prepare_count == 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (can_release) {
|
||||
free_compute_staging_block(*block);
|
||||
it = compute_staging_blocks_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelManager::free_params_storage_block(ParamsStorageBlock& block) {
|
||||
if (block.buffer != nullptr) {
|
||||
LOG_DEBUG("model manager releasing params backend buffer (%6.2f MB, %zu tensors, %s)",
|
||||
ggml_backend_buffer_get_size(block.buffer) / (1024.f * 1024.f),
|
||||
block.states.size(),
|
||||
ggml_backend_buffer_is_host(block.buffer) ? "RAM" : "VRAM");
|
||||
ggml_backend_buffer_free(block.buffer);
|
||||
block.buffer = nullptr;
|
||||
}
|
||||
block.mmap_tensor_stores.clear();
|
||||
|
||||
for (TensorState* state : block.states) {
|
||||
if (state == nullptr || state->tensor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
state->tensor->buffer = nullptr;
|
||||
state->tensor->data = nullptr;
|
||||
state->tensor->extra = nullptr;
|
||||
|
||||
state->loaded_to_params_backend = false;
|
||||
state->applied_lora_epoch = UINT64_MAX;
|
||||
}
|
||||
block.states.clear();
|
||||
}
|
||||
|
||||
void ModelManager::release_params_storage_blocks(bool force,
|
||||
const std::unordered_set<TensorState*>* target_states) {
|
||||
for (auto it = params_storage_blocks_.begin(); it != params_storage_blocks_.end();) {
|
||||
ParamsStorageBlock* block = it->get();
|
||||
bool can_release = force;
|
||||
if (!can_release) {
|
||||
can_release = std::all_of(block->states.begin(),
|
||||
block->states.end(),
|
||||
[target_states](TensorState* state) {
|
||||
if (state == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (target_states != nullptr &&
|
||||
target_states->find(state) == target_states->end()) {
|
||||
return false;
|
||||
}
|
||||
return state->active_prepare_count == 0 &&
|
||||
!state->staged_to_compute_backend &&
|
||||
state->residency_mode == ResidencyMode::Disk;
|
||||
});
|
||||
}
|
||||
|
||||
if (can_release) {
|
||||
free_params_storage_block(*block);
|
||||
it = params_storage_blocks_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelManager::erase_params_storage_block(ParamsStorageBlock* block) {
|
||||
auto it = std::find_if(params_storage_blocks_.begin(),
|
||||
params_storage_blocks_.end(),
|
||||
[block](const std::unique_ptr<ParamsStorageBlock>& item) {
|
||||
return item.get() == block;
|
||||
});
|
||||
if (it != params_storage_blocks_.end()) {
|
||||
params_storage_blocks_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelManager::release_all() {
|
||||
for (auto& state : tensor_states_) {
|
||||
state->active_prepare_count = 0;
|
||||
state->applied_lora_epoch = UINT64_MAX;
|
||||
}
|
||||
release_compute_staging_blocks(true);
|
||||
release_params_storage_blocks(true);
|
||||
}
|
||||
|
||||
bool ModelManager::resolve_required_tensor_states(const std::vector<ggml_tensor*>& tensors,
|
||||
std::vector<TensorState*>& required_states) const {
|
||||
required_states.clear();
|
||||
std::unordered_set<TensorState*> seen;
|
||||
for (ggml_tensor* tensor : tensors) {
|
||||
if (tensor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const char* raw_name = ggml_get_name(tensor);
|
||||
if (raw_name == nullptr || raw_name[0] == '\0') {
|
||||
LOG_ERROR("model manager unnamed tensor is not registered");
|
||||
return false;
|
||||
}
|
||||
auto state_it = tensor_states_by_name_.find(raw_name);
|
||||
if (state_it == tensor_states_by_name_.end()) {
|
||||
LOG_ERROR("model manager tensor '%s' is not registered", raw_name);
|
||||
return false;
|
||||
}
|
||||
TensorState* state = state_it->second;
|
||||
if (state == nullptr) {
|
||||
LOG_ERROR("model manager tensor '%s' has no tensor state", raw_name);
|
||||
return false;
|
||||
}
|
||||
if (seen.insert(state).second) {
|
||||
required_states.push_back(state);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::prepare_params(const std::vector<ggml_tensor*>& tensors) {
|
||||
if (tensors.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<TensorState*> required_states;
|
||||
if (!resolve_required_tensor_states(tensors, required_states)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!load_tensors_to_params_backend(required_states)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stage_tensors_to_compute_backend(required_states)) {
|
||||
release_compute_staging_blocks(false);
|
||||
release_params_storage_blocks(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!apply_loras_to_params(required_states)) {
|
||||
release_compute_staging_blocks(false);
|
||||
release_params_storage_blocks(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (TensorState* state : required_states) {
|
||||
if (state == nullptr) {
|
||||
continue;
|
||||
}
|
||||
state->active_prepare_count++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelManager::finish_compute_backend_usage(const std::vector<TensorState*>& states) {
|
||||
if (states.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_set<TensorState*> target_states;
|
||||
for (TensorState* state : states) {
|
||||
if (state == nullptr || !target_states.insert(state).second) {
|
||||
continue;
|
||||
}
|
||||
if (state->active_prepare_count > 0) {
|
||||
state->active_prepare_count--;
|
||||
}
|
||||
}
|
||||
release_compute_staging_blocks(false, &target_states);
|
||||
}
|
||||
|
||||
void ModelManager::release_compute_backend_params(const std::vector<ggml_tensor*>& tensors) {
|
||||
if (tensors.empty()) {
|
||||
return;
|
||||
}
|
||||
std::vector<TensorState*> required_states;
|
||||
if (!resolve_required_tensor_states(tensors, required_states)) {
|
||||
return;
|
||||
}
|
||||
finish_compute_backend_usage(required_states);
|
||||
}
|
||||
|
||||
void ModelManager::release_params_backend_params(const std::vector<ggml_tensor*>& tensors) {
|
||||
if (tensors.empty()) {
|
||||
return;
|
||||
}
|
||||
std::vector<TensorState*> required_states;
|
||||
if (!resolve_required_tensor_states(tensors, required_states)) {
|
||||
return;
|
||||
}
|
||||
if (required_states.empty()) {
|
||||
return;
|
||||
}
|
||||
std::unordered_set<TensorState*> target_states(required_states.begin(), required_states.end());
|
||||
release_params_storage_blocks(false, &target_states);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
#ifndef __MODEL_MANAGER_H__
|
||||
#define __MODEL_MANAGER_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "model_loader.h"
|
||||
#include "weight_manager.h"
|
||||
|
||||
class ModelManager : public RunnerWeightManager {
|
||||
public:
|
||||
enum class ResidencyMode {
|
||||
Disk,
|
||||
ParamBackend,
|
||||
};
|
||||
|
||||
struct LoraSpec {
|
||||
std::string path;
|
||||
float multiplier = 1.0f;
|
||||
bool is_high_noise = false;
|
||||
std::string tensor_name_prefix_filter;
|
||||
bool required = false;
|
||||
};
|
||||
|
||||
private:
|
||||
struct TensorState {
|
||||
std::string name;
|
||||
ggml_tensor* tensor = nullptr;
|
||||
std::string desc;
|
||||
|
||||
ResidencyMode residency_mode = ResidencyMode::ParamBackend;
|
||||
ggml_backend_t compute_backend = nullptr;
|
||||
ggml_backend_t params_backend = nullptr;
|
||||
bool metadata_validated = false;
|
||||
|
||||
int active_prepare_count = 0;
|
||||
|
||||
bool loaded_to_params_backend = false;
|
||||
bool staged_to_compute_backend = false;
|
||||
uint64_t applied_lora_epoch = UINT64_MAX;
|
||||
};
|
||||
|
||||
struct ParamsStorageBlock {
|
||||
ggml_backend_buffer_t buffer = nullptr;
|
||||
std::vector<MmapTensorStore> mmap_tensor_stores;
|
||||
std::vector<TensorState*> states;
|
||||
};
|
||||
|
||||
struct ComputeStagingBlock {
|
||||
ggml_backend_t compute_backend = nullptr;
|
||||
ggml_backend_buffer_t buffer = nullptr;
|
||||
ggml_context* staging_ctx = nullptr;
|
||||
std::vector<std::pair<TensorState*, ggml_tensor*>> staged_tensors;
|
||||
};
|
||||
|
||||
ModelLoader model_loader_;
|
||||
std::vector<std::unique_ptr<TensorState>> tensor_states_;
|
||||
std::map<std::string, TensorState*> tensor_states_by_name_;
|
||||
std::vector<std::unique_ptr<ParamsStorageBlock>> params_storage_blocks_;
|
||||
std::vector<std::unique_ptr<ComputeStagingBlock>> compute_staging_blocks_;
|
||||
std::set<std::string> common_ignore_tensors_;
|
||||
std::vector<LoraSpec> loras_;
|
||||
SDVersion lora_version_ = VERSION_COUNT;
|
||||
uint64_t current_lora_epoch_ = 0;
|
||||
int n_threads_ = 0;
|
||||
bool enable_mmap_ = false;
|
||||
|
||||
void finish_compute_backend_usage(const std::vector<TensorState*>& states);
|
||||
void release_all();
|
||||
|
||||
bool resolve_required_tensor_states(const std::vector<ggml_tensor*>& tensors,
|
||||
std::vector<TensorState*>& required_states) const;
|
||||
bool should_ignore(const TensorState& state) const;
|
||||
bool is_optional_missing_tensor(const std::string& name) const;
|
||||
bool validate_tensor(const TensorState& state) const;
|
||||
|
||||
bool load_tensors_to_params_backend(const std::vector<TensorState*>& states);
|
||||
bool apply_loras_to_params(const std::vector<TensorState*>& states);
|
||||
bool mmap_params(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks);
|
||||
bool can_mmap_storage(const TensorState& state) const;
|
||||
bool alloc_params_buffers(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks);
|
||||
bool load_tensors(const std::vector<TensorState*>& states);
|
||||
bool stage_tensors_to_compute_backend(const std::vector<TensorState*>& states);
|
||||
|
||||
ggml_backend_buffer_type_t params_buffer_type_for(const TensorState& state) const;
|
||||
void release_compute_staging_blocks(bool force = false,
|
||||
const std::unordered_set<TensorState*>* target_states = nullptr);
|
||||
void release_params_storage_blocks(bool force = false,
|
||||
const std::unordered_set<TensorState*>* target_states = nullptr);
|
||||
void free_compute_staging_block(ComputeStagingBlock& block);
|
||||
void free_params_storage_block(ParamsStorageBlock& block);
|
||||
void erase_params_storage_block(ParamsStorageBlock* block);
|
||||
void reset_lora_applied_params();
|
||||
|
||||
public:
|
||||
~ModelManager() override;
|
||||
|
||||
ModelLoader& loader() { return model_loader_; }
|
||||
const ModelLoader& loader() const { return model_loader_; }
|
||||
|
||||
void set_n_threads(int n_threads) {
|
||||
n_threads_ = n_threads;
|
||||
model_loader_.set_n_threads(n_threads);
|
||||
}
|
||||
void set_enable_mmap(bool enable_mmap) { enable_mmap_ = enable_mmap; }
|
||||
void set_common_ignore_tensors(std::set<std::string> ignore_tensors);
|
||||
void set_loras(std::vector<LoraSpec> loras, SDVersion version);
|
||||
|
||||
std::set<std::string> tensor_names() const;
|
||||
|
||||
bool register_param_tensors(const std::string& desc,
|
||||
std::map<std::string, ggml_tensor*> tensors,
|
||||
ResidencyMode residency_mode,
|
||||
ggml_backend_t compute_backend,
|
||||
ggml_backend_t params_backend,
|
||||
size_t* registered_tensor_size = nullptr);
|
||||
|
||||
template <typename Runner>
|
||||
bool register_runner_params(const std::string& desc,
|
||||
Runner& runner,
|
||||
ResidencyMode residency_mode,
|
||||
ggml_backend_t compute_backend,
|
||||
ggml_backend_t params_backend,
|
||||
size_t* registered_tensor_size = nullptr) {
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
runner.get_param_tensors(tensors);
|
||||
return register_param_tensors(desc,
|
||||
std::move(tensors),
|
||||
residency_mode,
|
||||
compute_backend,
|
||||
params_backend,
|
||||
registered_tensor_size);
|
||||
}
|
||||
|
||||
template <typename Runner>
|
||||
bool register_runner_params(const std::string& desc,
|
||||
Runner& runner,
|
||||
const std::string& prefix,
|
||||
ResidencyMode residency_mode,
|
||||
ggml_backend_t compute_backend,
|
||||
ggml_backend_t params_backend,
|
||||
size_t* registered_tensor_size = nullptr) {
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
runner.get_param_tensors(tensors, prefix);
|
||||
return register_param_tensors(desc,
|
||||
std::move(tensors),
|
||||
residency_mode,
|
||||
compute_backend,
|
||||
params_backend,
|
||||
registered_tensor_size);
|
||||
}
|
||||
|
||||
bool validate_registered_tensors();
|
||||
|
||||
bool prepare_params(const std::vector<ggml_tensor*>& tensors) override;
|
||||
void release_compute_backend_params(const std::vector<ggml_tensor*>& tensors) override;
|
||||
void release_params_backend_params(const std::vector<ggml_tensor*>& tensors) override;
|
||||
};
|
||||
|
||||
#endif // __MODEL_MANAGER_H__
|
||||
@@ -990,7 +990,46 @@ bool is_first_stage_model_name(const std::string& name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string convert_esrgan_tensor_name(std::string name) {
|
||||
static std::unordered_map<std::string, std::string> esrgan_name_map;
|
||||
|
||||
if (esrgan_name_map.empty()) {
|
||||
esrgan_name_map["model.0."] = "conv_first.";
|
||||
|
||||
constexpr int max_num_blocks = 64;
|
||||
for (int i = 0; i < max_num_blocks; i++) {
|
||||
std::string block_prefix = "model.1.sub." + std::to_string(i) + ".";
|
||||
for (int rdb = 1; rdb <= 3; rdb++) {
|
||||
for (int conv = 1; conv <= 5; conv++) {
|
||||
esrgan_name_map[block_prefix + "RDB" + std::to_string(rdb) + ".conv" + std::to_string(conv) + ".0."] =
|
||||
"body." + std::to_string(i) + ".rdb" + std::to_string(rdb) + ".conv" + std::to_string(conv) + ".";
|
||||
}
|
||||
}
|
||||
esrgan_name_map[block_prefix + "weight"] = "conv_body.weight";
|
||||
esrgan_name_map[block_prefix + "bias"] = "conv_body.bias";
|
||||
}
|
||||
|
||||
// RealESRGAN stores only the learned layers in a Sequential. These indices
|
||||
// cover the common x1, x2 and x4 layouts.
|
||||
esrgan_name_map["model.2."] = "conv_hr.";
|
||||
esrgan_name_map["model.3."] = "conv_up1.";
|
||||
esrgan_name_map["model.4."] = "conv_last.";
|
||||
esrgan_name_map["model.5."] = "conv_hr.";
|
||||
esrgan_name_map["model.6."] = "conv_up2.";
|
||||
esrgan_name_map["model.7."] = "conv_last.";
|
||||
esrgan_name_map["model.8."] = "conv_hr.";
|
||||
esrgan_name_map["model.10."] = "conv_last.";
|
||||
}
|
||||
|
||||
replace_with_prefix_map(name, esrgan_name_map);
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string convert_tensor_name(std::string name, SDVersion version) {
|
||||
if (version == VERSION_ESRGAN) {
|
||||
return convert_esrgan_tensor_name(std::move(name));
|
||||
}
|
||||
|
||||
bool is_lora = false;
|
||||
bool is_lycoris_underline = false;
|
||||
bool is_underline = false;
|
||||
|
||||
@@ -172,8 +172,8 @@ namespace sd::guidance {
|
||||
momentum_buffer_ = deltas;
|
||||
}
|
||||
|
||||
float diff_norm = 0.0f;
|
||||
const int standard_res = 2 * 1024 / 8; // Use SDXL as the standard resolution (1024x1024, 8x8 patches, 4=2x2 channels)
|
||||
float diff_norm = 0.0f;
|
||||
const int standard_res = 2 * 1024 / 8; // Use SDXL as the standard resolution (1024x1024, 8x8 patches, 4=2x2 channels)
|
||||
if (params_.norm_threshold > 0.0f) {
|
||||
diff_norm = std::sqrt((deltas * deltas).sum()) * standard_res / std::sqrt(static_cast<float>(deltas.numel()));
|
||||
}
|
||||
|
||||
+520
-627
File diff suppressed because it is too large
Load Diff
+38
-19
@@ -18,6 +18,12 @@ UpscalerGGML::UpscalerGGML(int n_threads,
|
||||
params_backend_spec(std::move(params_backend_spec)) {
|
||||
}
|
||||
|
||||
UpscalerGGML::~UpscalerGGML() {
|
||||
// ModelManager holds raw ggml tensor pointers owned by the runner context.
|
||||
model_manager.reset();
|
||||
esrgan_upscaler.reset();
|
||||
}
|
||||
|
||||
void UpscalerGGML::set_max_graph_vram_bytes(size_t max_vram_bytes) {
|
||||
max_graph_vram_bytes = max_vram_bytes;
|
||||
if (esrgan_upscaler) {
|
||||
@@ -33,17 +39,12 @@ void UpscalerGGML::set_stream_layers_enabled(bool enabled) {
|
||||
}
|
||||
|
||||
bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
int n_threads) {
|
||||
ggml_log_set(ggml_log_callback_default, nullptr);
|
||||
|
||||
std::string error;
|
||||
if (!backend_manager.init(backend_spec.c_str(),
|
||||
params_backend_spec.c_str(),
|
||||
offload_params_to_cpu,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&error)) {
|
||||
LOG_ERROR("upscaler backend config failed: %s", error.c_str());
|
||||
return false;
|
||||
@@ -72,22 +73,39 @@ bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
||||
return false;
|
||||
}
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file_and_convert_name(esrgan_path)) {
|
||||
model_manager = std::make_shared<ModelManager>();
|
||||
model_manager->set_n_threads(n_threads);
|
||||
model_manager->set_enable_mmap(false);
|
||||
|
||||
ModelLoader& model_loader = model_manager->loader();
|
||||
if (!model_loader.init_from_file_and_convert_name(esrgan_path, "", VERSION_ESRGAN)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", esrgan_path.c_str());
|
||||
return false;
|
||||
}
|
||||
model_loader.set_wtype_override(model_data_type);
|
||||
LOG_INFO("Upscaler weight type: %s", ggml_type_name(model_data_type));
|
||||
esrgan_upscaler = std::make_shared<ESRGAN>(backend_for(SDBackendModule::UPSCALER),
|
||||
params_backend_for(SDBackendModule::UPSCALER),
|
||||
tile_size,
|
||||
model_loader.get_tensor_storage_map());
|
||||
model_loader.get_tensor_storage_map(),
|
||||
model_manager);
|
||||
if (esrgan_upscaler == nullptr || esrgan_upscaler->rrdb_net == nullptr) {
|
||||
LOG_ERROR("init esrgan model from metadata failed: '%s'", esrgan_path.c_str());
|
||||
return false;
|
||||
}
|
||||
esrgan_upscaler->set_max_graph_vram_bytes(max_graph_vram_bytes);
|
||||
esrgan_upscaler->set_stream_layers_enabled(stream_layers_enabled);
|
||||
if (direct) {
|
||||
esrgan_upscaler->set_conv2d_direct_enabled(true);
|
||||
}
|
||||
if (!esrgan_upscaler->load_from_file(esrgan_path, n_threads)) {
|
||||
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
esrgan_upscaler->get_param_tensors(tensors);
|
||||
if (!model_manager->register_param_tensors("ESRGAN",
|
||||
std::move(tensors),
|
||||
backend_manager.params_backend_is_disk(SDBackendModule::UPSCALER) ? ModelManager::ResidencyMode::Disk : ModelManager::ResidencyMode::ParamBackend,
|
||||
backend_for(SDBackendModule::UPSCALER),
|
||||
params_backend_for(SDBackendModule::UPSCALER)) ||
|
||||
!model_manager->validate_registered_tensors()) {
|
||||
LOG_ERROR("register esrgan tensors with model manager failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -95,6 +113,7 @@ bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
||||
|
||||
sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_tensor) {
|
||||
sd::Tensor<float> upscaled;
|
||||
const int scale = esrgan_upscaler->config.scale;
|
||||
if (tile_size <= 0 || (input_tensor.shape()[0] <= tile_size && input_tensor.shape()[1] <= tile_size)) {
|
||||
upscaled = esrgan_upscaler->compute(n_threads, input_tensor);
|
||||
} else {
|
||||
@@ -108,9 +127,9 @@ sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_te
|
||||
};
|
||||
|
||||
upscaled = process_tiles_2d(input_tensor,
|
||||
static_cast<int>(input_tensor.shape()[0] * esrgan_upscaler->scale),
|
||||
static_cast<int>(input_tensor.shape()[1] * esrgan_upscaler->scale),
|
||||
esrgan_upscaler->scale,
|
||||
static_cast<int>(input_tensor.shape()[0] * scale),
|
||||
static_cast<int>(input_tensor.shape()[1] * scale),
|
||||
scale,
|
||||
tile_size,
|
||||
tile_size,
|
||||
0.25f,
|
||||
@@ -129,8 +148,9 @@ sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_te
|
||||
sd_image_t UpscalerGGML::upscale(sd_image_t input_image, uint32_t upscale_factor) {
|
||||
// upscale_factor, unused for RealESRGAN_x4plus_anime_6B.pth
|
||||
sd_image_t upscaled_image = {0, 0, 0, nullptr};
|
||||
int output_width = (int)input_image.width * esrgan_upscaler->scale;
|
||||
int output_height = (int)input_image.height * esrgan_upscaler->scale;
|
||||
const int scale = esrgan_upscaler->config.scale;
|
||||
int output_width = (int)input_image.width * scale;
|
||||
int output_height = (int)input_image.height * scale;
|
||||
LOG_INFO("upscaling from (%i x %i) to (%i x %i)",
|
||||
input_image.width, input_image.height, output_width, output_height);
|
||||
|
||||
@@ -153,7 +173,6 @@ struct upscaler_ctx_t {
|
||||
};
|
||||
|
||||
upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path_c_str,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads,
|
||||
int tile_size,
|
||||
@@ -170,7 +189,7 @@ upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path_c_str,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!upscaler_ctx->upscaler->load_from_file(esrgan_path, offload_params_to_cpu, n_threads)) {
|
||||
if (!upscaler_ctx->upscaler->load_from_file(esrgan_path, n_threads)) {
|
||||
delete upscaler_ctx->upscaler;
|
||||
upscaler_ctx->upscaler = nullptr;
|
||||
free(upscaler_ctx);
|
||||
@@ -187,7 +206,7 @@ int get_upscale_factor(upscaler_ctx_t* upscaler_ctx) {
|
||||
if (upscaler_ctx == nullptr || upscaler_ctx->upscaler == nullptr || upscaler_ctx->upscaler->esrgan_upscaler == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
return upscaler_ctx->upscaler->esrgan_upscaler->scale;
|
||||
return upscaler_ctx->upscaler->esrgan_upscaler->config.scale;
|
||||
}
|
||||
|
||||
void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx) {
|
||||
|
||||
+3
-1
@@ -4,6 +4,7 @@
|
||||
#include "core/ggml_extend_backend.h"
|
||||
#include "core/tensor.hpp"
|
||||
#include "model/upscaler/esrgan.hpp"
|
||||
#include "model_manager.h"
|
||||
#include "stable-diffusion.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -11,6 +12,7 @@
|
||||
|
||||
struct UpscalerGGML {
|
||||
SDBackendManager backend_manager;
|
||||
std::shared_ptr<ModelManager> model_manager;
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
std::shared_ptr<ESRGAN> esrgan_upscaler;
|
||||
std::string esrgan_path;
|
||||
@@ -27,9 +29,9 @@ struct UpscalerGGML {
|
||||
int tile_size = 128,
|
||||
std::string backend_spec = "",
|
||||
std::string params_backend_spec = "");
|
||||
~UpscalerGGML();
|
||||
|
||||
bool load_from_file(const std::string& esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
int n_threads);
|
||||
void set_max_graph_vram_bytes(size_t max_vram_bytes);
|
||||
void set_stream_layers_enabled(bool enabled);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef __WEIGHT_MANAGER_H__
|
||||
#define __WEIGHT_MANAGER_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct ggml_tensor;
|
||||
|
||||
struct RunnerWeightManager {
|
||||
virtual ~RunnerWeightManager() = default;
|
||||
virtual bool prepare_params(const std::vector<ggml_tensor*>& tensors) = 0;
|
||||
virtual void release_compute_backend_params(const std::vector<ggml_tensor*>& tensors) = 0;
|
||||
virtual void release_params_backend_params(const std::vector<ggml_tensor*>& tensors) = 0;
|
||||
};
|
||||
|
||||
#endif // __WEIGHT_MANAGER_H__
|
||||
Reference in New Issue
Block a user