mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-06-09 15:56:39 +00:00
Compare commits
No commits in common. "master" and "master-577-f3f69e2" have entirely different histories.
master
...
master-577
15
.github/pull_request_template.md
vendored
15
.github/pull_request_template.md
vendored
@ -1,15 +0,0 @@
|
|||||||
## Summary
|
|
||||||
|
|
||||||
<!-- Describe what changed and why. Keep the PR focused on one clear change. -->
|
|
||||||
|
|
||||||
## Related Issue / Discussion
|
|
||||||
|
|
||||||
<!-- Link related issues, discussions, or previous PRs if applicable. -->
|
|
||||||
|
|
||||||
## Additional Information
|
|
||||||
|
|
||||||
<!-- Add verification notes, screenshots, sample output, or other context when applicable. -->
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
- [ ] I have read and confirmed this PR follows the [contribution guidelines](https://github.com/leejet/stable-diffusion.cpp/blob/master/CONTRIBUTING.md).
|
|
||||||
300
.github/workflows/build.yml
vendored
300
.github/workflows/build.yml
vendored
@ -14,8 +14,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
[
|
[
|
||||||
".github/workflows/**",
|
".github/workflows/**",
|
||||||
".dockerignore",
|
|
||||||
"Dockerfile*",
|
|
||||||
"**/CMakeLists.txt",
|
"**/CMakeLists.txt",
|
||||||
"**/Makefile",
|
"**/Makefile",
|
||||||
"**/*.h",
|
"**/*.h",
|
||||||
@ -31,8 +29,6 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
[
|
[
|
||||||
".github/workflows/**",
|
".github/workflows/**",
|
||||||
".dockerignore",
|
|
||||||
"Dockerfile*",
|
|
||||||
"**/CMakeLists.txt",
|
"**/CMakeLists.txt",
|
||||||
"**/Makefile",
|
"**/Makefile",
|
||||||
"**/*.h",
|
"**/*.h",
|
||||||
@ -139,7 +135,7 @@ jobs:
|
|||||||
id: depends
|
id: depends
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install build-essential libvulkan-dev glslc spirv-headers
|
sudo apt-get install build-essential libvulkan-dev glslc
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
id: cmake_build
|
id: cmake_build
|
||||||
@ -180,8 +176,7 @@ jobs:
|
|||||||
|
|
||||||
build-and-push-docker-images:
|
build-and-push-docker-images:
|
||||||
name: Build and push container images
|
name: Build and push container images
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
runs-on: ubuntu-latest
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@ -193,20 +188,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
variant: [musa, sycl, vulkan, cuda]
|
variant: [musa, sycl, vulkan, cuda]
|
||||||
platform: [linux/amd64]
|
|
||||||
runner: [ubuntu-latest]
|
|
||||||
build-args: [""]
|
|
||||||
tag-suffix: [""]
|
|
||||||
include:
|
|
||||||
- variant: cuda
|
|
||||||
platform: linux/arm64
|
|
||||||
runner: ubuntu-24.04-arm
|
|
||||||
tag-suffix: "-spark"
|
|
||||||
build-args: |
|
|
||||||
CUDA_VERSION=13.0.0
|
|
||||||
UBUNTU_VERSION=24.04
|
|
||||||
CUDA_ARCHITECTURES=121
|
|
||||||
GGML_CUDA_FA_ALL_QUANTS=ON
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@ -261,13 +242,12 @@ jobs:
|
|||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: linux/amd64
|
||||||
push: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
push: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||||
file: Dockerfile.${{ matrix.variant }}
|
file: Dockerfile.${{ matrix.variant }}
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH_NAME }}-${{ matrix.variant }}${{ matrix.tag-suffix }}
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH_NAME }}-${{ matrix.variant }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
build-args: ${{ matrix.build-args }}
|
|
||||||
|
|
||||||
macOS-latest-cmake:
|
macOS-latest-cmake:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
@ -463,129 +443,12 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip
|
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-${{ matrix.build }}-x64.zip
|
||||||
|
|
||||||
windows-latest-rocm:
|
|
||||||
runs-on: windows-2022
|
|
||||||
|
|
||||||
env:
|
|
||||||
ROCM_VERSION: "7.13.0"
|
|
||||||
GPU_TARGETS: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: 10.15.1
|
|
||||||
|
|
||||||
- name: Cache ROCm Installation
|
|
||||||
id: cache-rocm
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: C:\TheRock\build
|
|
||||||
key: rocm-${{ env.ROCM_VERSION }}-gfx1151-${{ runner.os }}
|
|
||||||
|
|
||||||
- name: ccache
|
|
||||||
uses: ggml-org/ccache-action@v1.2.16
|
|
||||||
with:
|
|
||||||
key: windows-latest-rocm-${{ env.ROCM_VERSION }}-x64
|
|
||||||
evict-old-files: 1d
|
|
||||||
|
|
||||||
- name: Install ROCm
|
|
||||||
if: steps.cache-rocm.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
write-host "Downloading AMD ROCm ${{ env.ROCM_VERSION }} tarball"
|
|
||||||
Invoke-WebRequest -Uri "https://repo.amd.com/rocm/tarball/therock-dist-windows-gfx1151-${{ env.ROCM_VERSION }}.tar.gz" -OutFile "${env:RUNNER_TEMP}\rocm.tar.gz"
|
|
||||||
write-host "Extracting ROCm tarball"
|
|
||||||
mkdir C:\TheRock\build -Force
|
|
||||||
tar -xzf "${env:RUNNER_TEMP}\rocm.tar.gz" -C C:\TheRock\build --strip-components=1
|
|
||||||
write-host "Completed ROCm extraction"
|
|
||||||
|
|
||||||
- name: Setup ROCm Environment
|
|
||||||
run: |
|
|
||||||
$rocmPath = "C:\TheRock\build"
|
|
||||||
echo "HIP_PATH=$rocmPath" >> $env:GITHUB_ENV
|
|
||||||
echo "HIP_DEVICE_LIB_PATH=$rocmPath\lib\llvm\amdgcn\bitcode" >> $env:GITHUB_ENV
|
|
||||||
echo "HIP_PLATFORM=amd" >> $env:GITHUB_ENV
|
|
||||||
echo "LLVM_PATH=$rocmPath\lib\llvm" >> $env:GITHUB_ENV
|
|
||||||
echo "$rocmPath\bin" >> $env:GITHUB_PATH
|
|
||||||
echo "$rocmPath\lib\llvm\bin" >> $env:GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. `
|
|
||||||
-G "Unix Makefiles" `
|
|
||||||
-DCMAKE_PREFIX_PATH="${env:HIP_PATH}" `
|
|
||||||
-DSD_HIPBLAS=ON `
|
|
||||||
-DSD_BUILD_SHARED_LIBS=ON `
|
|
||||||
-DGGML_NATIVE=OFF `
|
|
||||||
-DCMAKE_C_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang.exe" `
|
|
||||||
-DCMAKE_CXX_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang++.exe" `
|
|
||||||
-DCMAKE_HIP_COMPILER="${env:HIP_PATH}\lib\llvm\bin\clang.exe" `
|
|
||||||
-DHIP_PATH="${env:HIP_PATH}" `
|
|
||||||
-DCMAKE_BUILD_TYPE=Release `
|
|
||||||
-DGPU_TARGETS="${{ env.GPU_TARGETS }}"
|
|
||||||
cmake --build . --config Release --parallel ${env:NUMBER_OF_PROCESSORS}
|
|
||||||
|
|
||||||
- name: Get commit hash
|
|
||||||
id: commit
|
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
|
||||||
uses: pr-mpt/actions-commit-hash@v2
|
|
||||||
|
|
||||||
- name: Pack artifacts
|
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
|
||||||
run: |
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
$dst = "build\bin"
|
|
||||||
$rocmBin = Join-Path "${env:HIP_PATH}" "bin"
|
|
||||||
$requiredRocmPaths = @(
|
|
||||||
(Join-Path $rocmBin "rocblas.dll"),
|
|
||||||
(Join-Path $rocmBin "rocblas\library")
|
|
||||||
)
|
|
||||||
foreach ($path in $requiredRocmPaths) {
|
|
||||||
if (!(Test-Path $path)) {
|
|
||||||
throw "Missing ROCm runtime dependency: $path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($pattern in @("rocblas*.dll", "hipblas*.dll", "libhipblas*.dll")) {
|
|
||||||
Copy-Item -Path (Join-Path $rocmBin $pattern) -Destination $dst -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($dir in @("rocblas", "hipblaslt")) {
|
|
||||||
$src = Join-Path $rocmBin $dir
|
|
||||||
if (Test-Path $src) {
|
|
||||||
Copy-Item -Path $src -Destination $dst -Recurse -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\*
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip
|
|
||||||
path: |
|
|
||||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip
|
|
||||||
|
|
||||||
windows-latest-cmake-hip:
|
windows-latest-cmake-hip:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
|
||||||
env:
|
env:
|
||||||
HIPSDK_INSTALLER_VERSION: "26.Q1"
|
HIPSDK_INSTALLER_VERSION: "25.Q3"
|
||||||
ROCM_VERSION: "7.1.1"
|
GPU_TARGETS: "gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032"
|
||||||
GPU_TARGETS: "gfx1150;gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -620,7 +483,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
write-host "Downloading AMD HIP SDK Installer"
|
write-host "Downloading AMD HIP SDK Installer"
|
||||||
Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-${{ env.HIPSDK_INSTALLER_VERSION }}-Win11-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe"
|
Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-${{ env.HIPSDK_INSTALLER_VERSION }}-WinSvr2022-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe"
|
||||||
write-host "Installing AMD HIP SDK"
|
write-host "Installing AMD HIP SDK"
|
||||||
$proc = Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -PassThru
|
$proc = Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -PassThru
|
||||||
$completed = $proc.WaitForExit(600000)
|
$completed = $proc.WaitForExit(600000)
|
||||||
@ -673,75 +536,47 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
md "build\bin\rocblas\library\"
|
md "build\bin\rocblas\library\"
|
||||||
md "build\bin\hipblaslt\library"
|
md "build\bin\hipblaslt\library"
|
||||||
cp "${env:HIP_PATH}\bin\libhipblas.dll" "build\bin\"
|
cp "${env:HIP_PATH}\bin\hipblas.dll" "build\bin\"
|
||||||
cp "${env:HIP_PATH}\bin\libhipblaslt.dll" "build\bin\"
|
cp "${env:HIP_PATH}\bin\hipblaslt.dll" "build\bin\"
|
||||||
cp "${env:HIP_PATH}\bin\rocblas.dll" "build\bin\"
|
cp "${env:HIP_PATH}\bin\rocblas.dll" "build\bin\"
|
||||||
cp "${env:HIP_PATH}\bin\rocblas\library\*" "build\bin\rocblas\library\"
|
cp "${env:HIP_PATH}\bin\rocblas\library\*" "build\bin\rocblas\library\"
|
||||||
cp "${env:HIP_PATH}\bin\hipblaslt\library\*" "build\bin\hipblaslt\library\"
|
cp "${env:HIP_PATH}\bin\hipblaslt\library\*" "build\bin\hipblaslt\library\"
|
||||||
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip .\build\bin\*
|
7z a sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip .\build\bin\*
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip
|
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip
|
||||||
path: |
|
path: |
|
||||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-${{ env.ROCM_VERSION }}-x64.zip
|
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-win-rocm-x64.zip
|
||||||
|
|
||||||
ubuntu-latest-rocm:
|
ubuntu-latest-rocm:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
|
container: rocm/dev-ubuntu-24.04:7.2
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
ROCM_VERSION: "7.2"
|
||||||
UBUNTU_VERSION: "24.04"
|
UBUNTU_VERSION: "24.04"
|
||||||
|
GPU_TARGETS: "gfx1151;gfx1150;gfx1100;gfx1101;gfx1102;gfx1200;gfx1201"
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- ROCM_VERSION: "7.2.1"
|
|
||||||
gpu_targets: "gfx908;gfx90a;gfx942;gfx1030;gfx1031;gfx1032;gfx1100;gfx1101;gfx1102;gfx1151;gfx1150;gfx1200;gfx1201"
|
|
||||||
build: 'x64'
|
|
||||||
- ROCM_VERSION: "7.13.0"
|
|
||||||
gpu_targets: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
|
|
||||||
build: x64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- run: apt-get update && apt-get install -y git
|
||||||
- name: Clone
|
- name: Clone
|
||||||
id: checkout
|
id: checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: ccache
|
- name: Setup Node
|
||||||
uses: ggml-org/ccache-action@v1.2.16
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
key: ubuntu-rocm-cmake-${{ matrix.ROCM_VERSION }}-${{ matrix.build }}
|
node-version: 20
|
||||||
evict-old-files: 1d
|
|
||||||
|
|
||||||
- name: Dependencies
|
- name: Setup pnpm
|
||||||
id: depends
|
uses: pnpm/action-setup@v4
|
||||||
run: |
|
with:
|
||||||
sudo apt install -y build-essential cmake wget zip ninja-build
|
version: 10.15.1
|
||||||
|
|
||||||
- name: Setup Legacy ROCm
|
|
||||||
if: matrix.ROCM_VERSION == '7.2.1'
|
|
||||||
id: legacy_env
|
|
||||||
run: |
|
|
||||||
sudo mkdir --parents --mode=0755 /etc/apt/keyrings
|
|
||||||
wget https://repo.radeon.com/rocm/rocm.gpg.key -O - | \
|
|
||||||
gpg --dearmor | sudo tee /etc/apt/keyrings/rocm.gpg > /dev/null
|
|
||||||
|
|
||||||
sudo tee /etc/apt/sources.list.d/rocm.list << EOF
|
|
||||||
deb [arch=amd64 signed-by=/etc/apt/keyrings/rocm.gpg] https://repo.radeon.com/rocm/apt/${{ matrix.ROCM_VERSION }} noble main
|
|
||||||
EOF
|
|
||||||
|
|
||||||
sudo tee /etc/apt/preferences.d/rocm-pin-600 << EOF
|
|
||||||
Package: *
|
|
||||||
Pin: release o=repo.radeon.com
|
|
||||||
Pin-Priority: 600
|
|
||||||
EOF
|
|
||||||
|
|
||||||
sudo apt update
|
|
||||||
sudo apt-get install -y libssl-dev rocm-hip-sdk
|
|
||||||
|
|
||||||
- name: Free disk space
|
- name: Free disk space
|
||||||
run: |
|
run: |
|
||||||
@ -756,29 +591,51 @@ jobs:
|
|||||||
sudo rm -rf /var/lib/apt/lists/* || true
|
sudo rm -rf /var/lib/apt/lists/* || true
|
||||||
sudo apt clean
|
sudo apt clean
|
||||||
|
|
||||||
- name: Setup TheRock
|
- name: Dependencies
|
||||||
if: matrix.ROCM_VERSION != '7.2.1'
|
id: depends
|
||||||
id: therock_env
|
|
||||||
run: |
|
run: |
|
||||||
wget https://repo.amd.com/rocm/tarball/therock-dist-linux-gfx1151-${{ matrix.ROCM_VERSION }}.tar.gz
|
sudo apt-get update
|
||||||
mkdir install
|
sudo apt install -y \
|
||||||
tar -xf *.tar.gz -C install
|
cmake \
|
||||||
export ROCM_PATH=$(pwd)/install
|
hip-dev \
|
||||||
echo ROCM_PATH=$ROCM_PATH >> $GITHUB_ENV
|
hipblas-dev \
|
||||||
echo PATH=$PATH:$ROCM_PATH/bin >> $GITHUB_ENV
|
ninja-build \
|
||||||
echo LD_LIBRARY_PATH=$ROCM_PATH/lib:$ROCM_PATH/llvm/lib:$ROCM_PATH/lib/rocprofiler-systems >> $GITHUB_ENV
|
rocm-dev \
|
||||||
|
zip
|
||||||
|
# Clean apt caches to recover disk space
|
||||||
|
sudo apt clean
|
||||||
|
sudo rm -rf /var/lib/apt/lists/* || true
|
||||||
|
|
||||||
# setup-node installs into /opt/hostedtoolcache, which is removed above.
|
- name: Setup ROCm Environment
|
||||||
# Keep Node/pnpm setup after disk cleanup so the server frontend can be embedded.
|
run: |
|
||||||
- name: Setup Node
|
# Add ROCm to PATH for current session
|
||||||
uses: actions/setup-node@v4
|
echo "/opt/rocm/bin" >> $GITHUB_PATH
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
# Build regex pattern from ${{ env.GPU_TARGETS }} (match target as substring)
|
||||||
uses: pnpm/action-setup@v4
|
TARGET_REGEX="($(printf '%s' "${{ env.GPU_TARGETS }}" | sed 's/;/|/g'))"
|
||||||
with:
|
|
||||||
version: 10.15.1
|
# Remove library files for architectures we're not building for to save disk space
|
||||||
|
echo "Cleaning up unneeded architecture files..."
|
||||||
|
cd /opt/rocm/lib/rocblas/library
|
||||||
|
# Keep only our target architectures
|
||||||
|
for file in *; do
|
||||||
|
if printf '%s' "$file" | grep -q 'gfx'; then
|
||||||
|
if ! printf '%s' "$file" | grep -Eq "$TARGET_REGEX"; then
|
||||||
|
echo "Removing $file" &&
|
||||||
|
sudo rm -f "$file";
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cd /opt/rocm/lib/hipblaslt/library
|
||||||
|
for file in *; do
|
||||||
|
if printf '%s' "$file" | grep -q 'gfx'; then
|
||||||
|
if ! printf '%s' "$file" | grep -Eq "$TARGET_REGEX"; then
|
||||||
|
echo "Removing $file" &&
|
||||||
|
sudo rm -f "$file";
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
id: cmake_build
|
id: cmake_build
|
||||||
@ -786,12 +643,12 @@ jobs:
|
|||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G Ninja \
|
cmake .. -G Ninja \
|
||||||
-DCMAKE_HIP_COMPILER="$(hipconfig -l)/clang" \
|
-DCMAKE_CXX_COMPILER=amdclang++ \
|
||||||
-DCMAKE_HIP_FLAGS="-mllvm --amdgpu-unroll-threshold-local=600" \
|
-DCMAKE_C_COMPILER=amdclang \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DSD_HIPBLAS=ON \
|
-DSD_HIPBLAS=ON \
|
||||||
-DHIP_PLATFORM=amd \
|
-DGPU_TARGETS="${{ env.GPU_TARGETS }}" \
|
||||||
-DGPU_TARGETS="${{ matrix.gpu_targets }}" \
|
-DAMDGPU_TARGETS="${{ env.GPU_TARGETS }}" \
|
||||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
-DSD_BUILD_SHARED_LIBS=ON
|
-DSD_BUILD_SHARED_LIBS=ON
|
||||||
@ -810,6 +667,16 @@ jobs:
|
|||||||
cp ggml/LICENSE ./build/bin/ggml.txt
|
cp ggml/LICENSE ./build/bin/ggml.txt
|
||||||
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
|
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
|
||||||
|
|
||||||
|
# Move ROCm runtime libraries (to avoid double space consumption)
|
||||||
|
sudo mv /opt/rocm/lib/librocsparse.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/libhsa-runtime64.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/libamdhip64.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/libhipblas.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/libhipblaslt.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/librocblas.so* ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/rocblas/ ./build/bin/
|
||||||
|
sudo mv /opt/rocm/lib/hipblaslt/ ./build/bin/
|
||||||
|
|
||||||
- name: Fetch system info
|
- name: Fetch system info
|
||||||
id: system-info
|
id: system-info
|
||||||
run: |
|
run: |
|
||||||
@ -824,15 +691,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp ggml/LICENSE ./build/bin/ggml.txt
|
cp ggml/LICENSE ./build/bin/ggml.txt
|
||||||
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
|
cp LICENSE ./build/bin/stable-diffusion.cpp.txt
|
||||||
zip -y -r sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip ./build/bin
|
zip -y -r sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm.zip ./build/bin
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip
|
name: sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm.zip
|
||||||
path: |
|
path: |
|
||||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm-${{ matrix.ROCM_VERSION }}.zip
|
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-Ubuntu-${{ env.UBUNTU_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}-rocm.zip
|
||||||
|
|
||||||
release:
|
release:
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
|
||||||
@ -847,7 +714,6 @@ jobs:
|
|||||||
- macOS-latest-cmake
|
- macOS-latest-cmake
|
||||||
- windows-latest-cmake
|
- windows-latest-cmake
|
||||||
- windows-latest-cmake-hip
|
- windows-latest-cmake-hip
|
||||||
- windows-latest-rocm
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone
|
- name: Clone
|
||||||
|
|||||||
55
.github/workflows/stale-prs.yml
vendored
55
.github/workflows/stale-prs.yml
vendored
@ -1,55 +0,0 @@
|
|||||||
name: Close inactive PRs
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Run daily. GitHub cron schedules use UTC.
|
|
||||||
- cron: "30 1 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
debug_only:
|
|
||||||
description: "Dry run: log intended actions without changing PRs"
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale-prs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Mark and close inactive PRs
|
|
||||||
uses: actions/stale@v10
|
|
||||||
with:
|
|
||||||
days-before-issue-stale: -1
|
|
||||||
days-before-issue-close: -1
|
|
||||||
|
|
||||||
days-before-pr-stale: 365
|
|
||||||
days-before-pr-close: 7
|
|
||||||
|
|
||||||
stale-pr-label: pr:inactive
|
|
||||||
close-pr-label: pr:auto-closed
|
|
||||||
exempt-pr-labels: pr:keep-open
|
|
||||||
|
|
||||||
stale-pr-message: >
|
|
||||||
This PR has been inactive for 365 days. If there is no new activity
|
|
||||||
within 7 days, it will be closed automatically. Comment, push new
|
|
||||||
commits, or remove the pr:inactive label to keep it open. Add
|
|
||||||
pr:keep-open to exempt it from future inactive PR cleanup.
|
|
||||||
|
|
||||||
close-pr-message: >
|
|
||||||
Closing this PR because it has had no activity for 7 days after
|
|
||||||
being marked inactive. If this is still useful or ready to move
|
|
||||||
forward, feel free to reopen it with fresh context or updated
|
|
||||||
details. Sorry for any inconvenience.
|
|
||||||
|
|
||||||
remove-pr-stale-when-updated: true
|
|
||||||
delete-branch: false
|
|
||||||
operations-per-run: 100
|
|
||||||
debug-only: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_only || false }}
|
|
||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,6 +1,6 @@
|
|||||||
[submodule "ggml"]
|
[submodule "ggml"]
|
||||||
path = ggml
|
path = ggml
|
||||||
url = https://github.com/leejet/ggml.git
|
url = https://github.com/ggml-org/ggml.git
|
||||||
[submodule "examples/server/frontend"]
|
[submodule "examples/server/frontend"]
|
||||||
path = examples/server/frontend
|
path = examples/server/frontend
|
||||||
url = https://github.com/leejet/sdcpp-webui.git
|
url = https://github.com/leejet/sdcpp-webui.git
|
||||||
|
|||||||
@ -11,42 +11,11 @@ endif()
|
|||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||||
add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||||
add_compile_options(
|
|
||||||
$<$<COMPILE_LANGUAGE:C>:/MP>
|
|
||||||
$<$<COMPILE_LANGUAGE:C>:/utf-8>
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:/MP>
|
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:/utf-8>
|
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
function(sd_set_macos_rpaths target)
|
|
||||||
get_target_property(target_type ${target} TYPE)
|
|
||||||
if(target_type STREQUAL "EXECUTABLE")
|
|
||||||
set(runtime_paths "@executable_path" "@executable_path/../lib")
|
|
||||||
elseif(target_type STREQUAL "SHARED_LIBRARY" OR target_type STREQUAL "MODULE_LIBRARY")
|
|
||||||
set(runtime_paths "@loader_path" "@loader_path/../lib")
|
|
||||||
set_target_properties(${target} PROPERTIES
|
|
||||||
MACOSX_RPATH ON
|
|
||||||
INSTALL_NAME_DIR "@rpath"
|
|
||||||
BUILD_WITH_INSTALL_NAME_DIR ON
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Release artifacts zip the build output directly, so keep macOS rpaths relocatable.
|
|
||||||
set_target_properties(${target} PROPERTIES
|
|
||||||
BUILD_RPATH "${runtime_paths}"
|
|
||||||
INSTALL_RPATH "${runtime_paths}"
|
|
||||||
BUILD_WITH_INSTALL_RPATH ON
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
set(SD_STANDALONE ON)
|
set(SD_STANDALONE ON)
|
||||||
else()
|
else()
|
||||||
@ -96,46 +65,40 @@ option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" O
|
|||||||
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
||||||
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
set(CMAKE_C_STANDARD_REQUIRED true)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
|
||||||
|
|
||||||
if(SD_CUDA)
|
if(SD_CUDA)
|
||||||
message("-- Use CUDA as backend stable-diffusion")
|
message("-- Use CUDA as backend stable-diffusion")
|
||||||
set(GGML_CUDA ON)
|
set(GGML_CUDA ON)
|
||||||
|
add_definitions(-DSD_USE_CUDA)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SD_METAL)
|
if(SD_METAL)
|
||||||
message("-- Use Metal as backend stable-diffusion")
|
message("-- Use Metal as backend stable-diffusion")
|
||||||
set(GGML_METAL ON)
|
set(GGML_METAL ON)
|
||||||
|
add_definitions(-DSD_USE_METAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (SD_VULKAN)
|
if (SD_VULKAN)
|
||||||
message("-- Use Vulkan as backend stable-diffusion")
|
message("-- Use Vulkan as backend stable-diffusion")
|
||||||
set(GGML_VULKAN ON)
|
set(GGML_VULKAN ON)
|
||||||
|
add_definitions(-DSD_USE_VULKAN)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (SD_OPENCL)
|
if (SD_OPENCL)
|
||||||
message("-- Use OpenCL as backend stable-diffusion")
|
message("-- Use OpenCL as backend stable-diffusion")
|
||||||
set(GGML_OPENCL ON)
|
set(GGML_OPENCL ON)
|
||||||
|
add_definitions(-DSD_USE_OPENCL)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (SD_HIPBLAS)
|
if (SD_HIPBLAS)
|
||||||
message("-- Use HIPBLAS as backend stable-diffusion")
|
message("-- Use HIPBLAS as backend stable-diffusion")
|
||||||
set(GGML_HIP ON)
|
set(GGML_HIP ON)
|
||||||
# ggml-hip's device-stub objects must be position-independent, or the
|
add_definitions(-DSD_USE_CUDA)
|
||||||
# default-PIE sd-cli link fails with `relocation R_X86_64_32 ... cannot be
|
|
||||||
# used when making a PIE object` on distros that default to PIE
|
|
||||||
# (Ubuntu 24.04, Fedora 40+, Debian 12+). The shared-library branch below
|
|
||||||
# already sets this; the static build (the HIP default) did not.
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if(SD_MUSA)
|
if(SD_MUSA)
|
||||||
message("-- Use MUSA as backend stable-diffusion")
|
message("-- Use MUSA as backend stable-diffusion")
|
||||||
set(GGML_MUSA ON)
|
set(GGML_MUSA ON)
|
||||||
|
add_definitions(-DSD_USE_CUDA)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SD_WEBP)
|
if(SD_WEBP)
|
||||||
@ -145,8 +108,7 @@ if(SD_WEBP)
|
|||||||
"Or link against system library:\n cmake (...) -DSD_USE_SYSTEM_WEBP=ON")
|
"Or link against system library:\n cmake (...) -DSD_USE_SYSTEM_WEBP=ON")
|
||||||
endif()
|
endif()
|
||||||
if(SD_USE_SYSTEM_WEBP)
|
if(SD_USE_SYSTEM_WEBP)
|
||||||
find_package(WebP)
|
find_package(WebP REQUIRED)
|
||||||
if(WebP_FOUND)
|
|
||||||
add_library(webp ALIAS WebP::webp)
|
add_library(webp ALIAS WebP::webp)
|
||||||
# libwebp CMake target naming is not consistent across versions/distros.
|
# libwebp CMake target naming is not consistent across versions/distros.
|
||||||
# Some export WebP::libwebpmux, others export WebP::webpmux.
|
# Some export WebP::libwebpmux, others export WebP::webpmux.
|
||||||
@ -160,14 +122,6 @@ if(SD_WEBP)
|
|||||||
"Expected WebP::libwebpmux or WebP::webpmux."
|
"Expected WebP::libwebpmux or WebP::webpmux."
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
else()
|
|
||||||
find_package(PkgConfig REQUIRED)
|
|
||||||
pkg_check_modules(WebP REQUIRED IMPORTED_TARGET GLOBAL libwebp)
|
|
||||||
pkg_check_modules(WebPMux REQUIRED IMPORTED_TARGET GLOBAL libwebpmux)
|
|
||||||
link_libraries(PkgConfig::WebP)
|
|
||||||
link_libraries(PkgConfig::WebPMux)
|
|
||||||
add_library(libwebpmux ALIAS PkgConfig::WebPMux)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -181,13 +135,6 @@ if(SD_WEBM)
|
|||||||
"Or link against system library:\n cmake (...) -DSD_USE_SYSTEM_WEBM=ON")
|
"Or link against system library:\n cmake (...) -DSD_USE_SYSTEM_WEBM=ON")
|
||||||
endif()
|
endif()
|
||||||
if(SD_USE_SYSTEM_WEBM)
|
if(SD_USE_SYSTEM_WEBM)
|
||||||
find_package(PkgConfig)
|
|
||||||
if(PkgConfig_FOUND)
|
|
||||||
pkg_check_modules(WebM REQUIRED IMPORTED_TARGET GLOBAL libwebm)
|
|
||||||
endif()
|
|
||||||
if(PkgConfig_FOUND AND WebM_FOUND)
|
|
||||||
link_libraries(PkgConfig::WebM)
|
|
||||||
else()
|
|
||||||
find_path(WEBM_INCLUDE_DIR
|
find_path(WEBM_INCLUDE_DIR
|
||||||
NAMES mkvmuxer/mkvmuxer.h mkvparser/mkvparser.h common/webmids.h
|
NAMES mkvmuxer/mkvmuxer.h mkvparser/mkvparser.h common/webmids.h
|
||||||
PATH_SUFFIXES webm
|
PATH_SUFFIXES webm
|
||||||
@ -202,31 +149,13 @@ if(SD_WEBM)
|
|||||||
INTERFACE_INCLUDE_DIRECTORIES "${WEBM_INCLUDE_DIR}")
|
INTERFACE_INCLUDE_DIRECTORIES "${WEBM_INCLUDE_DIR}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
|
||||||
|
|
||||||
set(SD_LIB stable-diffusion)
|
set(SD_LIB stable-diffusion)
|
||||||
|
|
||||||
file(GLOB SD_LIB_SOURCES CONFIGURE_DEPENDS
|
file(GLOB SD_LIB_SOURCES
|
||||||
"src/*.h"
|
"src/*.h"
|
||||||
"src/*.cpp"
|
"src/*.cpp"
|
||||||
"src/*.hpp"
|
"src/*.hpp"
|
||||||
"src/conditioning/*.h"
|
|
||||||
"src/conditioning/*.cpp"
|
|
||||||
"src/conditioning/*.hpp"
|
|
||||||
"src/core/*.h"
|
|
||||||
"src/core/*.cpp"
|
|
||||||
"src/core/*.hpp"
|
|
||||||
"src/extensions/*.h"
|
|
||||||
"src/extensions/*.cpp"
|
|
||||||
"src/extensions/*.hpp"
|
|
||||||
"src/model/*/*.h"
|
|
||||||
"src/model/*/*.cpp"
|
|
||||||
"src/model/*/*.hpp"
|
|
||||||
"src/runtime/*.h"
|
|
||||||
"src/runtime/*.cpp"
|
|
||||||
"src/runtime/*.hpp"
|
|
||||||
"src/model_io/*.h"
|
|
||||||
"src/model_io/*.cpp"
|
|
||||||
"src/tokenizers/*.h"
|
"src/tokenizers/*.h"
|
||||||
"src/tokenizers/*.cpp"
|
"src/tokenizers/*.cpp"
|
||||||
"src/tokenizers/vocab/*.h"
|
"src/tokenizers/vocab/*.h"
|
||||||
@ -283,14 +212,11 @@ else()
|
|||||||
add_library(${SD_LIB} STATIC ${SD_LIB_SOURCES})
|
add_library(${SD_LIB} STATIC ${SD_LIB_SOURCES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
sd_set_macos_rpaths(${SD_LIB})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SD_SYCL)
|
if(SD_SYCL)
|
||||||
message("-- Use SYCL as backend stable-diffusion")
|
message("-- Use SYCL as backend stable-diffusion")
|
||||||
set(GGML_SYCL ON)
|
set(GGML_SYCL ON)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -fsycl")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -fsycl")
|
||||||
|
add_definitions(-DSD_USE_SYCL)
|
||||||
# disable fast-math on host, see:
|
# disable fast-math on host, see:
|
||||||
# https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-10/fp-model-fp.html
|
# https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-10/fp-model-fp.html
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@ -327,7 +253,6 @@ add_subdirectory(thirdparty)
|
|||||||
|
|
||||||
target_link_libraries(${SD_LIB} PUBLIC ggml zip)
|
target_link_libraries(${SD_LIB} PUBLIC ggml zip)
|
||||||
target_include_directories(${SD_LIB} PUBLIC . src include)
|
target_include_directories(${SD_LIB} PUBLIC . src include)
|
||||||
target_include_directories(${SD_LIB} PRIVATE src/core)
|
|
||||||
target_include_directories(${SD_LIB} PUBLIC . thirdparty)
|
target_include_directories(${SD_LIB} PUBLIC . thirdparty)
|
||||||
target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17)
|
target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17)
|
||||||
|
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
This document collects general contribution conventions for this repository.
|
|
||||||
|
|
||||||
## Before You Start
|
|
||||||
|
|
||||||
Before opening a PR, please search existing PRs to avoid duplicating ongoing work.
|
|
||||||
|
|
||||||
For large-scale refactors or changes with broad impact, please open an issue first to discuss the approach before submitting a PR.
|
|
||||||
|
|
||||||
If you want to update a third-party dependency, please open an issue first instead of submitting a direct PR. See [Dependency Updates](#dependency-updates) for details.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
Keep each PR focused on one clear change. Large or overly complex PRs are harder to review and may not be merged.
|
|
||||||
|
|
||||||
Follow Conventional Commit-style subjects seen in history: `feat:`, `fix:`, `refactor:`, `ci:`, `docs:`, `chore:`. Keep subjects imperative and scoped.
|
|
||||||
|
|
||||||
PRs should include:
|
|
||||||
|
|
||||||
- What changed and why (short problem/solution summary).
|
|
||||||
- Verification evidence when applicable (commands and key outputs).
|
|
||||||
- Linked issue/PR context when applicable.
|
|
||||||
- Screenshots or sample outputs for UI/visual behavior changes.
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
Format code according to the repository style before submitting changes.
|
|
||||||
|
|
||||||
Formatting follows `.clang-format` (Chromium base, 4-space indent, no tabs). Run `format-code.sh` before opening a PR. Keep C++ standard at C++17-compatible patterns used in this repo.
|
|
||||||
|
|
||||||
Naming conventions:
|
|
||||||
|
|
||||||
- Use `PascalCase` for class/struct/type names.
|
|
||||||
- In `PascalCase` names, preserve common abbreviations in uppercase, for example `SD`, `API`, `HTTP`, `JSON`, `RGB`, `VAE`, `TAE`, `LoRA`, and `WebP`.
|
|
||||||
- Use `snake_case` for functions, methods, variables, and file names unless an existing API requires a different style.
|
|
||||||
- Use a trailing underscore for private data member names, for example `hidden_size_` or `tokenizer_`.
|
|
||||||
- Use `.h` for C and C++ header files. Do not introduce new `.hpp` headers.
|
|
||||||
- Use macro-based header include guards instead of `#pragma once`.
|
|
||||||
- Format header include guards as `__SD_{PATH}__`, where `{PATH}` is the header path in uppercase snake case without the file extension. For example, `src/sample.h` should use `__SD_SAMPLE_H__`.
|
|
||||||
- Do not introduce anonymous namespaces in new or modified code; prefer `static` file-local functions/variables or an explicit named namespace when scoping is needed.
|
|
||||||
- In `class`/`struct` definitions, place data members before member functions unless an existing type already clearly follows a different pattern.
|
|
||||||
- Keep `test_*.cpp` / `test_*.py` naming for tests.
|
|
||||||
|
|
||||||
Some older code in the project may not fully follow the current conventions. Please do not submit PRs that only rewrite existing code to match style rules.
|
|
||||||
|
|
||||||
When adding or modifying model implementations, follow the model config and weight detection conventions in [docs/model_config.md](docs/model_config.md).
|
|
||||||
|
|
||||||
## AI-Assisted Contributions
|
|
||||||
|
|
||||||
AI tools may be used to assist development, but contributors are responsible for the quality and correctness of the submitted code.
|
|
||||||
|
|
||||||
If any part of a contribution was generated with AI assistance, the contributor must perform a thorough human review before submitting the PR and understand every changed line.
|
|
||||||
|
|
||||||
Do not list AI tools as co-authors. The human contributor is the sole responsible author of the submitted code.
|
|
||||||
|
|
||||||
Please do not submit AI-generated code that you do not understand, and do not include meaningless experiments, temporary test code, or unrelated generated output in a PR.
|
|
||||||
|
|
||||||
## Dependency Updates
|
|
||||||
|
|
||||||
Do not submit PRs that update `ggml`. `ggml` updates are performed only after local validation by the maintainer.
|
|
||||||
|
|
||||||
Other third-party dependencies are not updated unless necessary. If you want to update a dependency, please open an issue first instead of submitting a direct PR.
|
|
||||||
|
|
||||||
## Security & Configuration
|
|
||||||
|
|
||||||
Do not commit model weights, secrets, or local absolute paths. Keep large binaries out of git unless intentionally tracked release assets.
|
|
||||||
13
Dockerfile
13
Dockerfile
@ -2,18 +2,7 @@ ARG UBUNTU_VERSION=24.04
|
|||||||
|
|
||||||
FROM ubuntu:$UBUNTU_VERSION AS build
|
FROM ubuntu:$UBUNTU_VERSION AS build
|
||||||
|
|
||||||
# sd-server embeds the web UI at build time, so the build image needs Node/pnpm.
|
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake ca-certificates curl gnupg && \
|
|
||||||
mkdir -p /etc/apt/keyrings && \
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource-repo.gpg.key && \
|
|
||||||
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg /tmp/nodesource-repo.gpg.key && \
|
|
||||||
rm /tmp/nodesource-repo.gpg.key && \
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
npm install -g pnpm@10.15.1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /sd.cpp
|
WORKDIR /sd.cpp
|
||||||
|
|
||||||
|
|||||||
@ -3,31 +3,14 @@ ARG UBUNTU_VERSION=24.04
|
|||||||
|
|
||||||
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-devel-ubuntu${UBUNTU_VERSION} AS build
|
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-devel-ubuntu${UBUNTU_VERSION} AS build
|
||||||
|
|
||||||
# sd-server embeds the web UI at build time, so the build image needs Node/pnpm.
|
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git ccache cmake
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git ccache cmake ca-certificates curl gnupg && \
|
|
||||||
mkdir -p /etc/apt/keyrings && \
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource-repo.gpg.key && \
|
|
||||||
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg /tmp/nodesource-repo.gpg.key && \
|
|
||||||
rm /tmp/nodesource-repo.gpg.key && \
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
npm install -g pnpm@10.15.1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /sd.cpp
|
WORKDIR /sd.cpp
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ARG CUDACXX=/usr/local/cuda/bin/nvcc
|
ARG CUDACXX=/usr/local/cuda/bin/nvcc
|
||||||
ARG CUDA_ARCHITECTURES=""
|
RUN cmake . -B ./build -DSD_CUDA=ON
|
||||||
ARG GGML_CUDA_FA_ALL_QUANTS=""
|
|
||||||
|
|
||||||
RUN cmake . -B ./build \
|
|
||||||
-DSD_CUDA=ON \
|
|
||||||
${CUDA_ARCHITECTURES:+-DCMAKE_CUDA_ARCHITECTURES="${CUDA_ARCHITECTURES}"} \
|
|
||||||
${GGML_CUDA_FA_ALL_QUANTS:+-DGGML_CUDA_FA_ALL_QUANTS=${GGML_CUDA_FA_ALL_QUANTS}}
|
|
||||||
RUN cmake --build ./build --config Release -j$(nproc)
|
RUN cmake --build ./build --config Release -j$(nproc)
|
||||||
|
|
||||||
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-runtime-ubuntu${UBUNTU_VERSION} AS runtime
|
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-runtime-ubuntu${UBUNTU_VERSION} AS runtime
|
||||||
|
|||||||
@ -3,18 +3,7 @@ ARG UBUNTU_VERSION=22.04
|
|||||||
|
|
||||||
FROM mthreads/musa:${MUSA_VERSION}-devel-ubuntu${UBUNTU_VERSION}-amd64 as build
|
FROM mthreads/musa:${MUSA_VERSION}-devel-ubuntu${UBUNTU_VERSION}-amd64 as build
|
||||||
|
|
||||||
# sd-server embeds the web UI at build time, so the build image needs Node/pnpm.
|
RUN apt-get update && apt-get install -y ccache cmake git
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends ccache cmake git ca-certificates curl gnupg && \
|
|
||||||
mkdir -p /etc/apt/keyrings && \
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource-repo.gpg.key && \
|
|
||||||
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg /tmp/nodesource-repo.gpg.key && \
|
|
||||||
rm /tmp/nodesource-repo.gpg.key && \
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
npm install -g pnpm@10.15.1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /sd.cpp
|
WORKDIR /sd.cpp
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,8 @@
|
|||||||
# ggml SYCL hardware detection uses BMG G31/WCL architecture enums added in oneAPI 2025.3.
|
ARG SYCL_VERSION=2025.1.0-0
|
||||||
ARG SYCL_VERSION=2025.3.2-0
|
|
||||||
|
|
||||||
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS build
|
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS build
|
||||||
|
|
||||||
# sd-server embeds the web UI at build time, so the build image needs Node/pnpm.
|
RUN apt-get update && apt-get install -y cmake
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends cmake ca-certificates curl gnupg && \
|
|
||||||
mkdir -p /etc/apt/keyrings && \
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource-repo.gpg.key && \
|
|
||||||
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg /tmp/nodesource-repo.gpg.key && \
|
|
||||||
rm /tmp/nodesource-repo.gpg.key && \
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
npm install -g pnpm@10.15.1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /sd.cpp
|
WORKDIR /sd.cpp
|
||||||
|
|
||||||
|
|||||||
@ -2,18 +2,7 @@ ARG UBUNTU_VERSION=24.04
|
|||||||
|
|
||||||
FROM ubuntu:$UBUNTU_VERSION AS build
|
FROM ubuntu:$UBUNTU_VERSION AS build
|
||||||
|
|
||||||
# sd-server embeds the web UI at build time, so the build image needs Node/pnpm.
|
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake libvulkan-dev glslc
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake libvulkan-dev glslc spirv-headers ca-certificates curl gnupg && \
|
|
||||||
mkdir -p /etc/apt/keyrings && \
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /tmp/nodesource-repo.gpg.key && \
|
|
||||||
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg /tmp/nodesource-repo.gpg.key && \
|
|
||||||
rm /tmp/nodesource-repo.gpg.key && \
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
|
||||||
npm install -g pnpm@10.15.1 && \
|
|
||||||
apt-get clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /sd.cpp
|
WORKDIR /sd.cpp
|
||||||
|
|
||||||
|
|||||||
36
README.md
36
README.md
@ -15,18 +15,29 @@ API and command-line option may change frequently.***
|
|||||||
|
|
||||||
## 🔥Important News
|
## 🔥Important News
|
||||||
|
|
||||||
* **2026/06/04** 🚀 stable-diffusion.cpp now supports **Ideogram4**
|
|
||||||
* **2026/05/31** 🚀 stable-diffusion.cpp now supports **PiD**
|
|
||||||
* **2026/05/27** 🚀 stable-diffusion.cpp now supports **Lens**
|
|
||||||
* **2026/05/17** 🚀 stable-diffusion.cpp now supports **LTX-2.3**
|
|
||||||
* **2026/04/11** 🚀 stable-diffusion.cpp now uses a brand-new embedded web UI.
|
* **2026/04/11** 🚀 stable-diffusion.cpp now uses a brand-new embedded web UI.
|
||||||
|
👉 Details: [PR #1408](https://github.com/leejet/stable-diffusion.cpp/pull/1408)
|
||||||
|
|
||||||
* **2026/01/18** 🚀 stable-diffusion.cpp now supports **FLUX.2-klein**
|
* **2026/01/18** 🚀 stable-diffusion.cpp now supports **FLUX.2-klein**
|
||||||
|
👉 Details: [PR #1193](https://github.com/leejet/stable-diffusion.cpp/pull/1193)
|
||||||
|
|
||||||
* **2025/12/01** 🚀 stable-diffusion.cpp now supports **Z-Image**
|
* **2025/12/01** 🚀 stable-diffusion.cpp now supports **Z-Image**
|
||||||
|
👉 Details: [PR #1020](https://github.com/leejet/stable-diffusion.cpp/pull/1020)
|
||||||
|
|
||||||
* **2025/11/30** 🚀 stable-diffusion.cpp now supports **FLUX.2-dev**
|
* **2025/11/30** 🚀 stable-diffusion.cpp now supports **FLUX.2-dev**
|
||||||
|
👉 Details: [PR #1016](https://github.com/leejet/stable-diffusion.cpp/pull/1016)
|
||||||
|
|
||||||
* **2025/10/13** 🚀 stable-diffusion.cpp now supports **Qwen-Image-Edit / Qwen-Image-Edit 2509**
|
* **2025/10/13** 🚀 stable-diffusion.cpp now supports **Qwen-Image-Edit / Qwen-Image-Edit 2509**
|
||||||
|
👉 Details: [PR #877](https://github.com/leejet/stable-diffusion.cpp/pull/877)
|
||||||
|
|
||||||
* **2025/10/12** 🚀 stable-diffusion.cpp now supports **Qwen-Image**
|
* **2025/10/12** 🚀 stable-diffusion.cpp now supports **Qwen-Image**
|
||||||
|
👉 Details: [PR #851](https://github.com/leejet/stable-diffusion.cpp/pull/851)
|
||||||
|
|
||||||
* **2025/09/14** 🚀 stable-diffusion.cpp now supports **Wan2.1 Vace**
|
* **2025/09/14** 🚀 stable-diffusion.cpp now supports **Wan2.1 Vace**
|
||||||
|
👉 Details: [PR #819](https://github.com/leejet/stable-diffusion.cpp/pull/819)
|
||||||
|
|
||||||
* **2025/09/06** 🚀 stable-diffusion.cpp now supports **Wan2.1 / Wan2.2**
|
* **2025/09/06** 🚀 stable-diffusion.cpp now supports **Wan2.1 / Wan2.2**
|
||||||
|
👉 Details: [PR #778](https://github.com/leejet/stable-diffusion.cpp/pull/778)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -40,25 +51,18 @@ API and command-line option may change frequently.***
|
|||||||
- [SD3/SD3.5](./docs/sd3.md)
|
- [SD3/SD3.5](./docs/sd3.md)
|
||||||
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
||||||
- [FLUX.2-dev/FLUX.2-klein](./docs/flux2.md)
|
- [FLUX.2-dev/FLUX.2-klein](./docs/flux2.md)
|
||||||
- [Lens](./docs/lens.md)
|
|
||||||
- [Chroma](./docs/chroma.md)
|
- [Chroma](./docs/chroma.md)
|
||||||
- [Chroma1-Radiance](./docs/chroma_radiance.md)
|
- [Chroma1-Radiance](./docs/chroma_radiance.md)
|
||||||
- [Qwen Image](./docs/qwen_image.md)
|
- [Qwen Image](./docs/qwen_image.md)
|
||||||
- [PiD](./docs/pid.md)
|
|
||||||
- [LongCat Image](./docs/longcat_image.md)
|
|
||||||
- [Z-Image](./docs/z_image.md)
|
- [Z-Image](./docs/z_image.md)
|
||||||
- [Ovis-Image](./docs/ovis_image.md)
|
- [Ovis-Image](./docs/ovis_image.md)
|
||||||
- [Anima](./docs/anima.md)
|
- [Anima](./docs/anima.md)
|
||||||
- [ERNIE-Image](./docs/ernie_image.md)
|
- [ERNIE-Image](./docs/ernie_image.md)
|
||||||
- [HiDream-O1-Image](./docs/hidream_o1_image.md)
|
|
||||||
- [Ideogram4](./docs/ideogram4.md)
|
|
||||||
- Image Edit Models
|
- Image Edit Models
|
||||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||||
- [Qwen Image Edit series](./docs/qwen_image_edit.md)
|
- [Qwen Image Edit series](./docs/qwen_image_edit.md)
|
||||||
- [LongCat Image Edit](./docs/longcat_image.md)
|
|
||||||
- Video Models
|
- Video Models
|
||||||
- [Wan2.1/Wan2.2](./docs/wan.md)
|
- [Wan2.1/Wan2.2](./docs/wan.md)
|
||||||
- [LTX-2.3](./docs/ltx2.md)
|
|
||||||
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker) support.
|
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker) support.
|
||||||
- Control Net support with SD 1.5
|
- Control Net support with SD 1.5
|
||||||
- LoRA support, same as [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#lora)
|
- LoRA support, same as [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#lora)
|
||||||
@ -73,10 +77,9 @@ API and command-line option may change frequently.***
|
|||||||
- OpenCL
|
- OpenCL
|
||||||
- SYCL
|
- SYCL
|
||||||
- Supported weight formats
|
- Supported weight formats
|
||||||
- Pytorch checkpoint (`.ckpt` or `.pth` or `.pt`)
|
- Pytorch checkpoint (`.ckpt` or `.pth`)
|
||||||
- Safetensors (`.safetensors`)
|
- Safetensors (`.safetensors`)
|
||||||
- GGUF (`.gguf`)
|
- GGUF (`.gguf`)
|
||||||
- Convert mode supports converting model weights to `.gguf` or `.safetensors`
|
|
||||||
- Supported platforms
|
- Supported platforms
|
||||||
- Linux
|
- Linux
|
||||||
- Mac OS
|
- Mac OS
|
||||||
@ -128,11 +131,9 @@ API and command-line option may change frequently.***
|
|||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
If you want to improve performance or reduce VRAM/RAM usage, please refer to [performance guide](./docs/performance.md).
|
If you want to improve performance or reduce VRAM/RAM usage, please refer to [performance guide](./docs/performance.md).
|
||||||
For runtime and parameter backend placement, see the [backend selection guide](./docs/backend.md).
|
|
||||||
|
|
||||||
## More Guides
|
## More Guides
|
||||||
|
|
||||||
- [Backend selection](./docs/backend.md)
|
|
||||||
- [SD1.x/SD2.x/SDXL](./docs/sd.md)
|
- [SD1.x/SD2.x/SDXL](./docs/sd.md)
|
||||||
- [SD3/SD3.5](./docs/sd3.md)
|
- [SD3/SD3.5](./docs/sd3.md)
|
||||||
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
|
||||||
@ -142,14 +143,10 @@ For runtime and parameter backend placement, see the [backend selection guide](.
|
|||||||
- [🔥Qwen Image](./docs/qwen_image.md)
|
- [🔥Qwen Image](./docs/qwen_image.md)
|
||||||
- [🔥Qwen Image Edit series](./docs/qwen_image_edit.md)
|
- [🔥Qwen Image Edit series](./docs/qwen_image_edit.md)
|
||||||
- [🔥Wan2.1/Wan2.2](./docs/wan.md)
|
- [🔥Wan2.1/Wan2.2](./docs/wan.md)
|
||||||
- [🔥LTX-2.3](./docs/ltx2.md)
|
|
||||||
- [🔥Z-Image](./docs/z_image.md)
|
- [🔥Z-Image](./docs/z_image.md)
|
||||||
- [Ovis-Image](./docs/ovis_image.md)
|
- [Ovis-Image](./docs/ovis_image.md)
|
||||||
- [Anima](./docs/anima.md)
|
- [Anima](./docs/anima.md)
|
||||||
- [ERNIE-Image](./docs/ernie_image.md)
|
- [ERNIE-Image](./docs/ernie_image.md)
|
||||||
- [HiDream-O1-Image](./docs/hidream_o1_image.md)
|
|
||||||
- [Lens](./docs/lens.md)
|
|
||||||
- [LongCat Image / LongCat Image Edit](./docs/longcat_image.md)
|
|
||||||
- [LoRA](./docs/lora.md)
|
- [LoRA](./docs/lora.md)
|
||||||
- [LCM/LCM-LoRA](./docs/lcm.md)
|
- [LCM/LCM-LoRA](./docs/lcm.md)
|
||||||
- [Using PhotoMaker to personalize image generation](./docs/photo_maker.md)
|
- [Using PhotoMaker to personalize image generation](./docs/photo_maker.md)
|
||||||
@ -165,7 +162,6 @@ These projects wrap `stable-diffusion.cpp` for easier use in other languages/fra
|
|||||||
|
|
||||||
* Golang (non-cgo): [seasonjs/stable-diffusion](https://github.com/seasonjs/stable-diffusion)
|
* Golang (non-cgo): [seasonjs/stable-diffusion](https://github.com/seasonjs/stable-diffusion)
|
||||||
* Golang (cgo): [Binozo/GoStableDiffusion](https://github.com/Binozo/GoStableDiffusion)
|
* Golang (cgo): [Binozo/GoStableDiffusion](https://github.com/Binozo/GoStableDiffusion)
|
||||||
* Golang (non-cgo): [l8bloom/gosd](https://github.com/l8bloom/gosd)
|
|
||||||
* C#: [DarthAffe/StableDiffusion.NET](https://github.com/DarthAffe/StableDiffusion.NET)
|
* C#: [DarthAffe/StableDiffusion.NET](https://github.com/DarthAffe/StableDiffusion.NET)
|
||||||
* Python: [william-murray1204/stable-diffusion-cpp-python](https://github.com/william-murray1204/stable-diffusion-cpp-python)
|
* Python: [william-murray1204/stable-diffusion-cpp-python](https://github.com/william-murray1204/stable-diffusion-cpp-python)
|
||||||
* Rust: [newfla/diffusion-rs](https://github.com/newfla/diffusion-rs)
|
* Rust: [newfla/diffusion-rs](https://github.com/newfla/diffusion-rs)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 630 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 555 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 423 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 9.0 MiB |
122
docs/backend.md
122
docs/backend.md
@ -1,122 +0,0 @@
|
|||||||
# Backend selection
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
If `--params-backend` is not set, parameters use the same backend as their module runtime backend.
|
|
||||||
|
|
||||||
## Syntax
|
|
||||||
|
|
||||||
A backend assignment can be a single backend name:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sd-cli -m model.safetensors -p "a cat" --backend cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
This applies to every module that does not have a more specific assignment.
|
|
||||||
|
|
||||||
Assignments can also target individual modules:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sd-cli -m model.safetensors -p "a cat" --backend te=cpu,vae=cuda0,diffusion=vulkan0
|
|
||||||
```
|
|
||||||
|
|
||||||
The same syntax is used for parameter placement:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sd-cli -m model.safetensors -p "a cat" --backend cuda0 --params-backend te=cpu,vae=cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sd-cli -m model.safetensors -p "a cat" --backend all=cuda0,te=cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
## Modules
|
|
||||||
|
|
||||||
| Module | Purpose | Accepted names |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `diffusion` | UNet, DiT, MMDiT, Flux, Wan, Qwen Image, and other diffusion models | `diffusion`, `model`, `unet`, `dit` |
|
|
||||||
| `te` | Text encoders and conditioners | `te`, `clip`, `text`, `textencoder`, `textencoders`, `conditioner`, `cond`, `llm`, `t5`, `t5xxl` |
|
|
||||||
| `clip_vision` | CLIP vision encoder | `clip_vision`, `clipvision`, `clip-vision`, `vision` |
|
|
||||||
| `vae` | VAE and TAE | `vae`, `firststage`, `autoencoder`, `tae` |
|
|
||||||
| `controlnet` | ControlNet | `controlnet`, `control` |
|
|
||||||
| `photomaker` | PhotoMaker ID encoder and PhotoMaker LoRA | `photomaker`, `photomakerid`, `pmid`, `photo` |
|
|
||||||
| `upscaler` | ESRGAN upscaler | `upscaler`, `esrgan`, `hires` |
|
|
||||||
|
|
||||||
`te` is the preferred module name for text encoders. `clip` is kept as an accepted alias because many existing commands and model names use CLIP terminology.
|
|
||||||
|
|
||||||
## Backend names
|
|
||||||
|
|
||||||
Backend names are resolved against the GGML backend device list. Matching is case-insensitive and accepts exact names or unique prefixes, so common values include names such as:
|
|
||||||
|
|
||||||
- `cpu`
|
|
||||||
- `cuda0`
|
|
||||||
- `vulkan0`
|
|
||||||
- `metal`
|
|
||||||
|
|
||||||
The special values `auto`, `default`, and an empty backend name select the default backend. The default preference is GPU, then integrated GPU, then CPU.
|
|
||||||
|
|
||||||
The special value `gpu` selects the first GPU backend, falling back to the first integrated GPU backend.
|
|
||||||
|
|
||||||
## Runtime backend vs. parameter backend
|
|
||||||
|
|
||||||
The runtime backend controls where graph execution runs. The parameter backend controls where model weights are allocated.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
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.
|
|
||||||
|
|
||||||
Per-module assignments can be mixed:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sd-cli -m model.safetensors -p "a cat" --backend diffusion=cuda0,te=cpu,vae=cpu --params-backend diffusion=cuda0,te=cpu,vae=cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
This keeps text encoding and VAE execution on CPU while the diffusion model runs on GPU.
|
|
||||||
|
|
||||||
## Backend sharing and lifetime
|
|
||||||
|
|
||||||
Backends are managed by `SDBackendManager`.
|
|
||||||
|
|
||||||
Within one manager, backend instances are cached by resolved backend device name. If multiple modules request the same backend, they share the same `ggml_backend_t`.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
--backend te=cpu,vae=cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
`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:
|
|
||||||
|
|
||||||
- `--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`.
|
|
||||||
|
|
||||||
`--offload-to-cpu` affects parameter backend assignment only when `--params-backend` is not set. It is equivalent to:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
--params-backend cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
Explicit `--backend` and `--params-backend` assignments are preferred for new commands.
|
|
||||||
@ -102,11 +102,6 @@ cmake --build . --config Release
|
|||||||
## Build with Vulkan
|
## Build with Vulkan
|
||||||
|
|
||||||
Install Vulkan SDK from https://www.lunarg.com/vulkan-sdk/.
|
Install Vulkan SDK from https://www.lunarg.com/vulkan-sdk/.
|
||||||
On Ubuntu, install the Vulkan development packages and SPIR-V headers:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo apt-get install build-essential libvulkan-dev glslc spirv-headers
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
|
|||||||
@ -131,6 +131,8 @@ sd-cli -m model.safetensors -p "a cat" --cache-mode spectrum
|
|||||||
| `warmup` | Steps to always compute before caching starts | 4 |
|
| `warmup` | Steps to always compute before caching starts | 4 |
|
||||||
| `stop` | Stop caching at this fraction of total steps | 0.9 |
|
| `stop` | Stop caching at this fraction of total steps | 0.9 |
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### Performance Tips
|
### Performance Tips
|
||||||
|
|
||||||
- Start with default thresholds and adjust based on output quality
|
- Start with default thresholds and adjust based on output quality
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download HiDream-O1-Image-Dev
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/HiDream-O1-Image/tree/main/checkpoints
|
|
||||||
- Download HiDream-O1-Image
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/HiDream-O1-Image/tree/main/checkpoints
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### HiDream-O1-Image-Dev
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -m ..\..\ComfyUI\models\diffusion_models\hidream_o1_image_dev_bf16.safetensors -p "a lovely cat holding a sign says
|
|
||||||
'hidream o1 cpp'" --cfg-scale 1.0 -v -H 1024 -W 1024
|
|
||||||
```
|
|
||||||
|
|
||||||
<img width="256" alt="HiDream-O1-Image-Dev example" src="../assets/hidream-o1/dev_example.png" />
|
|
||||||
|
|
||||||
@ -26,12 +26,12 @@ Fortunately, `AMD` provides complete help documentation, you can use the help do
|
|||||||
|
|
||||||
Then we must set `ROCM` as environment variables before running cmake.
|
Then we must set `ROCM` as environment variables before running cmake.
|
||||||
|
|
||||||
Usually if you install according to the official tutorial and do not modify the ROCM path, then there is a high probability that it is here `C:\Program Files\AMD\ROCm\7.1.1\bin`
|
Usually if you install according to the official tutorial and do not modify the ROCM path, then there is a high probability that it is here `C:\Program Files\AMD\ROCm\5.5\bin`
|
||||||
|
|
||||||
This is what I use to set the clang:
|
This is what I use to set the clang:
|
||||||
```Commandline
|
```Commandline
|
||||||
set CC=C:\Program Files\AMD\ROCm\7.1.1\bin\clang.exe
|
set CC=C:\Program Files\AMD\ROCm\5.5\bin\clang.exe
|
||||||
set CXX=C:\Program Files\AMD\ROCm\7.1.1\bin\clang++.exe
|
set CXX=C:\Program Files\AMD\ROCm\5.5\bin\clang++.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ninja
|
## Ninja
|
||||||
@ -46,7 +46,7 @@ set ninja=C:\Program Files\ninja\ninja.exe
|
|||||||
## Building stable-diffusion.cpp
|
## Building stable-diffusion.cpp
|
||||||
|
|
||||||
The thing different from the regular CPU build is `-DSD_HIPBLAS=ON` ,
|
The thing different from the regular CPU build is `-DSD_HIPBLAS=ON` ,
|
||||||
`-G "Ninja"`, `-DCMAKE_C_COMPILER=clang`, `-DCMAKE_CXX_COMPILER=clang++`, `-DAMDGPU_TARGETS=gfx1150;gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032`
|
`-G "Ninja"`, `-DCMAKE_C_COMPILER=clang`, `-DCMAKE_CXX_COMPILER=clang++`, `-DAMDGPU_TARGETS=gfx1100`
|
||||||
|
|
||||||
>**Notice**: check the `clang` and `clang++` information:
|
>**Notice**: check the `clang` and `clang++` information:
|
||||||
```Commandline
|
```Commandline
|
||||||
@ -59,29 +59,26 @@ If you see like this, we can continue:
|
|||||||
clang version 17.0.0 (git@github.amd.com:Compute-Mirrors/llvm-project e3201662d21c48894f2156d302276eb1cf47c7be)
|
clang version 17.0.0 (git@github.amd.com:Compute-Mirrors/llvm-project e3201662d21c48894f2156d302276eb1cf47c7be)
|
||||||
Target: x86_64-pc-windows-msvc
|
Target: x86_64-pc-windows-msvc
|
||||||
Thread model: posix
|
Thread model: posix
|
||||||
InstalledDir: C:\Program Files\AMD\ROCm\7.1.1\bin
|
InstalledDir: C:\Program Files\AMD\ROCm\5.5\bin
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
clang version 17.0.0 (git@github.amd.com:Compute-Mirrors/llvm-project e3201662d21c48894f2156d302276eb1cf47c7be)
|
clang version 17.0.0 (git@github.amd.com:Compute-Mirrors/llvm-project e3201662d21c48894f2156d302276eb1cf47c7be)
|
||||||
Target: x86_64-pc-windows-msvc
|
Target: x86_64-pc-windows-msvc
|
||||||
Thread model: posix
|
Thread model: posix
|
||||||
InstalledDir: C:\Program Files\AMD\ROCm\7.1.1\bin
|
InstalledDir: C:\Program Files\AMD\ROCm\5.5\bin
|
||||||
```
|
```
|
||||||
|
|
||||||
>**Notice** that the GPU targets are now compatible with multiple GPU architectures (ROCm 7.1.1 targets). You can change them to match your GPU architecture. Click here to see your architecture [LLVM Target](https://rocm.docs.amd.com/en/latest/release/windows_support.html#windows-supported-gpus)
|
>**Notice** that the `gfx1100` is the GPU architecture of my GPU, you can change it to your GPU architecture. Click here to see your architecture [LLVM Target](https://rocm.docs.amd.com/en/latest/release/windows_support.html#windows-supported-gpus)
|
||||||
|
|
||||||
Examples:
|
My GPU is AMD Radeon™ RX 7900 XTX Graphics, so I set it to `gfx1100`.
|
||||||
- AMD Radeon™ RX 7900 XTX Graphics: `gfx1100`
|
|
||||||
- AMD Radeon™ RX 7900 XT Graphics: `gfx1101`
|
|
||||||
- AMD Radeon™ RX 7900 GRE Graphics: `gfx1102`
|
|
||||||
|
|
||||||
option:
|
option:
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS="gfx1150;gfx1151;gfx1200;gfx1201;gfx1100;gfx1101;gfx1102;gfx1030;gfx1031;gfx1032"
|
cmake .. -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS=gfx1100
|
||||||
cmake --build . --config Release
|
cmake --build . --config Release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download Ideogram4
|
|
||||||
- safetensors: https://huggingface.co/ideogram-ai/ideogram-4-fp8/tree/main/transformer
|
|
||||||
- Download Ideogram4 uncond
|
|
||||||
- safetensors: https://huggingface.co/ideogram-ai/ideogram-4-fp8/tree/main/unconditional_transformer
|
|
||||||
- Download vae
|
|
||||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
|
|
||||||
- Download Qwen3-VL-8B-Instruct
|
|
||||||
- gguf: https://huggingface.co/unsloth/Qwen3-VL-8B-Instruct-GGUF/tree/main
|
|
||||||
|
|
||||||
## Convert weights
|
|
||||||
|
|
||||||
fp8 scale -> bf16
|
|
||||||
|
|
||||||
```
|
|
||||||
python .\convert_fp8_scale_to_bf16.py --input .\ideogram4_fp8.safetensors --output ideogram4_bf16.safetensors
|
|
||||||
python .\convert_fp8_scale_to_bf16.py --input .\ideogram4_uncond_fp8.safetensors --output ideogram4_uncond_bf16.safetensors
|
|
||||||
```
|
|
||||||
|
|
||||||
bf16 -> q8
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -M convert -m ideogram4_bf16.safetensors -o ideogram4-Q8_0.gguf --tensor-type-rules "^layers.*adaln_modulation.*weight=q8_0,layers.*attention.o.*weight=q8_0,layers.*attention.qkv.*weight=q8_0,layers.*feed_forward.*weight=q8_0" -v
|
|
||||||
|
|
||||||
.\bin\Release\sd-cli.exe -M convert -m ideogram4_uncond_bf16.safetensors -o ideogram4_uncond-Q8_0.gguf --tensor-type-rules "^layers.*adaln_modulation.*weight=q8_0,layers.*attention.o.*weight=q8_0,layers.*attention.qkv.*weight=q8_0,layers.*feed_forward.*weight=q8_0" -v
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want lower VRAM usage, you can change the quantization from q8_0 to a lower-level quantization, such as q4_0.
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```sh
|
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model ideogram4-Q8_0.gguf --uncond-diffusion-model ideogram4_uncond-Q8_0.gguf --llm ..\..\llm\Qwen3VL-8B-Instruct-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors -p '{"high_level_description":"A square 1024 x 1024 luxury fashion magazine cover featuring exactly one short chubby fluffy cat as the main model. The cat sits on a soft ivory studio floor, facing the viewer with a stylish calm expression, wearing tiny black sunglasses, a red silk scarf, and a small gold collar charm. In front of the cat on the floor is a wide horizontal luxury nameplate that clearly reads ideogram4.cpp. The whole design feels premium, fashionable, clean, and editorial.","style_description":{"aesthetics":"luxury fashion magazine cover, high-end pet couture campaign, minimalist editorial design, elegant studio photography, soft paper texture, refined typography, fashionable and polished","lighting":"Soft diffused studio lighting, gentle spotlight on the cat, subtle floor shadow, warm ivory highlights, clean separation between subject and background","photo":"high-resolution fashion editorial photography look, front-facing cat portrait, crisp fur details, glossy sunglasses, clear readable nameplate text, shallow depth of field","medium":"mixed media fashion photography and premium editorial graphic design","color_palette":["#F4EFE7","#111111","#D8B56D","#B73A3A","#FFFFFF","#8A7A6A"]},"compositional_deconstruction":{"canvas":"Square 1024 x 1024 canvas with a normal upright orientation. Do not rotate the poster or any text. Use a clean fashion magazine cover layout.","background":"Warm ivory studio backdrop with subtle paper grain, a soft spotlight gradient, faint floor shadow, and a few minimal gold editorial lines. The background is spacious, premium, and uncluttered.","layout":"Top center has a small elegant headline. Center area features one cat as the main fashion model. Lower foreground has a wide horizontal luxury nameplate placed on the floor in front of the cat. Bottom center has a small footer. All text is horizontal, upright, and readable left to right.","elements":[{"type":"text","desc":"Top center headline reading LOOK WHAT I FOUND in a refined high-fashion serif font. The headline is horizontal, centered, elegant, and secondary to the nameplate text."},{"type":"obj","desc":"Exactly one short chubby fluffy cat sitting in the center like a luxury fashion model. The cat has a large round head, compact body, short legs, soft detailed fur, expressive eyes, and a calm confident pose. The cat is cute and rounded, not tall, not stretched, not duplicated."},{"type":"obj","desc":"Tiny glossy black sunglasses worn naturally by the cat, slightly oversized but still showing the cat face clearly. The sunglasses add a chic fashion-editorial attitude."},{"type":"obj","desc":"A red silk scarf tied neatly around the cat neck, with soft folds and a couture feeling. The scarf must not cover the cat face or the nameplate."},{"type":"obj","desc":"A small gold collar charm or fashion accessory under the scarf, subtle and premium, adding a luxury campaign detail."},{"type":"obj","desc":"In the lower foreground, place a wide horizontal luxury nameplate on the floor in front of the cat. The nameplate is low, flat, landscape-oriented, much wider than tall, like a fashion show seat card or premium display plaque. It is centered, front-facing, level, and fully visible. It must not become vertical, tall, standing, rotated, or side-facing."},{"type":"text","desc":"Print the exact text ideogram4.cpp only on the wide horizontal nameplate. Use clean bold black lettering, perfectly spelled, lowercase, with the number 4 and .cpp extension. The text must fit completely inside the nameplate, stay horizontal, and be readable from left to right."},{"type":"obj","desc":"Add sparse premium editorial accents around the edges: thin gold lines, small code brackets, tiny cursor marks, subtle dots, and minimal geometric details. No extra cats, no stickers, no animal faces, no busy decorations."},{"type":"text","desc":"Bottom center footer reading tiny paws, big compile energy in a small refined monospace or editorial font. The footer is horizontal, centered, understated, and much smaller than the nameplate text."}]}}' --diffusion-fa -v --offload-to-cpu -H 1024 -W 1024
|
|
||||||
```
|
|
||||||
|
|
||||||
<img alt="ideogram4 image example" src="../assets/ideogram4/example.png" />
|
|
||||||
32
docs/lens.md
32
docs/lens.md
@ -1,32 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
Lens uses a Lens diffusion transformer, the FLUX.2 VAE, and GPT-OSS-20B as the LLM text encoder.
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download Lens
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/Lens/tree/main/diffusion_models
|
|
||||||
- Download Lens Turbo
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/Lens/tree/main/diffusion_models
|
|
||||||
- Download vae
|
|
||||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
|
|
||||||
- Download GPT-OSS-20B
|
|
||||||
- gguf: https://huggingface.co/unsloth/gpt-oss-20b-GGUF/tree/main
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Lens
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\lens_bf16.safetensors --llm "..\..\llm\gpt-oss-20b-UD-Q8_K_XL.gguf" --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --cfg-scale 5.0 -p "A crystal dragon soaring through an aurora borealis sky, its entire body made of transparent faceted crystal refracting the green and purple aurora light into rainbow spectra, ice particles trailing from its wings, high fantasy digital art" --diffusion-fa -v
|
|
||||||
```
|
|
||||||
|
|
||||||
<img width="256" alt="Lens example" src="../assets/lens/example.png" />
|
|
||||||
|
|
||||||
### Lens Turbo
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\lens_turbo_bf16.safetensors --llm "..\..\llm\gpt-oss-20b-UD-Q8_K_XL.gguf" --vae ..\..\ComfyUI\models\vae\flux2_ae.safetensors --cfg-scale 1.0 -p "A crystal dragon soaring through an aurora borealis sky, its entire body made of transparent faceted crystal refracting the green and purple aurora light into rainbow spectra, ice particles trailing from its wings, high fantasy digital art" --diffusion-fa -v --steps 4
|
|
||||||
```
|
|
||||||
|
|
||||||
<img width="256" alt="Lens Turbo example" src="../assets/lens/turbo_example.png" />
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
LongCat-Image uses a LongCat diffusion transformer, the FLUX VAE, and Qwen2.5-VL as the LLM text encoder.
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download LongCat Image
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/LongCat-Image/tree/main/split_files/diffusion_models
|
|
||||||
- gguf: https://huggingface.co/vantagewithai/LongCat-Image-GGUF/tree/main/comfy
|
|
||||||
- Download LongCat Image Edit
|
|
||||||
- LongCat Image Edit Turbo: https://huggingface.co/meituan-longcat/LongCat-Image-Edit-Turbo
|
|
||||||
- gguf: https://huggingface.co/vantagewithai/LongCat-Image-Edit-GGUF/tree/main
|
|
||||||
- Download vae
|
|
||||||
- safetensors: https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/ae.safetensors
|
|
||||||
- Download qwen_2.5_vl 7b
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main/split_files/text_encoders
|
|
||||||
- gguf: https://huggingface.co/mradermacher/Qwen2.5-VL-7B-Instruct-GGUF/tree/main
|
|
||||||
- For image editing with GGUF text encoders, also download the matching mmproj file and pass it with `--llm_vision`.
|
|
||||||
|
|
||||||
## Run
|
|
||||||
|
|
||||||
LongCat uses quoted text for character-level text rendering. Put target text inside single quotes, double quotes, or Chinese quotes.
|
|
||||||
|
|
||||||
### LongCat Image
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\LongCat-Image-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf -p "a lovely cat holding a sign says 'longcat.cpp'" --cfg-scale 5.0 --sampling-method euler --flow-shift 3 -v --offload-to-cpu --diffusion-fa
|
|
||||||
```
|
|
||||||
|
|
||||||
<img alt="longcat example" src="../assets/longcat/example.png" />
|
|
||||||
77
docs/ltx2.md
77
docs/ltx2.md
@ -1,77 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download LTX-2.3
|
|
||||||
- safetensors: https://huggingface.co/Kijai/LTX2.3_comfy/tree/main/diffusion_models
|
|
||||||
- gguf: https://huggingface.co/unsloth/LTX-2.3-GGUF/tree/main
|
|
||||||
- Download gemma-3-12b-it
|
|
||||||
- gguf: https://huggingface.co/unsloth/gemma-3-12b-it-GGUF/tree/main
|
|
||||||
- Download embeddings connectors
|
|
||||||
- safetensors: https://huggingface.co/unsloth/LTX-2.3-GGUF/tree/main/text_encoders
|
|
||||||
- Download vae
|
|
||||||
- safetensors: https://huggingface.co/unsloth/LTX-2.3-GGUF/tree/main/vae
|
|
||||||
- Download audio vae
|
|
||||||
- safetensors: https://huggingface.co/unsloth/LTX-2.3-GGUF/tree/main/vae
|
|
||||||
- Download LTX spatial latent upscaler
|
|
||||||
- safetensors: https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-spatial-upscaler-x2-1.1.safetensors
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### LTX-2.3 dev T2V
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\ltx-2.3-22b-dev-UD-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_video_vae.safetensors --audio-vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_audio_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\gemma-3-12b-it-qat-UD-Q4_K_XL.gguf --embeddings-connectors ..\..\ComfyUI\models\text_encoders\ltx-2.3-22b-dev_embeddings_connectors.safetensors -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "worst quality, low quality, blurry, distorted, artifacts" -W 1280 -H 720 --diffusion-fa --offload-to-cpu --video-frames 33 --fps 24 -o t2v.webm
|
|
||||||
```
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="../assets/ltx2/t2v.webm"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
style="max-width: 100%; height: auto;"></video>
|
|
||||||
|
|
||||||
### LTX-2.3 dev I2V
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\ltx-2.3-22b-dev-UD-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_video_vae.safetensors --audio-vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_audio_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\gemma-3-12b-it-qat-UD-Q4_K_XL.gguf --embeddings-connectors ..\..\ComfyUI\models\text_encoders\ltx-2.3-22b-dev_embeddings_connectors.safetensors -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -W 1280 -H 720 --diffusion-fa --offload-to-cpu --video-frames 33 -i ..\assets\ernie_image\turbo_example.png -o i2v.webm
|
|
||||||
```
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="../assets/ltx2/i2v.webm"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
style="max-width: 100%; height: auto;"></video>
|
|
||||||
|
|
||||||
### LTX-2.3 dev FLF2V
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\ltx-2.3-22b-dev-UD-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_video_vae.safetensors --audio-vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_audio_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\gemma-3-12b-it-qat-UD-Q4_K_XL.gguf --embeddings-connectors ..\..\ComfyUI\models\text_encoders\ltx-2.3-22b-dev_embeddings_connectors.safetensors -p "glass flower blossom" --cfg-scale 6.0 --sampling-method euler -v -W 1280 -H 720 --diffusion-fa --offload-to-cpu --video-frames 33 --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png -o flf2v.webm
|
|
||||||
```
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="../assets/ltx2/flf2v.webm"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
style="max-width: 100%; height: auto;"></video>
|
|
||||||
|
|
||||||
### LTX-2.3 spatial latent upscale
|
|
||||||
|
|
||||||
LTX spatial latent upscale runs a model-backed x2 latent upsampler between the low-resolution video pass and the high-resolution refine pass. `-W` and `-H` are the pre-upscale generation size; the spatial upsampler produces x2 latent dimensions.
|
|
||||||
|
|
||||||
Put `ltx-2.3-spatial-upscaler-x2-1.1.safetensors` under the directory passed to `--hires-upscalers-dir`, then use the model name without path or extension in `--hires-upscaler`.
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\ltx-2.3-22b-dev-UD-Q4_K_M.gguf --vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_video_vae.safetensors --audio-vae ..\..\ComfyUI\models\vae\ltx-2.3-22b-dev_audio_vae.safetensors --llm ..\..\ComfyUI\models\text_encoders\gemma-3-12b-it-qat-UD-Q4_K_XL.gguf --embeddings-connectors ..\..\ComfyUI\models\text_encoders\ltx-2.3-22b-dev_embeddings_connectors.safetensors --hires-upscalers-dir ..\..\ComfyUI\models\latent_upscale_models --hires-upscaler ltx-2.3-spatial-upscaler-x2-1.1 --hires --hires-steps 4 -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -W 640 -H 360 --diffusion-fa --offload-to-cpu --video-frames 33 -i ..\assets\ernie_image\turbo_example.png -o hires_i2v.webm
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, the hires refine pass uses the main sampler and scheduler, then trims the second-pass sigma schedule by `--hires-denoising-strength` (`0.7` by default). To reproduce a ComfyUI-style explicit refine schedule, pass custom hires sigmas:
|
|
||||||
|
|
||||||
```
|
|
||||||
--hires-sigmas "0.85,0.725,0.421875,0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="../assets/ltx2/hires_i2v.webm"
|
|
||||||
controls
|
|
||||||
muted
|
|
||||||
style="max-width: 100%; height: auto;"></video>
|
|
||||||
@ -1,118 +0,0 @@
|
|||||||
# Model Configuration Conventions
|
|
||||||
|
|
||||||
This document describes the conventions for model configuration structs and
|
|
||||||
weight-based configuration detection.
|
|
||||||
|
|
||||||
## Config Types
|
|
||||||
|
|
||||||
Model configuration should live in a model-specific `*Config` struct.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- `ZImageConfig`
|
|
||||||
- `UNetConfig`
|
|
||||||
- `MMDiTConfig`
|
|
||||||
- `LLMConfig`
|
|
||||||
|
|
||||||
Preserve established acronym casing in type names, such as `UNet`, `MMDiT`,
|
|
||||||
`LLM`, `VAE`, and `T5`.
|
|
||||||
|
|
||||||
Place the config struct near the top of the model header, before the main model
|
|
||||||
blocks and runner types that consume it.
|
|
||||||
|
|
||||||
## Config Variables
|
|
||||||
|
|
||||||
Variables and members that hold a config should be named `config`.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
UNetConfig config;
|
|
||||||
UnetModelBlock unet;
|
|
||||||
|
|
||||||
MMDiTRunner(...)
|
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
|
||||||
config(MMDiTConfig::detect_from_weights(tensor_storage_map, prefix)),
|
|
||||||
mmdit(config) {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Avoid alternate names such as `params`, `params_cfg`, `model_params`, or
|
|
||||||
model-specific aliases unless an existing public API requires them.
|
|
||||||
|
|
||||||
## Weight Detection
|
|
||||||
|
|
||||||
If a model can derive configuration from loaded weight metadata, expose that
|
|
||||||
logic as a static method on the config type:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
static XxxConfig detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
|
||||||
const std::string& prefix);
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional selector arguments are allowed when required by an existing model
|
|
||||||
family, for example `SDVersion version` or an architecture enum:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
static UNetConfig detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
|
||||||
const std::string& prefix,
|
|
||||||
SDVersion version = VERSION_SD1);
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `TensorStorage` metadata, especially `n_dims` and `ne`, to infer shapes.
|
|
||||||
Do not load or parse tensor data for config detection.
|
|
||||||
|
|
||||||
Detection should respect `prefix`. For nested weights, construct full names from
|
|
||||||
`prefix + "." + suffix` or filter entries with `starts_with(name, prefix)`.
|
|
||||||
|
|
||||||
Do not add persistent config fields such as `inferred_from_weights` only to
|
|
||||||
record whether detection happened. If the function needs to decide whether to
|
|
||||||
print a debug line, keep that as local control flow inside `detect_from_weights`.
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
When config values are inferred from weights, print one `LOG_DEBUG` line at the
|
|
||||||
end of `detect_from_weights`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
LOG_DEBUG("llm: num_layers = %" PRId64 ", vocab_size = %" PRId64 ", hidden_size = %" PRId64 ", intermediate_size = %" PRId64,
|
|
||||||
config.num_layers,
|
|
||||||
config.vocab_size,
|
|
||||||
config.hidden_size,
|
|
||||||
config.intermediate_size);
|
|
||||||
```
|
|
||||||
|
|
||||||
Only print the config detection log when the function actually inferred values
|
|
||||||
from weights. Do not duplicate the same config summary in runner constructors or
|
|
||||||
model loading code.
|
|
||||||
|
|
||||||
Use the correct format specifiers for field types, such as `%" PRId64 "` for
|
|
||||||
`int64_t` and `%d` for `int`.
|
|
||||||
|
|
||||||
## Runner And Model Responsibilities
|
|
||||||
|
|
||||||
Runners should detect the config once and pass it into the model block:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct XxxRunner : public DiffusionModelRunner {
|
|
||||||
XxxConfig config;
|
|
||||||
XxxModel model;
|
|
||||||
|
|
||||||
XxxRunner(..., const String2TensorStorage& tensor_storage_map, const std::string prefix)
|
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
|
||||||
config(XxxConfig::detect_from_weights(tensor_storage_map, prefix)),
|
|
||||||
model(config) {
|
|
||||||
model.init(params_ctx, tensor_storage_map, prefix);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Model blocks should consume `config` directly instead of re-scanning weights in
|
|
||||||
their constructors. Keep config-derived behavior centralized in the config
|
|
||||||
struct.
|
|
||||||
|
|
||||||
If a model has no weight-derived config today, it may still provide
|
|
||||||
`detect_from_weights` for API consistency, but it should not print a config
|
|
||||||
detection log unless it actually derives values from weights.
|
|
||||||
39
docs/pid.md
39
docs/pid.md
@ -1,39 +0,0 @@
|
|||||||
# How to Use
|
|
||||||
|
|
||||||
PiD is NVIDIA's Pixel Diffusion Decoder. It replaces the usual VAE decode or decode-then-upscale path with a pixel-space diffusion decoder conditioned on a
|
|
||||||
source latent and text prompt.
|
|
||||||
|
|
||||||
In stable-diffusion.cpp, PiD currently runs as an image edit pipeline: provide a reference image with `-r`/`--ref-image`, encode that image with a matching VAE, then let the PiD diffusion model decode/upscale directly to RGB.
|
|
||||||
|
|
||||||
## Download weights
|
|
||||||
|
|
||||||
- Download PiD
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/PixelDiT/tree/main/diffusion_models
|
|
||||||
- Download Gemma 2 2B
|
|
||||||
- safetensors: https://huggingface.co/Comfy-Org/PixelDiT/tree/main/text_encoders
|
|
||||||
- Download the VAE that matches the PiD checkpoint backbone
|
|
||||||
- safetensors: https://huggingface.co/nvidia/PiD/tree/main/checkpoints
|
|
||||||
- Flux / Z-Image PiD: use the Flux VAE and pass `--vae-format flux`
|
|
||||||
- SD3 PiD: use the SD3 VAE and pass `--vae-format sd3`
|
|
||||||
- Flux.2 PiD: use the Flux.2 VAE and pass `--vae-format flux2`
|
|
||||||
|
|
||||||
The official PiD model card should be checked before use. At the time of the initial PiD release, the official weights are under the NSCLv1 non-commercial license.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```
|
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\pid_flux1_512_to_2048_4step_bf16.safetensors --llm "..\..\ComfyUI\models\text_encoders\gemma_2_2b_it_elm_bf16.safetensors" --vae ..\..\ComfyUI\models\vae\ae.sft --vae-format flux --cfg-scale 1.0 -p "a lovely cat" -r ..\assets\ernie_image\turbo_example.png --diffusion-fa -v --steps 4 -H 2048 -W 2048 --rng cpu
|
|
||||||
```
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
<img width="256" alt="ERNIE-Image Turbo example" src="../assets/ernie_image/turbo_example.png" />
|
|
||||||
|
|
||||||
After:
|
|
||||||
<img width="1024" alt="PiD example" src="../assets/pid/example.png" />
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- `-r`/`--ref-image` is required. PiD uses the first reference image as the source latent condition.
|
|
||||||
- `--vae-format` should match the VAE latent layout used by the PiD checkpoint. This is important when using standalone VAE files because the PiD diffusion
|
|
||||||
checkpoint alone does not identify the VAE format.
|
|
||||||
@ -21,7 +21,7 @@ You can run Z-Image with stable-diffusion.cpp on GPUs with 4GB of VRAM — or ev
|
|||||||
### Z-Image-Turbo
|
### Z-Image-Turbo
|
||||||
|
|
||||||
```
|
```
|
||||||
.\bin\Release\sd-cli.exe --diffusion-model z_image_turbo-Q3_K.gguf --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\Qwen3-4B-Instruct-2507-Q4_K_M.gguf -p "A cinematic, melancholic photograph of a solitary hooded figure walking through a sprawling, rain-slicked metropolis at night. The city lights are a chaotic blur of neon orange and cool blue, reflecting on the wet asphalt. The scene evokes a sense of being a single component in a vast machine. Superimposed over the image in a sleek, modern, slightly glitched font is the philosophical quote: 'THE CITY IS A CIRCUIT BOARD, AND I AM A BROKEN TRANSISTOR.' -- moody, atmospheric, profound, dark academic" --cfg-scale 1.0 -v --offload-to-cpu --diffusion-fa -H 1024 -W 512 --steps 8
|
.\bin\Release\sd-cli.exe --diffusion-model z_image_turbo-Q3_K.gguf --vae ..\..\ComfyUI\models\vae\ae.sft --llm ..\..\ComfyUI\models\text_encoders\Qwen3-4B-Instruct-2507-Q4_K_M.gguf -p "A cinematic, melancholic photograph of a solitary hooded figure walking through a sprawling, rain-slicked metropolis at night. The city lights are a chaotic blur of neon orange and cool blue, reflecting on the wet asphalt. The scene evokes a sense of being a single component in a vast machine. Superimposed over the image in a sleek, modern, slightly glitched font is the philosophical quote: 'THE CITY IS A CIRCUIT BOARD, AND I AM A BROKEN TRANSISTOR.' -- moody, atmospheric, profound, dark academic" --cfg-scale 1.0 -v --offload-to-cpu --diffusion-fa -H 1024 -W 512
|
||||||
```
|
```
|
||||||
|
|
||||||
<img width="256" alt="z-image example" src="../assets/z_image/q3_K.png" />
|
<img width="256" alt="z-image example" src="../assets/z_image/q3_K.png" />
|
||||||
|
|||||||
@ -7,13 +7,6 @@ add_executable(${TARGET}
|
|||||||
image_metadata.cpp
|
image_metadata.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
if(APPLE)
|
|
||||||
sd_set_macos_rpaths(${TARGET})
|
|
||||||
endif()
|
|
||||||
target_include_directories(${TARGET} PRIVATE
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/.."
|
|
||||||
"${PROJECT_SOURCE_DIR}/src"
|
|
||||||
)
|
|
||||||
install(TARGETS ${TARGET} RUNTIME)
|
install(TARGETS ${TARGET} RUNTIME)
|
||||||
target_link_libraries(${TARGET} PRIVATE stable-diffusion zip ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(${TARGET} PRIVATE stable-diffusion zip ${CMAKE_THREAD_LIBS_INIT})
|
||||||
if(SD_WEBP)
|
if(SD_WEBP)
|
||||||
|
|||||||
@ -4,17 +4,14 @@
|
|||||||
usage: ./bin/sd-cli [options]
|
usage: ./bin/sd-cli [options]
|
||||||
|
|
||||||
CLI Options:
|
CLI Options:
|
||||||
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image
|
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image sequences (default:
|
||||||
sequences (default: ./output.png) (eg. output_%03d.png). Single-file video outputs
|
./output.png) (eg. output_%03d.png). For video generation, single-file outputs support .avi, .webm, and animated .webp
|
||||||
support .avi, .webm, and animated .webp
|
--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)
|
||||||
--image <string> path to the image to inspect (for metadata mode)
|
--image <string> path to the image to inspect (for metadata mode)
|
||||||
--metadata-format <string> metadata output format, one of [text, json] (default: text)
|
--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)
|
--canny apply canny preprocessor (edge detection)
|
||||||
--convert-name convert tensor name (for convert mode)
|
--convert-name convert tensor name (for convert mode)
|
||||||
-v, --verbose print extra info
|
-v, --verbose print extra info
|
||||||
@ -34,34 +31,28 @@ Context Options:
|
|||||||
--clip_g <string> path to the clip-g text encoder
|
--clip_g <string> path to the clip-g text encoder
|
||||||
--clip_vision <string> path to the clip-vision encoder
|
--clip_vision <string> path to the clip-vision encoder
|
||||||
--t5xxl <string> path to the t5xxl text encoder
|
--t5xxl <string> path to the t5xxl text encoder
|
||||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image,
|
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image, mistral-small3.2 for flux2, ...)
|
||||||
mistral-small3.2 for flux2, ...)
|
|
||||||
--llm_vision <string> path to the llm vit
|
--llm_vision <string> path to the llm vit
|
||||||
--qwen2vl <string> alias of --llm. Deprecated.
|
--qwen2vl <string> alias of --llm. Deprecated.
|
||||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||||
--diffusion-model <string> path to the standalone diffusion model
|
--diffusion-model <string> path to the standalone diffusion model
|
||||||
--high-noise-diffusion-model <string> path to the standalone high noise 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
|
--vae <string> path to standalone vae model
|
||||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||||
--tae <string> alias of --taesd
|
--tae <string> alias of --taesd
|
||||||
--control-net <string> path to control net model
|
--control-net <string> path to control net model
|
||||||
--embd-dir <string> embeddings directory
|
--embd-dir <string> embeddings directory
|
||||||
--lora-model-dir <string> lora model 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")
|
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||||
--photo-maker <string> path to PHOTOMAKER model
|
--photo-maker <string> path to PHOTOMAKER model
|
||||||
--upscale-model <string> path to esrgan model.
|
--upscale-model <string> path to esrgan model.
|
||||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0,
|
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||||
then threads will be set to the number of CPU physical cores
|
CPU physical cores
|
||||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
--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
|
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||||
graph splitting; a negative value auto-detects free VRAM, sparing the
|
--vae-tiling process vae in tiles to reduce memory usage
|
||||||
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
|
--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
|
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||||
when needed
|
|
||||||
--mmap whether to memory-map model
|
--mmap whether to memory-map model
|
||||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||||
--clip-on-cpu keep clip in cpu (for low vram)
|
--clip-on-cpu keep clip in cpu (for low vram)
|
||||||
@ -76,19 +67,20 @@ Context Options:
|
|||||||
--chroma-disable-dit-mask disable dit mask for chroma
|
--chroma-disable-dit-mask disable dit mask for chroma
|
||||||
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
||||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
--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,
|
--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
|
||||||
q4_K). If not specified, the default is the type of the weight file
|
type of the weight file
|
||||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
--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
|
--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,
|
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow, flux2_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
|
||||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is
|
contain any quantized parameters, the at_runtime mode will be used; otherwise,
|
||||||
auto. In auto mode, if the model weights contain any quantized parameters,
|
immediately will be used.The immediately mode may have precision and
|
||||||
the at_runtime mode will be used; otherwise, immediately will be used.The
|
compatibility issues with quantized parameters, but it usually offers faster inference
|
||||||
immediately mode may have precision and compatibility issues with quantized
|
speed and, in some cases, lower memory usage. The at_runtime mode, on the
|
||||||
parameters, but it usually offers faster inference speed and, in some cases,
|
other hand, is exactly the opposite.
|
||||||
lower memory usage. The at_runtime mode, on the other hand, is exactly the
|
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||||
opposite.
|
--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)
|
||||||
|
|
||||||
Generation Options:
|
Generation Options:
|
||||||
-p, --prompt <string> the prompt to render
|
-p, --prompt <string> the prompt to render
|
||||||
@ -97,108 +89,69 @@ Generation Options:
|
|||||||
--end-img <string> path to the end image, required by flf2v
|
--end-img <string> path to the end image, required by flf2v
|
||||||
--mask <string> path to the mask image
|
--mask <string> path to the mask image
|
||||||
--control-image <string> path to control image, control net
|
--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
|
--control-video <string> path to control video frames, It must be a directory path. The video frames inside should be stored as images in
|
||||||
inside should be stored as images in lexicographical (character) order. For
|
lexicographical (character) order. For example, if the control video path is
|
||||||
example, if the control video path is `frames`, the directory contain images
|
`frames`, the directory contain images such as 00.png, 01.png, ... etc.
|
||||||
such as 00.png, 01.png, ... etc.
|
|
||||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
--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)
|
-H, --height <int> image height, in pixel space (default: 512)
|
||||||
-W, --width <int> image width, in pixel space (default: 512)
|
-W, --width <int> image width, in pixel space (default: 512)
|
||||||
--steps <int> number of sample steps (default: 20)
|
--steps <int> number of sample steps (default: 20)
|
||||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
--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
|
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1). <= 0 represents unspecified,
|
||||||
(default: -1). <= 0 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
|
will be 1 for SD1.x, 2 for SD2.x
|
||||||
-b, --batch-count <int> batch count
|
-b, --batch-count <int> batch count
|
||||||
--video-frames <int> video frames (default: 1)
|
--video-frames <int> video frames (default: 1)
|
||||||
--fps <int> fps (default: 24)
|
--fps <int> fps (default: 24)
|
||||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for
|
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for NitroSD-Realism around 250 and 500 for
|
||||||
NitroSD-Realism around 250 and 500 for NitroSD-Vibrant
|
NitroSD-Vibrant
|
||||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
--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)
|
--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
|
--img-cfg-scale <float> image guidance scale for inpaint or instruct-pix2pix models: (default: same as --cfg-scale)
|
||||||
--cfg-scale)
|
|
||||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
--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
|
--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
|
||||||
disabled, a value of 2.5 is nice for sd3.5 medium
|
medium
|
||||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||||
--eta <float> noise multiplier (default: 0 for ddim_trailing, tcd, res_multistep and
|
--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)
|
||||||
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)
|
--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-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:
|
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||||
same as --cfg-scale)
|
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input (default: 3.5)
|
||||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input
|
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||||
(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-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-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,
|
--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)
|
||||||
res_multistep and res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
|
||||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||||
--pm-style-strength <float>
|
--pm-style-strength <float>
|
||||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full
|
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||||
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
|
||||||
--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
|
--vace-strength <float> wan vace strength
|
||||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||||
--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-auto-resize-ref-image disable auto resize of ref images
|
||||||
--disable-image-metadata do not embed generation metadata on image files
|
--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)
|
-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,
|
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||||
dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s,
|
tcd, res_multistep, res_2s, er_sde] (default: euler for Flux/SD3/Wan, euler_a
|
||||||
er_sde, euler_cfg_pp, euler_a_cfg_pp] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
otherwise)
|
||||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a,
|
--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,
|
||||||
dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep,
|
ddim_trailing, tcd, res_multistep, res_2s, er_sde] default: euler for Flux/SD3/Wan,
|
||||||
res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp] default: euler for Flux/SD3/Wan, euler_a otherwise
|
euler_a otherwise
|
||||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits,
|
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||||
smoothstep, sgm_uniform, simple, kl_optimal, lcm, bong_tangent, ltx2], default:
|
kl_optimal, lcm, bong_tangent], default: discrete
|
||||||
model-specific
|
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||||
--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])
|
--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])
|
--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)
|
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET),
|
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level),
|
||||||
'dbcache'/'taylorseer'/'cache-dit' (DiT block-level), 'spectrum' (UNET/DiT
|
'spectrum' (UNET/DiT Chebyshev+Taylor forecasting)
|
||||||
Chebyshev+Taylor forecasting)
|
|
||||||
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit:
|
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=;
|
||||||
Fn=,Bn=,threshold=,warmup=; spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=.
|
spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=. Examples:
|
||||||
Examples: "threshold=0.25" or "threshold=1.5,reset=0"
|
"threshold=0.25" or "threshold=1.5,reset=0" or "w=0.4,window=2"
|
||||||
--scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g.,
|
--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
|
||||||
"1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache
|
|
||||||
--scm-policy SCM policy: 'dynamic' (default) or 'static'
|
--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)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Metadata mode inspects PNG/JPEG container metadata without loading any model:
|
Metadata mode inspects PNG/JPEG container metadata without loading any model:
|
||||||
|
|||||||
@ -169,9 +169,8 @@ struct SDCliParams {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto on_help_arg = [&](int argc, const char** argv, int index, bool& valid) {
|
auto on_help_arg = [&](int argc, const char** argv, int index) {
|
||||||
normal_exit = true;
|
normal_exit = true;
|
||||||
valid = true;
|
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -279,9 +278,7 @@ void parse_args(int argc, const char** argv, SDCliParams& cli_params, SDContextP
|
|||||||
bool valid = cli_params.resolve_and_validate();
|
bool valid = cli_params.resolve_and_validate();
|
||||||
if (valid && cli_params.mode != METADATA) {
|
if (valid && cli_params.mode != METADATA) {
|
||||||
valid = ctx_params.resolve_and_validate(cli_params.mode) &&
|
valid = ctx_params.resolve_and_validate(cli_params.mode) &&
|
||||||
gen_params.resolve_and_validate(cli_params.mode,
|
gen_params.resolve_and_validate(cli_params.mode, ctx_params.lora_model_dir);
|
||||||
ctx_params.lora_model_dir,
|
|
||||||
ctx_params.hires_upscalers_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
@ -386,32 +383,11 @@ std::string format_frame_idx(std::string pattern, int frame_idx) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fs::path get_video_audio_sidecar_path(const SDCliParams& cli_params) {
|
|
||||||
fs::path out_path = cli_params.output_path;
|
|
||||||
fs::path base_path = out_path;
|
|
||||||
fs::path ext = out_path.has_extension() ? out_path.extension() : fs::path{};
|
|
||||||
std::string ext_lower = ext.string();
|
|
||||||
std::transform(ext_lower.begin(), ext_lower.end(), ext_lower.begin(), ::tolower);
|
|
||||||
const EncodedImageFormat output_format = encoded_image_format_from_path(out_path.string());
|
|
||||||
if (!ext.empty()) {
|
|
||||||
if (output_format == EncodedImageFormat::JPEG ||
|
|
||||||
output_format == EncodedImageFormat::PNG ||
|
|
||||||
output_format == EncodedImageFormat::WEBP ||
|
|
||||||
ext_lower == ".avi" ||
|
|
||||||
ext_lower == ".webm") {
|
|
||||||
base_path.replace_extension();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base_path += ".wav";
|
|
||||||
return base_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool save_results(const SDCliParams& cli_params,
|
bool save_results(const SDCliParams& cli_params,
|
||||||
const SDContextParams& ctx_params,
|
const SDContextParams& ctx_params,
|
||||||
const SDGenerationParams& gen_params,
|
const SDGenerationParams& gen_params,
|
||||||
sd_image_t* results,
|
sd_image_t* results,
|
||||||
int num_results,
|
int num_results) {
|
||||||
const sd_audio_t* generated_audio = nullptr) {
|
|
||||||
if (results == nullptr || num_results <= 0) {
|
if (results == nullptr || num_results <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -455,30 +431,14 @@ bool save_results(const SDCliParams& cli_params,
|
|||||||
if (!img.data)
|
if (!img.data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const int64_t metadata_seed = cli_params.mode == VID_GEN ? gen_params.seed : gen_params.seed + idx;
|
|
||||||
std::string params = gen_params.embed_image_metadata
|
std::string params = gen_params.embed_image_metadata
|
||||||
? get_image_params(ctx_params, gen_params, metadata_seed, cli_params.mode)
|
? get_image_params(ctx_params, gen_params, gen_params.seed + idx)
|
||||||
: "";
|
: "";
|
||||||
const bool ok = write_image_to_file(path.string(), img.data, img.width, img.height, img.channel, params, 90);
|
const bool ok = write_image_to_file(path.string(), img.data, img.width, img.height, img.channel, params, 90);
|
||||||
LOG_INFO("save result image %d to '%s' (%s)", idx, path.string().c_str(), ok ? "success" : "failure");
|
LOG_INFO("save result image %d to '%s' (%s)", idx, path.string().c_str(), ok ? "success" : "failure");
|
||||||
return ok;
|
return ok;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto write_audio_sidecar = [&](const fs::path& wav_path) {
|
|
||||||
if (generated_audio == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (write_wav_to_file(wav_path.string(),
|
|
||||||
generated_audio->data,
|
|
||||||
generated_audio->sample_count,
|
|
||||||
generated_audio->channels,
|
|
||||||
generated_audio->sample_rate)) {
|
|
||||||
LOG_INFO("save result audio to '%s'", wav_path.string().c_str());
|
|
||||||
} else {
|
|
||||||
LOG_WARN("failed to save result audio to '%s'", wav_path.string().c_str());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int sucessful_reults = 0;
|
int sucessful_reults = 0;
|
||||||
|
|
||||||
if (std::regex_search(cli_params.output_path, format_specifier_regex)) {
|
if (std::regex_search(cli_params.output_path, format_specifier_regex)) {
|
||||||
@ -502,16 +462,8 @@ bool save_results(const SDCliParams& cli_params,
|
|||||||
ext = ".avi";
|
ext = ".avi";
|
||||||
fs::path video_path = base_path;
|
fs::path video_path = base_path;
|
||||||
video_path += ext;
|
video_path += ext;
|
||||||
std::string final_ext_lower = ext.string();
|
if (create_video_from_sd_images(video_path.string().c_str(), results, num_results, gen_params.fps) == 0) {
|
||||||
std::transform(final_ext_lower.begin(), final_ext_lower.end(), final_ext_lower.begin(), ::tolower);
|
|
||||||
const bool mux_audio = generated_audio != nullptr && (final_ext_lower == ".avi" || final_ext_lower == ".webm");
|
|
||||||
if (create_video_from_sd_images(video_path.string().c_str(), results, num_results, gen_params.fps, 90, mux_audio ? generated_audio : nullptr) == 0) {
|
|
||||||
LOG_INFO("save result video to '%s'", video_path.string().c_str());
|
LOG_INFO("save result video to '%s'", video_path.string().c_str());
|
||||||
if (generated_audio != nullptr && !mux_audio) {
|
|
||||||
fs::path wav_path = video_path;
|
|
||||||
wav_path.replace_extension(".wav");
|
|
||||||
write_audio_sidecar(wav_path);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to save result video to '%s'", video_path.string().c_str());
|
LOG_ERROR("Failed to save result video to '%s'", video_path.string().c_str());
|
||||||
@ -533,9 +485,6 @@ bool save_results(const SDCliParams& cli_params,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_INFO("%d/%d images saved", sucessful_reults, num_results);
|
LOG_INFO("%d/%d images saved", sucessful_reults, num_results);
|
||||||
if (generated_audio != nullptr) {
|
|
||||||
write_audio_sidecar(get_video_audio_sidecar_path(cli_params));
|
|
||||||
}
|
|
||||||
return sucessful_reults != 0;
|
return sucessful_reults != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,18 +688,10 @@ int main(int argc, const char* argv[]) {
|
|||||||
vae_decode_only = false;
|
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(vae_decode_only, true, cli_params.taesd_preview);
|
||||||
|
|
||||||
SDImageVec results;
|
SDImageVec results;
|
||||||
int num_results = 0;
|
int num_results = 0;
|
||||||
sd_audio_t* generated_audio = nullptr;
|
|
||||||
|
|
||||||
if (cli_params.mode == UPSCALE) {
|
if (cli_params.mode == UPSCALE) {
|
||||||
num_results = 1;
|
num_results = 1;
|
||||||
@ -782,10 +723,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
results.adopt(generate_image(sd_ctx.get(), &img_gen_params), num_results);
|
results.adopt(generate_image(sd_ctx.get(), &img_gen_params), num_results);
|
||||||
} else if (cli_params.mode == VID_GEN) {
|
} else if (cli_params.mode == VID_GEN) {
|
||||||
sd_vid_gen_params_t vid_gen_params = gen_params.to_sd_vid_gen_params_t();
|
sd_vid_gen_params_t vid_gen_params = gen_params.to_sd_vid_gen_params_t();
|
||||||
sd_image_t* generated_video = nullptr;
|
sd_image_t* generated_video = generate_video(sd_ctx.get(), &vid_gen_params, &num_results);
|
||||||
if (!generate_video(sd_ctx.get(), &vid_gen_params, &generated_video, &num_results, &generated_audio)) {
|
|
||||||
generated_video = nullptr;
|
|
||||||
}
|
|
||||||
results.adopt(generated_video, num_results);
|
results.adopt(generated_video, num_results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,9 +739,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
ctx_params.offload_params_to_cpu,
|
ctx_params.offload_params_to_cpu,
|
||||||
ctx_params.diffusion_conv_direct,
|
ctx_params.diffusion_conv_direct,
|
||||||
ctx_params.n_threads,
|
ctx_params.n_threads,
|
||||||
gen_params.upscale_tile_size,
|
gen_params.upscale_tile_size));
|
||||||
ctx_params.backend.c_str(),
|
|
||||||
ctx_params.params_backend.c_str()));
|
|
||||||
|
|
||||||
if (upscaler_ctx == nullptr) {
|
if (upscaler_ctx == nullptr) {
|
||||||
LOG_ERROR("new_upscaler_ctx failed");
|
LOG_ERROR("new_upscaler_ctx failed");
|
||||||
@ -827,12 +763,9 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!save_results(cli_params, ctx_params, gen_params, results.data(), num_results, generated_audio)) {
|
if (!save_results(cli_params, ctx_params, gen_params, results.data(), num_results)) {
|
||||||
free_sd_audio(generated_audio);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_sd_audio(generated_audio);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -56,42 +56,11 @@ struct BoolOption {
|
|||||||
bool* target;
|
bool* target;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ManualFunction {
|
|
||||||
std::function<int(int, const char**, int, bool&)> _func;
|
|
||||||
|
|
||||||
ManualFunction() = default;
|
|
||||||
|
|
||||||
ManualFunction(std::function<int(int argc, const char** argv, int index, bool& valid)> func)
|
|
||||||
: _func(std::move(func)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
ManualFunction(F func)
|
|
||||||
: _func(make_function(func)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
int operator()(int argc, const char** argv, int index, bool& valid) const {
|
|
||||||
return _func(argc, argv, index, valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename F>
|
|
||||||
static std::function<int(int, const char**, int, bool&)> make_function(F func) {
|
|
||||||
if constexpr (std::is_invocable_v<F, int, const char**, int, bool&>) {
|
|
||||||
return func;
|
|
||||||
} else {
|
|
||||||
return [func](int argc, const char** argv, int index, bool&) {
|
|
||||||
return func(argc, argv, index);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ManualOption {
|
struct ManualOption {
|
||||||
std::string short_name;
|
std::string short_name;
|
||||||
std::string long_name;
|
std::string long_name;
|
||||||
std::string desc;
|
std::string desc;
|
||||||
ManualFunction cb;
|
std::function<int(int argc, const char** argv, int index)> cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArgOptions {
|
struct ArgOptions {
|
||||||
@ -123,11 +92,7 @@ struct SDContextParams {
|
|||||||
std::string llm_vision_path;
|
std::string llm_vision_path;
|
||||||
std::string diffusion_model_path;
|
std::string diffusion_model_path;
|
||||||
std::string high_noise_diffusion_model_path;
|
std::string high_noise_diffusion_model_path;
|
||||||
std::string uncond_diffusion_model_path;
|
|
||||||
std::string embeddings_connectors_path;
|
|
||||||
std::string vae_path;
|
std::string vae_path;
|
||||||
std::string vae_format = "auto";
|
|
||||||
std::string audio_vae_path;
|
|
||||||
std::string taesd_path;
|
std::string taesd_path;
|
||||||
std::string esrgan_path;
|
std::string esrgan_path;
|
||||||
std::string control_net_path;
|
std::string control_net_path;
|
||||||
@ -136,7 +101,6 @@ struct SDContextParams {
|
|||||||
sd_type_t wtype = SD_TYPE_COUNT;
|
sd_type_t wtype = SD_TYPE_COUNT;
|
||||||
std::string tensor_type_rules;
|
std::string tensor_type_rules;
|
||||||
std::string lora_model_dir = ".";
|
std::string lora_model_dir = ".";
|
||||||
std::string hires_upscalers_dir;
|
|
||||||
|
|
||||||
std::map<std::string, std::string> embedding_map;
|
std::map<std::string, std::string> embedding_map;
|
||||||
std::vector<sd_embedding_t> embedding_vec;
|
std::vector<sd_embedding_t> embedding_vec;
|
||||||
@ -144,10 +108,6 @@ struct SDContextParams {
|
|||||||
rng_type_t rng_type = CUDA_RNG;
|
rng_type_t rng_type = CUDA_RNG;
|
||||||
rng_type_t sampler_rng_type = RNG_TYPE_COUNT;
|
rng_type_t sampler_rng_type = RNG_TYPE_COUNT;
|
||||||
bool offload_params_to_cpu = false;
|
bool offload_params_to_cpu = false;
|
||||||
float max_vram = 0.f;
|
|
||||||
bool stream_layers = false;
|
|
||||||
std::string backend;
|
|
||||||
std::string params_backend;
|
|
||||||
bool enable_mmap = false;
|
bool enable_mmap = false;
|
||||||
bool control_net_cpu = false;
|
bool control_net_cpu = false;
|
||||||
bool clip_on_cpu = false;
|
bool clip_on_cpu = false;
|
||||||
@ -206,8 +166,6 @@ struct SDGenerationParams {
|
|||||||
|
|
||||||
sd_sample_params_t sample_params;
|
sd_sample_params_t sample_params;
|
||||||
sd_sample_params_t high_noise_sample_params;
|
sd_sample_params_t high_noise_sample_params;
|
||||||
std::string extra_sample_args;
|
|
||||||
std::string high_noise_extra_sample_args;
|
|
||||||
std::vector<int> skip_layers = {7, 8, 9};
|
std::vector<int> skip_layers = {7, 8, 9};
|
||||||
std::vector<int> high_noise_skip_layers = {7, 8, 9};
|
std::vector<int> high_noise_skip_layers = {7, 8, 9};
|
||||||
|
|
||||||
@ -223,8 +181,7 @@ struct SDGenerationParams {
|
|||||||
int video_frames = 1;
|
int video_frames = 1;
|
||||||
int fps = 16;
|
int fps = 16;
|
||||||
float vace_strength = 1.f;
|
float vace_strength = 1.f;
|
||||||
sd_tiling_params_t vae_tiling_params = {false, false, 0, 0, 0.5f, 0.0f, 0.0f, nullptr};
|
sd_tiling_params_t vae_tiling_params = {false, 0, 0, 0.5f, 0.0f, 0.0f};
|
||||||
std::string extra_tiling_args;
|
|
||||||
|
|
||||||
std::string pm_id_images_dir;
|
std::string pm_id_images_dir;
|
||||||
std::string pm_id_embed_path;
|
std::string pm_id_embed_path;
|
||||||
@ -233,24 +190,12 @@ struct SDGenerationParams {
|
|||||||
int upscale_repeats = 1;
|
int upscale_repeats = 1;
|
||||||
int upscale_tile_size = 128;
|
int upscale_tile_size = 128;
|
||||||
|
|
||||||
bool hires_enabled = false;
|
|
||||||
std::string hires_upscaler = "Latent";
|
|
||||||
std::string hires_upscaler_model_path;
|
|
||||||
float hires_scale = 2.f;
|
|
||||||
int hires_width = 0;
|
|
||||||
int hires_height = 0;
|
|
||||||
int hires_steps = 0;
|
|
||||||
float hires_denoising_strength = 0.7f;
|
|
||||||
int hires_upscale_tile_size = 128;
|
|
||||||
std::vector<float> hires_custom_sigmas;
|
|
||||||
|
|
||||||
std::map<std::string, float> lora_map;
|
std::map<std::string, float> lora_map;
|
||||||
std::map<std::string, float> high_noise_lora_map;
|
std::map<std::string, float> high_noise_lora_map;
|
||||||
|
|
||||||
// Derived and normalized fields.
|
// Derived and normalized fields.
|
||||||
std::string prompt_with_lora; // for metadata record only
|
std::string prompt_with_lora; // for metadata record only
|
||||||
std::vector<sd_lora_t> lora_vec;
|
std::vector<sd_lora_t> lora_vec;
|
||||||
sd_hires_upscaler_t resolved_hires_upscaler;
|
|
||||||
|
|
||||||
// Owned execution payload.
|
// Owned execution payload.
|
||||||
SDImageOwner init_image;
|
SDImageOwner init_image;
|
||||||
@ -280,25 +225,15 @@ struct SDGenerationParams {
|
|||||||
void set_width_and_height_if_unset(int w, int h);
|
void set_width_and_height_if_unset(int w, int h);
|
||||||
int get_resolved_width() const;
|
int get_resolved_width() const;
|
||||||
int get_resolved_height() const;
|
int get_resolved_height() const;
|
||||||
bool resolve(const std::string& lora_model_dir, const std::string& hires_upscalers_dir, bool strict = false);
|
bool resolve(const std::string& lora_model_dir, bool strict = false);
|
||||||
bool validate(SDMode mode);
|
bool validate(SDMode mode);
|
||||||
bool resolve_and_validate(SDMode mode,
|
bool resolve_and_validate(SDMode mode, const std::string& lora_model_dir, bool strict = false);
|
||||||
const std::string& lora_model_dir,
|
|
||||||
const std::string& hires_upscalers_dir,
|
|
||||||
bool strict = false);
|
|
||||||
sd_img_gen_params_t to_sd_img_gen_params_t();
|
sd_img_gen_params_t to_sd_img_gen_params_t();
|
||||||
sd_vid_gen_params_t to_sd_vid_gen_params_t();
|
sd_vid_gen_params_t to_sd_vid_gen_params_t();
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string version_string();
|
std::string version_string();
|
||||||
std::string build_sdcpp_image_metadata_json(const SDContextParams& ctx_params,
|
std::string get_image_params(const SDContextParams& ctx_params, const SDGenerationParams& gen_params, int64_t seed);
|
||||||
const SDGenerationParams& gen_params,
|
|
||||||
int64_t seed,
|
|
||||||
SDMode mode = IMG_GEN);
|
|
||||||
std::string get_image_params(const SDContextParams& ctx_params,
|
|
||||||
const SDGenerationParams& gen_params,
|
|
||||||
int64_t seed,
|
|
||||||
SDMode mode = IMG_GEN);
|
|
||||||
|
|
||||||
#endif // __EXAMPLES_COMMON_COMMON_H__
|
#endif // __EXAMPLES_COMMON_COMMON_H__
|
||||||
|
|||||||
@ -95,57 +95,6 @@ using WebPMuxPtr = std::unique_ptr<WebPMux, WebPMuxDeleter>;
|
|||||||
using WebPAnimEncoderPtr = std::unique_ptr<WebPAnimEncoder, WebPAnimEncoderDeleter>;
|
using WebPAnimEncoderPtr = std::unique_ptr<WebPAnimEncoder, WebPAnimEncoderDeleter>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SD_USE_WEBM
|
|
||||||
class MemoryMkvWriter : public mkvmuxer::IMkvWriter {
|
|
||||||
public:
|
|
||||||
mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override {
|
|
||||||
if (buf == nullptr && len > 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const size_t end_pos = position_ + static_cast<size_t>(len);
|
|
||||||
if (end_pos > data_.size()) {
|
|
||||||
data_.resize(end_pos);
|
|
||||||
}
|
|
||||||
if (len > 0) {
|
|
||||||
memcpy(data_.data() + position_, buf, len);
|
|
||||||
}
|
|
||||||
position_ = end_pos;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mkvmuxer::int64 Position() const override {
|
|
||||||
return static_cast<mkvmuxer::int64>(position_);
|
|
||||||
}
|
|
||||||
|
|
||||||
mkvmuxer::int32 Position(mkvmuxer::int64 position) override {
|
|
||||||
if (position < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const size_t target = static_cast<size_t>(position);
|
|
||||||
if (target > data_.size()) {
|
|
||||||
data_.resize(target);
|
|
||||||
}
|
|
||||||
position_ = target;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Seekable() const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ElementStartNotify(mkvmuxer::uint64, mkvmuxer::int64) override {
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8_t>& data() const {
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> data_;
|
|
||||||
size_t position_ = 0;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool read_binary_file_bytes(const char* path, std::vector<uint8_t>& data) {
|
bool read_binary_file_bytes(const char* path, std::vector<uint8_t>& data) {
|
||||||
std::ifstream fin(fs::path(path), std::ios::binary);
|
std::ifstream fin(fs::path(path), std::ios::binary);
|
||||||
if (!fin) {
|
if (!fin) {
|
||||||
@ -613,13 +562,6 @@ typedef struct {
|
|||||||
uint32_t size;
|
uint32_t size;
|
||||||
} avi_index_entry;
|
} avi_index_entry;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char fourcc[4];
|
|
||||||
uint32_t flags;
|
|
||||||
uint32_t offset;
|
|
||||||
uint32_t size;
|
|
||||||
} avi_chunk_index_entry;
|
|
||||||
|
|
||||||
void write_u32_le(FILE* f, uint32_t val) {
|
void write_u32_le(FILE* f, uint32_t val) {
|
||||||
fwrite(&val, 4, 1, f);
|
fwrite(&val, 4, 1, f);
|
||||||
}
|
}
|
||||||
@ -628,59 +570,6 @@ void write_u16_le(FILE* f, uint16_t val) {
|
|||||||
fwrite(&val, 2, 1, f);
|
fwrite(&val, 2, 1, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_u32_le(std::vector<uint8_t>& data, uint32_t val) {
|
|
||||||
data.push_back(static_cast<uint8_t>(val & 0xFF));
|
|
||||||
data.push_back(static_cast<uint8_t>((val >> 8) & 0xFF));
|
|
||||||
data.push_back(static_cast<uint8_t>((val >> 16) & 0xFF));
|
|
||||||
data.push_back(static_cast<uint8_t>((val >> 24) & 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_u16_le(std::vector<uint8_t>& data, uint16_t val) {
|
|
||||||
data.push_back(static_cast<uint8_t>(val & 0xFF));
|
|
||||||
data.push_back(static_cast<uint8_t>((val >> 8) & 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
void patch_u32_le(std::vector<uint8_t>& data, size_t offset, uint32_t val) {
|
|
||||||
if (offset + 4 > data.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
data[offset + 0] = static_cast<uint8_t>(val & 0xFF);
|
|
||||||
data[offset + 1] = static_cast<uint8_t>((val >> 8) & 0xFF);
|
|
||||||
data[offset + 2] = static_cast<uint8_t>((val >> 16) & 0xFF);
|
|
||||||
data[offset + 3] = static_cast<uint8_t>((val >> 24) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_fourcc(std::vector<uint8_t>& data, const char* fourcc) {
|
|
||||||
data.insert(data.end(), fourcc, fourcc + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<uint8_t> audio_to_pcm16_bytes(const sd_audio_t* audio) {
|
|
||||||
if (audio == nullptr || audio->data == nullptr || audio->sample_count == 0 || audio->channels == 0 || audio->sample_rate == 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t pcm_samples = static_cast<size_t>(audio->sample_count) * static_cast<size_t>(audio->channels);
|
|
||||||
std::vector<uint8_t> bytes(pcm_samples * sizeof(int16_t));
|
|
||||||
auto* pcm = reinterpret_cast<int16_t*>(bytes.data());
|
|
||||||
for (size_t i = 0; i < pcm_samples; ++i) {
|
|
||||||
const float sample = std::clamp(audio->data[i], -1.0f, 1.0f);
|
|
||||||
pcm[i] = static_cast<int16_t>(std::lrint(sample * 32767.0f));
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::pair<uint64_t, uint64_t> audio_sample_range_for_video_frame(const sd_audio_t* audio, int frame_idx, int num_frames, int fps) {
|
|
||||||
if (audio == nullptr || fps <= 0 || num_frames <= 0) {
|
|
||||||
return {0, 0};
|
|
||||||
}
|
|
||||||
const uint64_t total = audio->sample_count;
|
|
||||||
const uint64_t start = static_cast<uint64_t>((static_cast<long double>(frame_idx) * total) / num_frames);
|
|
||||||
const uint64_t end = frame_idx + 1 == num_frames
|
|
||||||
? total
|
|
||||||
: static_cast<uint64_t>((static_cast<long double>(frame_idx + 1) * total) / num_frames);
|
|
||||||
return {start, std::max(start, end)};
|
|
||||||
}
|
|
||||||
|
|
||||||
EncodedImageFormat encoded_image_format_from_path(const std::string& path) {
|
EncodedImageFormat encoded_image_format_from_path(const std::string& path) {
|
||||||
std::string ext = fs::path(path).extension().string();
|
std::string ext = fs::path(path).extension().string();
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||||
@ -810,144 +699,97 @@ uint8_t* load_image_from_memory(const char* image_bytes,
|
|||||||
return load_image_common(true, image_bytes, len, width, height, expected_width, expected_height, expected_channel);
|
return load_image_common(true, image_bytes, len, width, height, expected_width, expected_height, expected_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> create_mjpg_avi_from_sd_images_to_vector(sd_image_t* images, int num_images, int fps, int quality, const sd_audio_t* audio) {
|
int create_mjpg_avi_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality) {
|
||||||
if (num_images == 0) {
|
if (num_images == 0) {
|
||||||
fprintf(stderr, "Error: Image array is empty.\n");
|
fprintf(stderr, "Error: Image array is empty.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePtr file(fopen(filename, "wb"));
|
||||||
|
if (!file) {
|
||||||
|
perror("Error opening file for writing");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FILE* f = file.get();
|
||||||
|
|
||||||
uint32_t width = images[0].width;
|
uint32_t width = images[0].width;
|
||||||
uint32_t height = images[0].height;
|
uint32_t height = images[0].height;
|
||||||
uint32_t channels = images[0].channel;
|
uint32_t channels = images[0].channel;
|
||||||
if (channels != 3 && channels != 4) {
|
if (channels != 3 && channels != 4) {
|
||||||
fprintf(stderr, "Error: Unsupported channel count: %u\n", channels);
|
fprintf(stderr, "Error: Unsupported channel count: %u\n", channels);
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stb_image_write changes JPEG sampling behavior above quality 90.
|
fwrite("RIFF", 4, 1, f);
|
||||||
// MJPG AVI playback is more compatible when we keep the encoder on the
|
long riff_size_pos = ftell(f);
|
||||||
// <= 90 path.
|
write_u32_le(f, 0);
|
||||||
const int mjpg_quality = std::clamp(quality, 1, 90);
|
fwrite("AVI ", 4, 1, f);
|
||||||
const bool has_audio = audio != nullptr && audio->data != nullptr && audio->sample_count > 0 && audio->channels > 0 && audio->sample_rate > 0;
|
|
||||||
const std::vector<uint8_t> audio_pcm = audio_to_pcm16_bytes(audio);
|
|
||||||
const uint16_t audio_bits_per_sample = 16;
|
|
||||||
const uint16_t audio_block_align = has_audio ? static_cast<uint16_t>(audio->channels * (audio_bits_per_sample / 8)) : 0;
|
|
||||||
const uint32_t audio_byte_rate = has_audio ? static_cast<uint32_t>(audio->sample_rate * audio_block_align) : 0;
|
|
||||||
const uint32_t audio_data_size = has_audio ? static_cast<uint32_t>(audio_pcm.size()) : 0;
|
|
||||||
|
|
||||||
std::vector<uint8_t> avi_data;
|
fwrite("LIST", 4, 1, f);
|
||||||
avi_data.reserve(static_cast<size_t>(num_images) * 1024);
|
write_u32_le(f, 4 + 8 + 56 + 8 + 4 + 8 + 56 + 8 + 40);
|
||||||
|
fwrite("hdrl", 4, 1, f);
|
||||||
|
|
||||||
write_fourcc(avi_data, "RIFF");
|
fwrite("avih", 4, 1, f);
|
||||||
const size_t riff_size_pos = avi_data.size();
|
write_u32_le(f, 56);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, 1000000 / fps);
|
||||||
write_fourcc(avi_data, "AVI ");
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0x110);
|
||||||
|
write_u32_le(f, num_images);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 1);
|
||||||
|
write_u32_le(f, width * height * 3);
|
||||||
|
write_u32_le(f, width);
|
||||||
|
write_u32_le(f, height);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
|
||||||
write_fourcc(avi_data, "LIST");
|
fwrite("LIST", 4, 1, f);
|
||||||
uint32_t hdrl_size = 4 + 8 + 56 + 8 + 4 + 8 + 56 + 8 + 40;
|
write_u32_le(f, 4 + 8 + 56 + 8 + 40);
|
||||||
if (has_audio) {
|
fwrite("strl", 4, 1, f);
|
||||||
hdrl_size += 8 + (4 + 8 + 56 + 8 + 16);
|
|
||||||
}
|
|
||||||
write_u32_le(avi_data, hdrl_size);
|
|
||||||
write_fourcc(avi_data, "hdrl");
|
|
||||||
|
|
||||||
write_fourcc(avi_data, "avih");
|
fwrite("strh", 4, 1, f);
|
||||||
write_u32_le(avi_data, 56);
|
write_u32_le(f, 56);
|
||||||
write_u32_le(avi_data, 1000000 / fps);
|
fwrite("vids", 4, 1, f);
|
||||||
write_u32_le(avi_data, 0);
|
fwrite("MJPG", 4, 1, f);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, 0);
|
||||||
write_u32_le(avi_data, 0x110);
|
write_u16_le(f, 0);
|
||||||
write_u32_le(avi_data, num_images);
|
write_u16_le(f, 0);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, 0);
|
||||||
write_u32_le(avi_data, has_audio ? 2 : 1);
|
write_u32_le(f, 1);
|
||||||
write_u32_le(avi_data, width * height * 3);
|
write_u32_le(f, fps);
|
||||||
write_u32_le(avi_data, width);
|
write_u32_le(f, 0);
|
||||||
write_u32_le(avi_data, height);
|
write_u32_le(f, num_images);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, width * height * 3);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, (uint32_t)-1);
|
||||||
write_u32_le(avi_data, 0);
|
write_u32_le(f, 0);
|
||||||
write_u32_le(avi_data, 0);
|
write_u16_le(f, 0);
|
||||||
|
write_u16_le(f, 0);
|
||||||
|
write_u16_le(f, 0);
|
||||||
|
write_u16_le(f, 0);
|
||||||
|
|
||||||
write_fourcc(avi_data, "LIST");
|
fwrite("strf", 4, 1, f);
|
||||||
write_u32_le(avi_data, 4 + 8 + 56 + 8 + 40);
|
write_u32_le(f, 40);
|
||||||
write_fourcc(avi_data, "strl");
|
write_u32_le(f, 40);
|
||||||
|
write_u32_le(f, width);
|
||||||
|
write_u32_le(f, height);
|
||||||
|
write_u16_le(f, 1);
|
||||||
|
write_u16_le(f, 24);
|
||||||
|
fwrite("MJPG", 4, 1, f);
|
||||||
|
write_u32_le(f, width * height * 3);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
write_u32_le(f, 0);
|
||||||
|
|
||||||
write_fourcc(avi_data, "strh");
|
fwrite("LIST", 4, 1, f);
|
||||||
write_u32_le(avi_data, 56);
|
long movi_size_pos = ftell(f);
|
||||||
write_fourcc(avi_data, "vids");
|
write_u32_le(f, 0);
|
||||||
write_fourcc(avi_data, "MJPG");
|
fwrite("movi", 4, 1, f);
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 1);
|
|
||||||
write_u32_le(avi_data, fps);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, num_images);
|
|
||||||
write_u32_le(avi_data, width * height * 3);
|
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(-1));
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
|
|
||||||
write_fourcc(avi_data, "strf");
|
std::vector<avi_index_entry> index(static_cast<size_t>(num_images));
|
||||||
write_u32_le(avi_data, 40);
|
|
||||||
write_u32_le(avi_data, 40);
|
|
||||||
write_u32_le(avi_data, width);
|
|
||||||
write_u32_le(avi_data, height);
|
|
||||||
write_u16_le(avi_data, 1);
|
|
||||||
write_u16_le(avi_data, 24);
|
|
||||||
write_fourcc(avi_data, "MJPG");
|
|
||||||
write_u32_le(avi_data, width * height * 3);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
|
|
||||||
if (has_audio) {
|
|
||||||
write_fourcc(avi_data, "LIST");
|
|
||||||
write_u32_le(avi_data, 4 + 8 + 56 + 8 + 16);
|
|
||||||
write_fourcc(avi_data, "strl");
|
|
||||||
|
|
||||||
write_fourcc(avi_data, "strh");
|
|
||||||
write_u32_le(avi_data, 56);
|
|
||||||
write_fourcc(avi_data, "auds");
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, audio_block_align);
|
|
||||||
write_u32_le(avi_data, audio_byte_rate);
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(audio->sample_count));
|
|
||||||
write_u32_le(avi_data, audio_data_size);
|
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(-1));
|
|
||||||
write_u32_le(avi_data, audio_block_align);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
write_u16_le(avi_data, 0);
|
|
||||||
|
|
||||||
write_fourcc(avi_data, "strf");
|
|
||||||
write_u32_le(avi_data, 16);
|
|
||||||
write_u16_le(avi_data, 1);
|
|
||||||
write_u16_le(avi_data, static_cast<uint16_t>(audio->channels));
|
|
||||||
write_u32_le(avi_data, audio->sample_rate);
|
|
||||||
write_u32_le(avi_data, audio_byte_rate);
|
|
||||||
write_u16_le(avi_data, audio_block_align);
|
|
||||||
write_u16_le(avi_data, audio_bits_per_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_fourcc(avi_data, "LIST");
|
|
||||||
const size_t movi_size_pos = avi_data.size();
|
|
||||||
write_u32_le(avi_data, 0);
|
|
||||||
write_fourcc(avi_data, "movi");
|
|
||||||
|
|
||||||
std::vector<avi_chunk_index_entry> index;
|
|
||||||
index.reserve(static_cast<size_t>(num_images) + (has_audio ? 1 : 0));
|
|
||||||
std::vector<uint8_t> jpeg_data;
|
std::vector<uint8_t> jpeg_data;
|
||||||
|
|
||||||
for (int i = 0; i < num_images; i++) {
|
for (int i = 0; i < num_images; i++) {
|
||||||
@ -959,80 +801,55 @@ std::vector<uint8_t> create_mjpg_avi_from_sd_images_to_vector(sd_image_t* images
|
|||||||
buffer->insert(buffer->end(), src, src + size);
|
buffer->insert(buffer->end(), src, src + size);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!stbi_write_jpg_to_func(write_to_buf, &jpeg_data, images[i].width, images[i].height, channels, images[i].data, mjpg_quality)) {
|
if (!stbi_write_jpg_to_func(write_to_buf, &jpeg_data, images[i].width, images[i].height, channels, images[i].data, quality)) {
|
||||||
fprintf(stderr, "Error: Failed to encode JPEG frame.\n");
|
fprintf(stderr, "Error: Failed to encode JPEG frame.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
avi_chunk_index_entry video_entry = {};
|
fwrite("00dc", 4, 1, f);
|
||||||
memcpy(video_entry.fourcc, "00dc", 4);
|
write_u32_le(f, (uint32_t)jpeg_data.size());
|
||||||
video_entry.flags = 0x10;
|
index[i].offset = ftell(f) - 8;
|
||||||
video_entry.offset = static_cast<uint32_t>(avi_data.size());
|
index[i].size = (uint32_t)jpeg_data.size();
|
||||||
write_fourcc(avi_data, "00dc");
|
fwrite(jpeg_data.data(), 1, jpeg_data.size(), f);
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(jpeg_data.size()));
|
|
||||||
video_entry.size = static_cast<uint32_t>(jpeg_data.size());
|
|
||||||
avi_data.insert(avi_data.end(), jpeg_data.begin(), jpeg_data.end());
|
|
||||||
index.push_back(video_entry);
|
|
||||||
|
|
||||||
if (jpeg_data.size() % 2) {
|
if (jpeg_data.size() % 2) {
|
||||||
avi_data.push_back(0);
|
fputc(0, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_audio && !audio_pcm.empty()) {
|
long cur_pos = ftell(f);
|
||||||
avi_chunk_index_entry audio_entry = {};
|
long movi_size = cur_pos - movi_size_pos - 4;
|
||||||
memcpy(audio_entry.fourcc, "01wb", 4);
|
fseek(f, movi_size_pos, SEEK_SET);
|
||||||
audio_entry.flags = 0;
|
write_u32_le(f, movi_size);
|
||||||
audio_entry.offset = static_cast<uint32_t>(avi_data.size());
|
fseek(f, cur_pos, SEEK_SET);
|
||||||
audio_entry.size = static_cast<uint32_t>(audio_pcm.size());
|
|
||||||
write_fourcc(avi_data, "01wb");
|
fwrite("idx1", 4, 1, f);
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(audio_pcm.size()));
|
write_u32_le(f, num_images * 16);
|
||||||
avi_data.insert(avi_data.end(), audio_pcm.begin(), audio_pcm.end());
|
for (int i = 0; i < num_images; i++) {
|
||||||
index.push_back(audio_entry);
|
fwrite("00dc", 4, 1, f);
|
||||||
if (audio_pcm.size() % 2 != 0) {
|
write_u32_le(f, 0x10);
|
||||||
avi_data.push_back(0);
|
write_u32_le(f, index[i].offset);
|
||||||
}
|
write_u32_le(f, index[i].size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t movi_size = avi_data.size() - movi_size_pos - 4;
|
cur_pos = ftell(f);
|
||||||
patch_u32_le(avi_data, movi_size_pos, static_cast<uint32_t>(movi_size));
|
long file_size = cur_pos - riff_size_pos - 4;
|
||||||
|
fseek(f, riff_size_pos, SEEK_SET);
|
||||||
|
write_u32_le(f, file_size);
|
||||||
|
fseek(f, cur_pos, SEEK_SET);
|
||||||
|
|
||||||
write_fourcc(avi_data, "idx1");
|
|
||||||
write_u32_le(avi_data, static_cast<uint32_t>(index.size() * 16));
|
|
||||||
for (const auto& entry : index) {
|
|
||||||
write_fourcc(avi_data, entry.fourcc);
|
|
||||||
write_u32_le(avi_data, entry.flags);
|
|
||||||
write_u32_le(avi_data, entry.offset);
|
|
||||||
write_u32_le(avi_data, entry.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t file_size = avi_data.size() - riff_size_pos - 4;
|
|
||||||
patch_u32_le(avi_data, riff_size_pos, static_cast<uint32_t>(file_size));
|
|
||||||
|
|
||||||
return avi_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int create_mjpg_avi_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality, const sd_audio_t* audio) {
|
|
||||||
std::vector<uint8_t> avi_data = create_mjpg_avi_from_sd_images_to_vector(images, num_images, fps, quality, audio);
|
|
||||||
if (avi_data.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!write_binary_file_bytes(filename, avi_data)) {
|
|
||||||
perror("Error opening file for writing");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SD_USE_WEBP
|
#ifdef SD_USE_WEBP
|
||||||
std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* images, int num_images, int fps, int quality) {
|
int create_animated_webp_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality) {
|
||||||
if (num_images == 0) {
|
if (num_images == 0) {
|
||||||
fprintf(stderr, "Error: Image array is empty.\n");
|
fprintf(stderr, "Error: Image array is empty.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
if (fps <= 0) {
|
if (fps <= 0) {
|
||||||
fprintf(stderr, "Error: FPS must be positive.\n");
|
fprintf(stderr, "Error: FPS must be positive.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int width = static_cast<int>(images[0].width);
|
const int width = static_cast<int>(images[0].width);
|
||||||
@ -1040,14 +857,14 @@ std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* i
|
|||||||
const int channels = static_cast<int>(images[0].channel);
|
const int channels = static_cast<int>(images[0].channel);
|
||||||
if (channels != 1 && channels != 3 && channels != 4) {
|
if (channels != 1 && channels != 3 && channels != 4) {
|
||||||
fprintf(stderr, "Error: Unsupported channel count: %d\n", channels);
|
fprintf(stderr, "Error: Unsupported channel count: %d\n", channels);
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPAnimEncoderOptions anim_options;
|
WebPAnimEncoderOptions anim_options;
|
||||||
WebPConfig config;
|
WebPConfig config;
|
||||||
if (!WebPAnimEncoderOptionsInit(&anim_options) || !WebPConfigInit(&config)) {
|
if (!WebPAnimEncoderOptionsInit(&anim_options) || !WebPConfigInit(&config)) {
|
||||||
fprintf(stderr, "Error: Failed to initialize WebP animation encoder.\n");
|
fprintf(stderr, "Error: Failed to initialize WebP animation encoder.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.quality = static_cast<float>(quality);
|
config.quality = static_cast<float>(quality);
|
||||||
@ -1058,13 +875,13 @@ std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* i
|
|||||||
}
|
}
|
||||||
if (!WebPValidateConfig(&config)) {
|
if (!WebPValidateConfig(&config)) {
|
||||||
fprintf(stderr, "Error: Invalid WebP encoder configuration.\n");
|
fprintf(stderr, "Error: Invalid WebP encoder configuration.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPAnimEncoderPtr enc(WebPAnimEncoderNew(width, height, &anim_options));
|
WebPAnimEncoderPtr enc(WebPAnimEncoderNew(width, height, &anim_options));
|
||||||
if (enc == nullptr) {
|
if (enc == nullptr) {
|
||||||
fprintf(stderr, "Error: Could not create WebPAnimEncoder object.\n");
|
fprintf(stderr, "Error: Could not create WebPAnimEncoder object.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int frame_duration_ms = std::max(1, static_cast<int>(std::lround(1000.0 / static_cast<double>(fps))));
|
const int frame_duration_ms = std::max(1, static_cast<int>(std::lround(1000.0 / static_cast<double>(fps))));
|
||||||
@ -1074,13 +891,13 @@ std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* i
|
|||||||
const sd_image_t& image = images[i];
|
const sd_image_t& image = images[i];
|
||||||
if (static_cast<int>(image.width) != width || static_cast<int>(image.height) != height) {
|
if (static_cast<int>(image.width) != width || static_cast<int>(image.height) != height) {
|
||||||
fprintf(stderr, "Error: Frame dimensions do not match.\n");
|
fprintf(stderr, "Error: Frame dimensions do not match.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPPictureGuard picture;
|
WebPPictureGuard picture;
|
||||||
if (!picture.initialized) {
|
if (!picture.initialized) {
|
||||||
fprintf(stderr, "Error: Failed to initialize WebPPicture.\n");
|
fprintf(stderr, "Error: Failed to initialize WebPPicture.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
picture.picture.use_argb = 1;
|
picture.picture.use_argb = 1;
|
||||||
picture.picture.width = width;
|
picture.picture.width = width;
|
||||||
@ -1104,12 +921,12 @@ std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* i
|
|||||||
|
|
||||||
if (!picture_ok) {
|
if (!picture_ok) {
|
||||||
fprintf(stderr, "Error: Failed to import frame into WebPPicture.\n");
|
fprintf(stderr, "Error: Failed to import frame into WebPPicture.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WebPAnimEncoderAdd(enc.get(), &picture.picture, timestamp_ms, &config)) {
|
if (!WebPAnimEncoderAdd(enc.get(), &picture.picture, timestamp_ms, &config)) {
|
||||||
fprintf(stderr, "Error: Failed to add frame to animated WebP: %s\n", WebPAnimEncoderGetError(enc.get()));
|
fprintf(stderr, "Error: Failed to add frame to animated WebP: %s\n", WebPAnimEncoderGetError(enc.get()));
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
timestamp_ms += frame_duration_ms;
|
timestamp_ms += frame_duration_ms;
|
||||||
@ -1117,50 +934,52 @@ std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* i
|
|||||||
|
|
||||||
if (!WebPAnimEncoderAdd(enc.get(), nullptr, timestamp_ms, nullptr)) {
|
if (!WebPAnimEncoderAdd(enc.get(), nullptr, timestamp_ms, nullptr)) {
|
||||||
fprintf(stderr, "Error: Failed to finalize animated WebP frames: %s\n", WebPAnimEncoderGetError(enc.get()));
|
fprintf(stderr, "Error: Failed to finalize animated WebP frames: %s\n", WebPAnimEncoderGetError(enc.get()));
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPDataGuard webp_data;
|
WebPDataGuard webp_data;
|
||||||
if (!WebPAnimEncoderAssemble(enc.get(), &webp_data.data)) {
|
if (!WebPAnimEncoderAssemble(enc.get(), &webp_data.data)) {
|
||||||
fprintf(stderr, "Error: Failed to assemble animated WebP: %s\n", WebPAnimEncoderGetError(enc.get()));
|
fprintf(stderr, "Error: Failed to assemble animated WebP: %s\n", WebPAnimEncoderGetError(enc.get()));
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::vector<uint8_t>(webp_data.data.bytes, webp_data.data.bytes + webp_data.data.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int create_animated_webp_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality) {
|
|
||||||
std::vector<uint8_t> webp_data = create_animated_webp_from_sd_images_to_vector(images, num_images, fps, quality);
|
|
||||||
if (webp_data.empty()) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!write_binary_file_bytes(filename, webp_data)) {
|
|
||||||
|
FilePtr f(fopen(filename, "wb"));
|
||||||
|
if (!f) {
|
||||||
perror("Error opening file for writing");
|
perror("Error opening file for writing");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (webp_data.data.size > 0 && fwrite(webp_data.data.bytes, 1, webp_data.data.size, f.get()) != webp_data.data.size) {
|
||||||
|
fprintf(stderr, "Error: Failed to write animated WebP file.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SD_USE_WEBM
|
#ifdef SD_USE_WEBM
|
||||||
std::vector<uint8_t> create_webm_from_sd_images_to_vector(sd_image_t* images, int num_images, int fps, int quality, const sd_audio_t* audio) {
|
int create_webm_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality) {
|
||||||
if (num_images == 0) {
|
if (num_images == 0) {
|
||||||
fprintf(stderr, "Error: Image array is empty.\n");
|
fprintf(stderr, "Error: Image array is empty.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
if (fps <= 0) {
|
if (fps <= 0) {
|
||||||
fprintf(stderr, "Error: FPS must be positive.\n");
|
fprintf(stderr, "Error: FPS must be positive.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int width = static_cast<int>(images[0].width);
|
const int width = static_cast<int>(images[0].width);
|
||||||
const int height = static_cast<int>(images[0].height);
|
const int height = static_cast<int>(images[0].height);
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
fprintf(stderr, "Error: Invalid frame dimensions.\n");
|
fprintf(stderr, "Error: Invalid frame dimensions.\n");
|
||||||
return {};
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryMkvWriter writer;
|
mkvmuxer::MkvWriter writer;
|
||||||
|
if (!writer.Open(filename)) {
|
||||||
|
fprintf(stderr, "Error: Could not open WebM file for writing.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const int ret = [&]() -> int {
|
const int ret = [&]() -> int {
|
||||||
mkvmuxer::Segment segment;
|
mkvmuxer::Segment segment;
|
||||||
@ -1188,25 +1007,6 @@ std::vector<uint8_t> create_webm_from_sd_images_to_vector(sd_image_t* images, in
|
|||||||
video_track->set_display_height(static_cast<uint64_t>(height));
|
video_track->set_display_height(static_cast<uint64_t>(height));
|
||||||
video_track->set_frame_rate(static_cast<double>(fps));
|
video_track->set_frame_rate(static_cast<double>(fps));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t audio_track_number = 0;
|
|
||||||
std::vector<uint8_t> audio_pcm = audio_to_pcm16_bytes(audio);
|
|
||||||
if (audio != nullptr && !audio_pcm.empty()) {
|
|
||||||
audio_track_number = segment.AddAudioTrack(static_cast<int32_t>(audio->sample_rate), static_cast<int32_t>(audio->channels), 0);
|
|
||||||
if (audio_track_number == 0) {
|
|
||||||
fprintf(stderr, "Error: Failed to add audio track.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
auto* audio_track = static_cast<mkvmuxer::AudioTrack*>(segment.GetTrackByNumber(audio_track_number));
|
|
||||||
if (audio_track == nullptr) {
|
|
||||||
fprintf(stderr, "Error: Failed to get audio track.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
audio_track->set_codec_id("A_PCM/INT/LIT");
|
|
||||||
audio_track->set_bit_depth(16);
|
|
||||||
audio_track->set_sample_rate(static_cast<double>(audio->sample_rate));
|
|
||||||
audio_track->set_channels(audio->channels);
|
|
||||||
}
|
|
||||||
segment.GetSegmentInfo()->set_writing_app("stable-diffusion.cpp");
|
segment.GetSegmentInfo()->set_writing_app("stable-diffusion.cpp");
|
||||||
segment.GetSegmentInfo()->set_muxing_app("stable-diffusion.cpp");
|
segment.GetSegmentInfo()->set_muxing_app("stable-diffusion.cpp");
|
||||||
|
|
||||||
@ -1236,23 +1036,6 @@ std::vector<uint8_t> create_webm_from_sd_images_to_vector(sd_image_t* images, in
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_track_number != 0) {
|
|
||||||
auto [audio_begin, audio_end] = audio_sample_range_for_video_frame(audio, i, num_images, fps);
|
|
||||||
const uint64_t frame_samples = audio_end - audio_begin;
|
|
||||||
if (frame_samples > 0) {
|
|
||||||
const uint64_t frame_bytes = frame_samples * audio->channels * sizeof(int16_t);
|
|
||||||
const uint8_t* frame_ptr = audio_pcm.data() + audio_begin * audio->channels * sizeof(int16_t);
|
|
||||||
if (!segment.AddFrame(frame_ptr,
|
|
||||||
frame_bytes,
|
|
||||||
audio_track_number,
|
|
||||||
timestamp_ns,
|
|
||||||
true)) {
|
|
||||||
fprintf(stderr, "Error: Failed to mux audio chunk %d into WebM.\n", i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp_ns += frame_duration_ns;
|
timestamp_ns += frame_duration_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1262,115 +1045,30 @@ std::vector<uint8_t> create_webm_from_sd_images_to_vector(sd_image_t* images, in
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}();
|
}();
|
||||||
if (ret != 0) {
|
writer.Close();
|
||||||
return {};
|
return ret;
|
||||||
}
|
|
||||||
return writer.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
int create_webm_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality, const sd_audio_t* audio) {
|
|
||||||
std::vector<uint8_t> webm_data = create_webm_from_sd_images_to_vector(images, num_images, fps, quality, audio);
|
|
||||||
if (webm_data.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!write_binary_file_bytes(filename, webm_data)) {
|
|
||||||
perror("Error opening file for writing");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::vector<uint8_t> create_video_from_sd_images_to_vector(const std::string& output_format,
|
int create_video_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality) {
|
||||||
sd_image_t* images,
|
std::string path = filename ? filename : "";
|
||||||
int num_images,
|
auto pos = path.find_last_of('.');
|
||||||
int fps,
|
std::string ext = pos == std::string::npos ? "" : path.substr(pos);
|
||||||
int quality,
|
for (char& ch : ext) {
|
||||||
const sd_audio_t* audio) {
|
ch = static_cast<char>(tolower(static_cast<unsigned char>(ch)));
|
||||||
std::string format = output_format;
|
|
||||||
std::transform(format.begin(), format.end(), format.begin(),
|
|
||||||
[](unsigned char c) { return static_cast<char>(tolower(c)); });
|
|
||||||
if (!format.empty() && format[0] == '.') {
|
|
||||||
format.erase(format.begin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SD_USE_WEBM
|
#ifdef SD_USE_WEBM
|
||||||
if (format == "webm") {
|
if (ext == ".webm") {
|
||||||
return create_webm_from_sd_images_to_vector(images, num_images, fps, quality, audio);
|
return create_webm_from_sd_images(filename, images, num_images, fps, quality);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SD_USE_WEBP
|
#ifdef SD_USE_WEBP
|
||||||
if (format == "webp") {
|
if (ext == ".webp") {
|
||||||
return create_animated_webp_from_sd_images_to_vector(images, num_images, fps, quality);
|
return create_animated_webp_from_sd_images(filename, images, num_images, fps, quality);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return create_mjpg_avi_from_sd_images_to_vector(images, num_images, fps, quality, audio);
|
return create_mjpg_avi_from_sd_images(filename, images, num_images, fps, quality);
|
||||||
}
|
|
||||||
|
|
||||||
int create_video_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality, const sd_audio_t* audio) {
|
|
||||||
std::string path = filename ? filename : "";
|
|
||||||
auto pos = path.find_last_of('.');
|
|
||||||
std::string ext = pos == std::string::npos ? "" : path.substr(pos);
|
|
||||||
std::vector<uint8_t> video_data = create_video_from_sd_images_to_vector(ext, images, num_images, fps, quality, audio);
|
|
||||||
if (video_data.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!write_binary_file_bytes(filename, video_data)) {
|
|
||||||
perror("Error opening file for writing");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write_wav_to_file(const std::string& path,
|
|
||||||
const float* interleaved_samples,
|
|
||||||
uint64_t sample_count,
|
|
||||||
uint32_t channels,
|
|
||||||
uint32_t sample_rate) {
|
|
||||||
if (interleaved_samples == nullptr || sample_count == 0 || channels == 0 || sample_rate == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t bits_per_sample = 16;
|
|
||||||
uint32_t bytes_per_sample = bits_per_sample / 8;
|
|
||||||
uint32_t block_align = channels * bytes_per_sample;
|
|
||||||
uint32_t byte_rate = sample_rate * block_align;
|
|
||||||
uint32_t data_size = static_cast<uint32_t>(sample_count * channels * bytes_per_sample);
|
|
||||||
uint32_t riff_size = 36 + data_size;
|
|
||||||
|
|
||||||
file.write("RIFF", 4);
|
|
||||||
file.write(reinterpret_cast<const char*>(&riff_size), sizeof(riff_size));
|
|
||||||
file.write("WAVE", 4);
|
|
||||||
file.write("fmt ", 4);
|
|
||||||
|
|
||||||
uint32_t fmt_size = 16;
|
|
||||||
uint16_t audio_format = 1;
|
|
||||||
uint16_t wav_channels = static_cast<uint16_t>(channels);
|
|
||||||
uint16_t wav_block_align = static_cast<uint16_t>(block_align);
|
|
||||||
uint16_t wav_bits_per_sample = static_cast<uint16_t>(bits_per_sample);
|
|
||||||
file.write(reinterpret_cast<const char*>(&fmt_size), sizeof(fmt_size));
|
|
||||||
file.write(reinterpret_cast<const char*>(&audio_format), sizeof(audio_format));
|
|
||||||
file.write(reinterpret_cast<const char*>(&wav_channels), sizeof(wav_channels));
|
|
||||||
file.write(reinterpret_cast<const char*>(&sample_rate), sizeof(sample_rate));
|
|
||||||
file.write(reinterpret_cast<const char*>(&byte_rate), sizeof(byte_rate));
|
|
||||||
file.write(reinterpret_cast<const char*>(&wav_block_align), sizeof(wav_block_align));
|
|
||||||
file.write(reinterpret_cast<const char*>(&wav_bits_per_sample), sizeof(wav_bits_per_sample));
|
|
||||||
|
|
||||||
file.write("data", 4);
|
|
||||||
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size));
|
|
||||||
|
|
||||||
std::vector<int16_t> pcm(sample_count * channels);
|
|
||||||
for (size_t i = 0; i < pcm.size(); ++i) {
|
|
||||||
float sample = std::max(-1.0f, std::min(1.0f, interleaved_samples[i]));
|
|
||||||
pcm[i] = static_cast<int16_t>(std::lrint(sample * 32767.0f));
|
|
||||||
}
|
|
||||||
file.write(reinterpret_cast<const char*>(pcm.data()), static_cast<std::streamsize>(pcm.size() * sizeof(int16_t)));
|
|
||||||
return file.good();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,13 +57,7 @@ int create_mjpg_avi_from_sd_images(const char* filename,
|
|||||||
sd_image_t* images,
|
sd_image_t* images,
|
||||||
int num_images,
|
int num_images,
|
||||||
int fps,
|
int fps,
|
||||||
int quality = 90,
|
int quality = 90);
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
std::vector<uint8_t> create_mjpg_avi_from_sd_images_to_vector(sd_image_t* images,
|
|
||||||
int num_images,
|
|
||||||
int fps,
|
|
||||||
int quality = 90,
|
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
|
|
||||||
#ifdef SD_USE_WEBP
|
#ifdef SD_USE_WEBP
|
||||||
int create_animated_webp_from_sd_images(const char* filename,
|
int create_animated_webp_from_sd_images(const char* filename,
|
||||||
@ -71,10 +65,6 @@ int create_animated_webp_from_sd_images(const char* filename,
|
|||||||
int num_images,
|
int num_images,
|
||||||
int fps,
|
int fps,
|
||||||
int quality = 90);
|
int quality = 90);
|
||||||
std::vector<uint8_t> create_animated_webp_from_sd_images_to_vector(sd_image_t* images,
|
|
||||||
int num_images,
|
|
||||||
int fps,
|
|
||||||
int quality = 90);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SD_USE_WEBM
|
#ifdef SD_USE_WEBM
|
||||||
@ -82,32 +72,13 @@ int create_webm_from_sd_images(const char* filename,
|
|||||||
sd_image_t* images,
|
sd_image_t* images,
|
||||||
int num_images,
|
int num_images,
|
||||||
int fps,
|
int fps,
|
||||||
int quality = 90,
|
int quality = 90);
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
std::vector<uint8_t> create_webm_from_sd_images_to_vector(sd_image_t* images,
|
|
||||||
int num_images,
|
|
||||||
int fps,
|
|
||||||
int quality = 90,
|
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int create_video_from_sd_images(const char* filename,
|
int create_video_from_sd_images(const char* filename,
|
||||||
sd_image_t* images,
|
sd_image_t* images,
|
||||||
int num_images,
|
int num_images,
|
||||||
int fps,
|
int fps,
|
||||||
int quality = 90,
|
int quality = 90);
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
std::vector<uint8_t> create_video_from_sd_images_to_vector(const std::string& output_format,
|
|
||||||
sd_image_t* images,
|
|
||||||
int num_images,
|
|
||||||
int fps,
|
|
||||||
int quality = 90,
|
|
||||||
const sd_audio_t* audio = nullptr);
|
|
||||||
|
|
||||||
bool write_wav_to_file(const std::string& path,
|
|
||||||
const float* interleaved_samples,
|
|
||||||
uint64_t sample_count,
|
|
||||||
uint32_t channels,
|
|
||||||
uint32_t sample_rate);
|
|
||||||
|
|
||||||
#endif // __MEDIA_IO_H__
|
#endif // __MEDIA_IO_H__
|
||||||
|
|||||||
@ -74,9 +74,6 @@ add_executable(${TARGET}
|
|||||||
routes_sdapi.cpp
|
routes_sdapi.cpp
|
||||||
routes_sdcpp.cpp
|
routes_sdcpp.cpp
|
||||||
)
|
)
|
||||||
if(APPLE)
|
|
||||||
sd_set_macos_rpaths(${TARGET})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_FRONTEND_BUILD)
|
if(HAVE_FRONTEND_BUILD)
|
||||||
add_dependencies(${TARGET} ${TARGET}_frontend)
|
add_dependencies(${TARGET} ${TARGET}_frontend)
|
||||||
|
|||||||
@ -136,34 +136,28 @@ Context Options:
|
|||||||
--clip_g <string> path to the clip-g text encoder
|
--clip_g <string> path to the clip-g text encoder
|
||||||
--clip_vision <string> path to the clip-vision encoder
|
--clip_vision <string> path to the clip-vision encoder
|
||||||
--t5xxl <string> path to the t5xxl text encoder
|
--t5xxl <string> path to the t5xxl text encoder
|
||||||
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image,
|
--llm <string> path to the llm text encoder. For example: (qwenvl2.5 for qwen-image, mistral-small3.2 for flux2, ...)
|
||||||
mistral-small3.2 for flux2, ...)
|
|
||||||
--llm_vision <string> path to the llm vit
|
--llm_vision <string> path to the llm vit
|
||||||
--qwen2vl <string> alias of --llm. Deprecated.
|
--qwen2vl <string> alias of --llm. Deprecated.
|
||||||
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
--qwen2vl_vision <string> alias of --llm_vision. Deprecated.
|
||||||
--diffusion-model <string> path to the standalone diffusion model
|
--diffusion-model <string> path to the standalone diffusion model
|
||||||
--high-noise-diffusion-model <string> path to the standalone high noise 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
|
--vae <string> path to standalone vae model
|
||||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||||
--tae <string> alias of --taesd
|
--tae <string> alias of --taesd
|
||||||
--control-net <string> path to control net model
|
--control-net <string> path to control net model
|
||||||
--embd-dir <string> embeddings directory
|
--embd-dir <string> embeddings directory
|
||||||
--lora-model-dir <string> lora model 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")
|
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||||
--photo-maker <string> path to PHOTOMAKER model
|
--photo-maker <string> path to PHOTOMAKER model
|
||||||
--upscale-model <string> path to esrgan model.
|
--upscale-model <string> path to esrgan model.
|
||||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0,
|
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||||
then threads will be set to the number of CPU physical cores
|
CPU physical cores
|
||||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
--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
|
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||||
graph splitting; a negative value auto-detects free VRAM, sparing the
|
--vae-tiling process vae in tiles to reduce memory usage
|
||||||
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
|
--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
|
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||||
when needed
|
|
||||||
--mmap whether to memory-map model
|
--mmap whether to memory-map model
|
||||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||||
--clip-on-cpu keep clip in cpu (for low vram)
|
--clip-on-cpu keep clip in cpu (for low vram)
|
||||||
@ -178,19 +172,20 @@ Context Options:
|
|||||||
--chroma-disable-dit-mask disable dit mask for chroma
|
--chroma-disable-dit-mask disable dit mask for chroma
|
||||||
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
--qwen-image-zero-cond-t enable zero_cond_t for qwen image
|
||||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
--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,
|
--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
|
||||||
q4_K). If not specified, the default is the type of the weight file
|
type of the weight file
|
||||||
--rng RNG, one of [std_default, cuda, cpu], default: cuda(sd-webui), cpu(comfyui)
|
--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
|
--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,
|
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow, flux2_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
|
||||||
--lora-apply-mode the way to apply LoRA, one of [auto, immediately, at_runtime], default is
|
contain any quantized parameters, the at_runtime mode will be used; otherwise,
|
||||||
auto. In auto mode, if the model weights contain any quantized parameters,
|
immediately will be used.The immediately mode may have precision and
|
||||||
the at_runtime mode will be used; otherwise, immediately will be used.The
|
compatibility issues with quantized parameters, but it usually offers faster inference
|
||||||
immediately mode may have precision and compatibility issues with quantized
|
speed and, in some cases, lower memory usage. The at_runtime mode, on the
|
||||||
parameters, but it usually offers faster inference speed and, in some cases,
|
other hand, is exactly the opposite.
|
||||||
lower memory usage. The at_runtime mode, on the other hand, is exactly the
|
--vae-tile-size tile size for vae tiling, format [X]x[Y] (default: 32x32)
|
||||||
opposite.
|
--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)
|
||||||
|
|
||||||
Default Generation Options:
|
Default Generation Options:
|
||||||
-p, --prompt <string> the prompt to render
|
-p, --prompt <string> the prompt to render
|
||||||
@ -199,106 +194,65 @@ Default Generation Options:
|
|||||||
--end-img <string> path to the end image, required by flf2v
|
--end-img <string> path to the end image, required by flf2v
|
||||||
--mask <string> path to the mask image
|
--mask <string> path to the mask image
|
||||||
--control-image <string> path to control image, control net
|
--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
|
--control-video <string> path to control video frames, It must be a directory path. The video frames inside should be stored as images in
|
||||||
inside should be stored as images in lexicographical (character) order. For
|
lexicographical (character) order. For example, if the control video path is
|
||||||
example, if the control video path is `frames`, the directory contain images
|
`frames`, the directory contain images such as 00.png, 01.png, ... etc.
|
||||||
such as 00.png, 01.png, ... etc.
|
|
||||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
--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)
|
-H, --height <int> image height, in pixel space (default: 512)
|
||||||
-W, --width <int> image width, in pixel space (default: 512)
|
-W, --width <int> image width, in pixel space (default: 512)
|
||||||
--steps <int> number of sample steps (default: 20)
|
--steps <int> number of sample steps (default: 20)
|
||||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
--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
|
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1). <= 0 represents unspecified,
|
||||||
(default: -1). <= 0 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
|
will be 1 for SD1.x, 2 for SD2.x
|
||||||
-b, --batch-count <int> batch count
|
-b, --batch-count <int> batch count
|
||||||
--video-frames <int> video frames (default: 1)
|
--video-frames <int> video frames (default: 1)
|
||||||
--fps <int> fps (default: 24)
|
--fps <int> fps (default: 24)
|
||||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for
|
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for NitroSD-Realism around 250 and 500 for
|
||||||
NitroSD-Realism around 250 and 500 for NitroSD-Vibrant
|
NitroSD-Vibrant
|
||||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||||
--upscale-tile-size <int> tile size for ESRGAN upscaling (default: 128)
|
--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)
|
--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
|
--img-cfg-scale <float> image guidance scale for inpaint or instruct-pix2pix models: (default: same as --cfg-scale)
|
||||||
--cfg-scale)
|
|
||||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
--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
|
--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
|
||||||
disabled, a value of 2.5 is nice for sd3.5 medium
|
medium
|
||||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||||
--eta <float> noise multiplier (default: 0 for ddim_trailing, tcd, res_multistep and
|
--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)
|
||||||
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)
|
--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-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:
|
--high-noise-img-cfg-scale <float> (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale)
|
||||||
same as --cfg-scale)
|
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input (default: 3.5)
|
||||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input
|
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||||
(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-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-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,
|
--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)
|
||||||
res_multistep and res_2s; 1 for euler_a, er_sde and dpm++2s_a)
|
|
||||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||||
--pm-style-strength <float>
|
--pm-style-strength <float>
|
||||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full
|
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||||
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
|
||||||
--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
|
--vace-strength <float> wan vace strength
|
||||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
--increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1).
|
||||||
--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-auto-resize-ref-image disable auto resize of ref images
|
||||||
--disable-image-metadata do not embed generation metadata on image files
|
--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)
|
-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,
|
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||||
dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s,
|
tcd, res_multistep, res_2s, er_sde] (default: euler for Flux/SD3/Wan, euler_a
|
||||||
er_sde, euler_cfg_pp, euler_a_cfg_pp] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
otherwise)
|
||||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a,
|
--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,
|
||||||
dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep,
|
ddim_trailing, tcd, res_multistep, res_2s, er_sde] default: euler for Flux/SD3/Wan,
|
||||||
res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp] default: euler for Flux/SD3/Wan, euler_a otherwise
|
euler_a otherwise
|
||||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits,
|
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple,
|
||||||
smoothstep, sgm_uniform, simple, kl_optimal, lcm, bong_tangent, ltx2], default:
|
kl_optimal, lcm, bong_tangent], default: discrete
|
||||||
model-specific
|
--sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0").
|
||||||
--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])
|
--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])
|
--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)
|
-r, --ref-image reference image for Flux Kontext models (can be used multiple times)
|
||||||
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET),
|
--cache-mode caching method: 'easycache' (DiT), 'ucache' (UNET), 'dbcache'/'taylorseer'/'cache-dit' (DiT block-level), 'spectrum' (UNET/DiT Chebyshev+Taylor forecasting)
|
||||||
'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:
|
--cache-option named cache params (key=value format, comma-separated). easycache/ucache:
|
||||||
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit:
|
threshold=,start=,end=,decay=,relative=,reset=; dbcache/taylorseer/cache-dit: Fn=,Bn=,threshold=,warmup=. Examples:
|
||||||
Fn=,Bn=,threshold=,warmup=; spectrum: w=,m=,lam=,window=,flex=,warmup=,stop=.
|
"threshold=0.25" or "threshold=1.5,reset=0"
|
||||||
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-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'
|
--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)
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -9,7 +9,7 @@ The server currently exposes three API families:
|
|||||||
- `sdcpp API` under `/sdcpp/v1/...`
|
- `sdcpp API` under `/sdcpp/v1/...`
|
||||||
|
|
||||||
The `sdcpp API` is the native API surface.
|
The `sdcpp API` is the native API surface.
|
||||||
Its request schema is the same schema used by `sd_cpp_extra_args`.
|
Its request schema is also the canonical schema for `sd_cpp_extra_args`.
|
||||||
|
|
||||||
Global LoRA rule:
|
Global LoRA rule:
|
||||||
|
|
||||||
@ -38,8 +38,6 @@ Current generation-related endpoints include:
|
|||||||
- `POST /sdapi/v1/txt2img`
|
- `POST /sdapi/v1/txt2img`
|
||||||
- `POST /sdapi/v1/img2img`
|
- `POST /sdapi/v1/img2img`
|
||||||
- `GET /sdapi/v1/loras`
|
- `GET /sdapi/v1/loras`
|
||||||
- `GET /sdapi/v1/upscalers`
|
|
||||||
- `GET /sdapi/v1/latent-upscale-modes`
|
|
||||||
- `GET /sdapi/v1/samplers`
|
- `GET /sdapi/v1/samplers`
|
||||||
- `GET /sdapi/v1/schedulers`
|
- `GET /sdapi/v1/schedulers`
|
||||||
- `GET /sdapi/v1/sd-models`
|
- `GET /sdapi/v1/sd-models`
|
||||||
@ -57,6 +55,8 @@ Current endpoints include:
|
|||||||
- `POST /sdcpp/v1/jobs/{id}/cancel`
|
- `POST /sdcpp/v1/jobs/{id}/cancel`
|
||||||
- `POST /sdcpp/v1/vid_gen`
|
- `POST /sdcpp/v1/vid_gen`
|
||||||
|
|
||||||
|
`POST /sdcpp/v1/vid_gen` is currently exposed but returns `501 Not Implemented`.
|
||||||
|
|
||||||
## `sd_cpp_extra_args`
|
## `sd_cpp_extra_args`
|
||||||
|
|
||||||
`sd_cpp_extra_args` is an extension mechanism for the compatibility APIs.
|
`sd_cpp_extra_args` is an extension mechanism for the compatibility APIs.
|
||||||
@ -79,12 +79,12 @@ Behavior:
|
|||||||
- The JSON block is parsed using the same field rules as the `sdcpp API`.
|
- The JSON block is parsed using the same field rules as the `sdcpp API`.
|
||||||
- The block is removed from the final prompt before generation.
|
- The block is removed from the final prompt before generation.
|
||||||
|
|
||||||
Supported use:
|
Intended use:
|
||||||
|
|
||||||
- extend `OpenAI API` requests with native `stable-diffusion.cpp` controls
|
- extend `OpenAI API` requests with native `stable-diffusion.cpp` controls
|
||||||
- extend `sdapi` requests with native `stable-diffusion.cpp` controls
|
- extend `sdapi` requests with native `stable-diffusion.cpp` controls
|
||||||
|
|
||||||
Unsupported use:
|
Not intended use:
|
||||||
|
|
||||||
- do not use `sd_cpp_extra_args` with `/sdcpp/v1/*`
|
- do not use `sd_cpp_extra_args` with `/sdcpp/v1/*`
|
||||||
|
|
||||||
@ -218,13 +218,6 @@ Currently supported request fields:
|
|||||||
| `scheduler` | `string` | Scheduler name |
|
| `scheduler` | `string` | Scheduler name |
|
||||||
| `lora` | `array<object>` | Structured LoRA list |
|
| `lora` | `array<object>` | Structured LoRA list |
|
||||||
| `extra_images` | `array<string>` | Base64 or data URL images |
|
| `extra_images` | `array<string>` | Base64 or data URL images |
|
||||||
| `enable_hr` | `boolean` | Enable highres fix for `txt2img` |
|
|
||||||
| `hr_upscaler` | `string` | `Lanczos`, `Nearest`, a latent mode such as `Latent (nearest-exact)`, or an upscaler model name from `/sdapi/v1/upscalers` |
|
|
||||||
| `hr_scale` | `number` | Highres scale when resize target is not set |
|
|
||||||
| `hr_resize_x` | `integer` | Highres target width, `0` to use scale |
|
|
||||||
| `hr_resize_y` | `integer` | Highres target height, `0` to use scale |
|
|
||||||
| `hr_steps` | `integer` | Highres second-pass sample steps, `0` to reuse `steps` |
|
|
||||||
| `denoising_strength` | `number` | Highres denoising strength for `txt2img` |
|
|
||||||
|
|
||||||
Native extension fields:
|
Native extension fields:
|
||||||
|
|
||||||
@ -250,8 +243,6 @@ Currently supported request fields:
|
|||||||
| `inpainting_mask_invert` | `integer` or `boolean` | Treated as invert flag |
|
| `inpainting_mask_invert` | `integer` or `boolean` | Treated as invert flag |
|
||||||
| `denoising_strength` | `number` | Clamped to `0.0..1.0` |
|
| `denoising_strength` | `number` | Clamped to `0.0..1.0` |
|
||||||
|
|
||||||
Highres fix fields are currently handled for `txt2img`; `img2img` uses `denoising_strength` as image-to-image strength.
|
|
||||||
|
|
||||||
Native extension fields:
|
Native extension fields:
|
||||||
|
|
||||||
- any `sdcpp API` fields embedded through `sd_cpp_extra_args` inside `prompt`
|
- any `sdcpp API` fields embedded through `sd_cpp_extra_args` inside `prompt`
|
||||||
@ -269,8 +260,6 @@ Response fields:
|
|||||||
Currently exposed:
|
Currently exposed:
|
||||||
|
|
||||||
- `GET /sdapi/v1/loras`
|
- `GET /sdapi/v1/loras`
|
||||||
- `GET /sdapi/v1/upscalers`
|
|
||||||
- `GET /sdapi/v1/latent-upscale-modes`
|
|
||||||
- `GET /sdapi/v1/samplers`
|
- `GET /sdapi/v1/samplers`
|
||||||
- `GET /sdapi/v1/schedulers`
|
- `GET /sdapi/v1/schedulers`
|
||||||
- `GET /sdapi/v1/sd-models`
|
- `GET /sdapi/v1/sd-models`
|
||||||
@ -285,26 +274,6 @@ Response fields:
|
|||||||
| `[].name` | `string` | Display name derived from file stem |
|
| `[].name` | `string` | Display name derived from file stem |
|
||||||
| `[].path` | `string` | Relative path under the configured LoRA directory |
|
| `[].path` | `string` | Relative path under the configured LoRA directory |
|
||||||
|
|
||||||
`GET /sdapi/v1/upscalers`
|
|
||||||
|
|
||||||
| Field | Type | Notes |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `[].name` | `string` | Built-in name or model stem |
|
|
||||||
| `[].model_name` | `string \| null` | Model family label for model-backed upscalers |
|
|
||||||
| `[].model_path` | `string \| null` | Absolute model path for model-backed upscalers |
|
|
||||||
| `[].model_url` | `string \| null` | Currently always null |
|
|
||||||
| `[].scale` | `integer` | Currently `4` |
|
|
||||||
|
|
||||||
Built-in entries include `None`, `Lanczos`, and `Nearest`. Model-backed entries are scanned from the top level of `--hires-upscalers-dir`; subdirectories are not scanned.
|
|
||||||
|
|
||||||
`GET /sdapi/v1/latent-upscale-modes`
|
|
||||||
|
|
||||||
| Field | Type | Notes |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `[].name` | `string` | WebUI-compatible latent upscale mode name |
|
|
||||||
|
|
||||||
Built-in latent modes include `Latent`, `Latent (nearest)`, `Latent (nearest-exact)`, `Latent (antialiased)`, `Latent (bicubic)`, and `Latent (bicubic antialiased)`.
|
|
||||||
|
|
||||||
`GET /sdapi/v1/samplers`
|
`GET /sdapi/v1/samplers`
|
||||||
|
|
||||||
| Field | Type | Notes |
|
| Field | Type | Notes |
|
||||||
@ -403,26 +372,20 @@ Field types:
|
|||||||
|
|
||||||
Returns frontend-friendly capability metadata.
|
Returns frontend-friendly capability metadata.
|
||||||
|
|
||||||
The mode-aware fields are the primary interface. The top-level compatibility fields are deprecated mirrors kept for older clients.
|
Typical contents:
|
||||||
|
|
||||||
Top-level fields:
|
| Field | Type |
|
||||||
|
| --- | --- |
|
||||||
|
| `model` | `object` |
|
||||||
|
| `defaults` | `object` |
|
||||||
|
| `loras` | `array<object>` |
|
||||||
|
| `samplers` | `array<string>` |
|
||||||
|
| `schedulers` | `array<string>` |
|
||||||
|
| `output_formats` | `array<string>` |
|
||||||
|
| `limits` | `object` |
|
||||||
|
| `features` | `object` |
|
||||||
|
|
||||||
| Field | Type | Notes |
|
Nested fields currently returned:
|
||||||
| --- | --- | --- |
|
|
||||||
| `model` | `object` | Loaded model metadata |
|
|
||||||
| `current_mode` | `string` | The native generation mode mirrored by top-level compatibility fields |
|
|
||||||
| `supported_modes` | `array<string>` | Supported native modes such as `img_gen` or `vid_gen` |
|
|
||||||
| `defaults` | `object` | Deprecated compatibility mirror of `defaults_by_mode[current_mode]` |
|
|
||||||
| `output_formats` | `array<string>` | Deprecated compatibility mirror of `output_formats_by_mode[current_mode]` |
|
|
||||||
| `features` | `object` | Deprecated compatibility mirror of `features_by_mode[current_mode]` |
|
|
||||||
| `defaults_by_mode` | `object` | Explicit defaults for each supported mode |
|
|
||||||
| `output_formats_by_mode` | `object` | Explicit output formats for each supported mode |
|
|
||||||
| `features_by_mode` | `object` | Explicit feature flags for each supported mode |
|
|
||||||
| `samplers` | `array<string>` | Available sampling methods |
|
|
||||||
| `schedulers` | `array<string>` | Available schedulers |
|
|
||||||
| `loras` | `array<object>` | Available LoRA entries |
|
|
||||||
| `upscalers` | `array<object>` | Available model-backed highres upscalers |
|
|
||||||
| `limits` | `object` | Shared queue and size limits |
|
|
||||||
|
|
||||||
`model`
|
`model`
|
||||||
|
|
||||||
@ -432,24 +395,50 @@ Top-level fields:
|
|||||||
| `model.stem` | `string` |
|
| `model.stem` | `string` |
|
||||||
| `model.path` | `string` |
|
| `model.path` | `string` |
|
||||||
|
|
||||||
Compatibility rules:
|
`defaults`
|
||||||
|
|
||||||
- `defaults`, `output_formats`, and `features` are deprecated compatibility mirrors
|
|
||||||
- those three top-level fields always mirror `current_mode`
|
|
||||||
- `supported_modes`, `defaults_by_mode`, `output_formats_by_mode`, and `features_by_mode` are the mode-aware fields
|
|
||||||
|
|
||||||
Mode-aware objects:
|
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `defaults_by_mode.img_gen` | `object` |
|
| `defaults.prompt` | `string` |
|
||||||
| `defaults_by_mode.vid_gen` | `object` |
|
| `defaults.negative_prompt` | `string` |
|
||||||
| `output_formats_by_mode.img_gen` | `array<string>` |
|
| `defaults.clip_skip` | `integer` |
|
||||||
| `output_formats_by_mode.vid_gen` | `array<string>` |
|
| `defaults.width` | `integer` |
|
||||||
| `features_by_mode.img_gen` | `object` |
|
| `defaults.height` | `integer` |
|
||||||
| `features_by_mode.vid_gen` | `object` |
|
| `defaults.strength` | `number` |
|
||||||
|
| `defaults.seed` | `integer` |
|
||||||
Shared nested fields:
|
| `defaults.batch_count` | `integer` |
|
||||||
|
| `defaults.auto_resize_ref_image` | `boolean` |
|
||||||
|
| `defaults.increase_ref_index` | `boolean` |
|
||||||
|
| `defaults.control_strength` | `number` |
|
||||||
|
| `defaults.sample_params` | `object` |
|
||||||
|
| `defaults.sample_params.scheduler` | `string` |
|
||||||
|
| `defaults.sample_params.sample_method` | `string` |
|
||||||
|
| `defaults.sample_params.sample_steps` | `integer` |
|
||||||
|
| `defaults.sample_params.eta` | `number \| null` |
|
||||||
|
| `defaults.sample_params.shifted_timestep` | `integer` |
|
||||||
|
| `defaults.sample_params.flow_shift` | `number \| null` |
|
||||||
|
| `defaults.sample_params.guidance` | `object` |
|
||||||
|
| `defaults.sample_params.guidance.txt_cfg` | `number` |
|
||||||
|
| `defaults.sample_params.guidance.img_cfg` | `number \| null` |
|
||||||
|
| `defaults.sample_params.guidance.distilled_guidance` | `number` |
|
||||||
|
| `defaults.sample_params.guidance.slg` | `object` |
|
||||||
|
| `defaults.sample_params.guidance.slg.layers` | `array<integer>` |
|
||||||
|
| `defaults.sample_params.guidance.slg.layer_start` | `number` |
|
||||||
|
| `defaults.sample_params.guidance.slg.layer_end` | `number` |
|
||||||
|
| `defaults.sample_params.guidance.slg.scale` | `number` |
|
||||||
|
| `defaults.vae_tiling_params` | `object` |
|
||||||
|
| `defaults.vae_tiling_params.enabled` | `boolean` |
|
||||||
|
| `defaults.vae_tiling_params.tile_size_x` | `integer` |
|
||||||
|
| `defaults.vae_tiling_params.tile_size_y` | `integer` |
|
||||||
|
| `defaults.vae_tiling_params.target_overlap` | `number` |
|
||||||
|
| `defaults.vae_tiling_params.rel_size_x` | `number` |
|
||||||
|
| `defaults.vae_tiling_params.rel_size_y` | `number` |
|
||||||
|
| `defaults.cache_mode` | `string` |
|
||||||
|
| `defaults.cache_option` | `string` |
|
||||||
|
| `defaults.scm_mask` | `string` |
|
||||||
|
| `defaults.scm_policy_dynamic` | `boolean` |
|
||||||
|
| `defaults.output_format` | `string` |
|
||||||
|
| `defaults.output_compression` | `integer` |
|
||||||
|
|
||||||
`loras`
|
`loras`
|
||||||
|
|
||||||
@ -458,14 +447,6 @@ Shared nested fields:
|
|||||||
| `loras[].name` | `string` |
|
| `loras[].name` | `string` |
|
||||||
| `loras[].path` | `string` |
|
| `loras[].path` | `string` |
|
||||||
|
|
||||||
`upscalers`
|
|
||||||
|
|
||||||
| Field | Type | Notes |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `upscalers[].name` | `string` | Built-in name or model stem; use this value in `hires.upscaler` |
|
|
||||||
|
|
||||||
Built-in entries include `None`, `Lanczos`, `Nearest`, `Latent`, `Latent (nearest)`, `Latent (nearest-exact)`, `Latent (antialiased)`, `Latent (bicubic)`, and `Latent (bicubic antialiased)`. Model-backed entries are scanned from the top level of `--hires-upscalers-dir`; subdirectories are not scanned.
|
|
||||||
|
|
||||||
`limits`
|
`limits`
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
@ -477,115 +458,19 @@ Built-in entries include `None`, `Lanczos`, `Nearest`, `Latent`, `Latent (neares
|
|||||||
| `limits.max_batch_count` | `integer` |
|
| `limits.max_batch_count` | `integer` |
|
||||||
| `limits.max_queue_size` | `integer` |
|
| `limits.max_queue_size` | `integer` |
|
||||||
|
|
||||||
Shared default fields used by both `img_gen` and `vid_gen`:
|
`features`
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `prompt` | `string` |
|
| `features.init_image` | `boolean` |
|
||||||
| `negative_prompt` | `string` |
|
| `features.mask_image` | `boolean` |
|
||||||
| `clip_skip` | `integer` |
|
| `features.control_image` | `boolean` |
|
||||||
| `width` | `integer` |
|
| `features.ref_images` | `boolean` |
|
||||||
| `height` | `integer` |
|
| `features.lora` | `boolean` |
|
||||||
| `strength` | `number` |
|
| `features.vae_tiling` | `boolean` |
|
||||||
| `seed` | `integer` |
|
| `features.cache` | `boolean` |
|
||||||
| `sample_params` | `object` |
|
| `features.cancel_queued` | `boolean` |
|
||||||
| `sample_params.scheduler` | `string` |
|
| `features.cancel_generating` | `boolean` |
|
||||||
| `sample_params.sample_method` | `string` |
|
|
||||||
| `sample_params.sample_steps` | `integer` |
|
|
||||||
| `sample_params.eta` | `number \| null` |
|
|
||||||
| `sample_params.shifted_timestep` | `integer` |
|
|
||||||
| `sample_params.flow_shift` | `number \| null` |
|
|
||||||
| `sample_params.guidance.txt_cfg` | `number` |
|
|
||||||
| `sample_params.guidance.img_cfg` | `number \| null` |
|
|
||||||
| `sample_params.guidance.distilled_guidance` | `number` |
|
|
||||||
| `sample_params.guidance.slg.layers` | `array<integer>` |
|
|
||||||
| `sample_params.guidance.slg.layer_start` | `number` |
|
|
||||||
| `sample_params.guidance.slg.layer_end` | `number` |
|
|
||||||
| `sample_params.guidance.slg.scale` | `number` |
|
|
||||||
| `vae_tiling_params` | `object` |
|
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
|
||||||
| `cache_mode` | `string` |
|
|
||||||
| `cache_option` | `string` |
|
|
||||||
| `scm_mask` | `string` |
|
|
||||||
| `scm_policy_dynamic` | `boolean` |
|
|
||||||
| `output_format` | `string` |
|
|
||||||
| `output_compression` | `integer` |
|
|
||||||
|
|
||||||
`vae_tiling_params.extra_tiling_args` accepts a key=value list. For LTX video VAE temporal tiling, `temporal_tile_frames` defaults to `4` and `temporal_tile_overlap` defaults to `1`.
|
|
||||||
|
|
||||||
`img_gen`-specific default fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `batch_count` | `integer` |
|
|
||||||
| `auto_resize_ref_image` | `boolean` |
|
|
||||||
| `increase_ref_index` | `boolean` |
|
|
||||||
| `control_strength` | `number` |
|
|
||||||
| `hires` | `object` |
|
|
||||||
| `hires.enabled` | `boolean` |
|
|
||||||
| `hires.upscaler` | `string` |
|
|
||||||
| `hires.scale` | `number` |
|
|
||||||
| `hires.target_width` | `integer` |
|
|
||||||
| `hires.target_height` | `integer` |
|
|
||||||
| `hires.steps` | `integer` |
|
|
||||||
| `hires.denoising_strength` | `number` |
|
|
||||||
| `hires.custom_sigmas` | `array<number>` |
|
|
||||||
| `hires.upscale_tile_size` | `integer` |
|
|
||||||
|
|
||||||
`vid_gen`-specific default fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `video_frames` | `integer` |
|
|
||||||
| `fps` | `integer` |
|
|
||||||
| `moe_boundary` | `number` |
|
|
||||||
| `vace_strength` | `number` |
|
|
||||||
| `high_noise_sample_params` | `object` |
|
|
||||||
| `high_noise_sample_params.scheduler` | `string` |
|
|
||||||
| `high_noise_sample_params.sample_method` | `string` |
|
|
||||||
| `high_noise_sample_params.sample_steps` | `integer` |
|
|
||||||
| `high_noise_sample_params.eta` | `number \| null` |
|
|
||||||
| `high_noise_sample_params.shifted_timestep` | `integer` |
|
|
||||||
| `high_noise_sample_params.flow_shift` | `number \| null` |
|
|
||||||
| `high_noise_sample_params.guidance.txt_cfg` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.img_cfg` | `number \| null` |
|
|
||||||
| `high_noise_sample_params.guidance.distilled_guidance` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layers` | `array<integer>` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layer_start` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layer_end` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.scale` | `number` |
|
|
||||||
|
|
||||||
Fields returned in `features_by_mode.img_gen`:
|
|
||||||
|
|
||||||
- `init_image`
|
|
||||||
- `mask_image`
|
|
||||||
- `control_image`
|
|
||||||
- `ref_images`
|
|
||||||
- `lora`
|
|
||||||
- `vae_tiling`
|
|
||||||
- `hires`
|
|
||||||
- `cache`
|
|
||||||
- `cancel_queued`
|
|
||||||
- `cancel_generating`
|
|
||||||
|
|
||||||
Fields returned in `features_by_mode.vid_gen`:
|
|
||||||
|
|
||||||
- `init_image`
|
|
||||||
- `end_image`
|
|
||||||
- `control_frames`
|
|
||||||
- `high_noise_sample_params`
|
|
||||||
- `lora`
|
|
||||||
- `vae_tiling`
|
|
||||||
- `cache`
|
|
||||||
- `cancel_queued`
|
|
||||||
- `cancel_generating`
|
|
||||||
|
|
||||||
#### `POST /sdcpp/v1/img_gen`
|
#### `POST /sdcpp/v1/img_gen`
|
||||||
|
|
||||||
@ -636,7 +521,9 @@ Typical status codes:
|
|||||||
- `409 Conflict`
|
- `409 Conflict`
|
||||||
- `410 Gone`
|
- `410 Gone`
|
||||||
|
|
||||||
### Request Body
|
### Canonical Request Schema
|
||||||
|
|
||||||
|
The `sdcpp API` request body is the canonical native schema.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -682,27 +569,14 @@ Example:
|
|||||||
},
|
},
|
||||||
|
|
||||||
"lora": [],
|
"lora": [],
|
||||||
"hires": {
|
|
||||||
"enabled": false,
|
|
||||||
"upscaler": "Latent",
|
|
||||||
"scale": 2.0,
|
|
||||||
"target_width": 0,
|
|
||||||
"target_height": 0,
|
|
||||||
"steps": 0,
|
|
||||||
"denoising_strength": 0.7,
|
|
||||||
"custom_sigmas": [],
|
|
||||||
"upscale_tile_size": 128
|
|
||||||
},
|
|
||||||
|
|
||||||
"vae_tiling_params": {
|
"vae_tiling_params": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"temporal_tiling": false,
|
|
||||||
"tile_size_x": 0,
|
"tile_size_x": 0,
|
||||||
"tile_size_y": 0,
|
"tile_size_y": 0,
|
||||||
"target_overlap": 0.5,
|
"target_overlap": 0.5,
|
||||||
"rel_size_x": 0.0,
|
"rel_size_x": 0.0,
|
||||||
"rel_size_y": 0.0,
|
"rel_size_y": 0.0
|
||||||
"extra_tiling_args": ""
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"cache_mode": "disabled",
|
"cache_mode": "disabled",
|
||||||
@ -738,7 +612,7 @@ Channel expectations:
|
|||||||
If omitted or null:
|
If omitted or null:
|
||||||
|
|
||||||
- single-image fields map to an empty `sd_image_t`
|
- single-image fields map to an empty `sd_image_t`
|
||||||
- array fields map to an empty C-style array, represented as `pointer = nullptr` and `count = 0`
|
- array fields map to `nullptr + count = 0`
|
||||||
|
|
||||||
### Field Mapping Summary
|
### Field Mapping Summary
|
||||||
|
|
||||||
@ -799,32 +673,12 @@ Other native fields:
|
|||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `hires` | `object` |
|
|
||||||
| `hires.enabled` | `boolean` |
|
|
||||||
| `hires.upscaler` | `string` |
|
|
||||||
| `hires.scale` | `number` |
|
|
||||||
| `hires.target_width` | `integer` |
|
|
||||||
| `hires.target_height` | `integer` |
|
|
||||||
| `hires.steps` | `integer` |
|
|
||||||
| `hires.denoising_strength` | `number` |
|
|
||||||
| `hires.custom_sigmas` | `array<number>` |
|
|
||||||
| `hires.upscale_tile_size` | `integer` |
|
|
||||||
| `vae_tiling_params` | `object` |
|
| `vae_tiling_params` | `object` |
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
|
||||||
| `cache_mode` | `string` |
|
| `cache_mode` | `string` |
|
||||||
| `cache_option` | `string` |
|
| `cache_option` | `string` |
|
||||||
| `scm_mask` | `string` |
|
| `scm_mask` | `string` |
|
||||||
| `scm_policy_dynamic` | `boolean` |
|
| `scm_policy_dynamic` | `boolean` |
|
||||||
|
|
||||||
For `hires.upscaler`, use `Lanczos`, `Nearest`, `Latent`, `Latent (nearest)`, `Latent (nearest-exact)`, `Latent (antialiased)`, `Latent (bicubic)`, `Latent (bicubic antialiased)`, or an `upscalers[].name` value from `GET /sdcpp/v1/capabilities`. Model-backed upscalers are resolved as `--hires-upscalers-dir / (name + ext)` and must live directly in that directory. `hires.custom_sigmas`, when present, overrides the generated second-pass hires sigma schedule; otherwise the hires schedule is trimmed by `hires.denoising_strength`.
|
|
||||||
|
|
||||||
HTTP-only output fields:
|
HTTP-only output fields:
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
@ -832,11 +686,11 @@ HTTP-only output fields:
|
|||||||
| `output_format` | `string` |
|
| `output_format` | `string` |
|
||||||
| `output_compression` | `integer` |
|
| `output_compression` | `integer` |
|
||||||
|
|
||||||
### Optional Field Handling
|
### Optional Field Semantics
|
||||||
|
|
||||||
Optional sampling fields may be omitted.
|
Clients should preserve unset semantics for optional sampling fields.
|
||||||
|
|
||||||
When omitted, backend defaults apply to these fields:
|
If a user has not explicitly provided one of these fields, the client should omit it instead of injecting a guessed fallback:
|
||||||
|
|
||||||
- `sample_params.scheduler`
|
- `sample_params.scheduler`
|
||||||
- `sample_params.sample_method`
|
- `sample_params.sample_method`
|
||||||
@ -912,404 +766,29 @@ Example cancelled job:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Submission Errors
|
### Validation and Retention
|
||||||
|
|
||||||
`POST /sdcpp/v1/img_gen` may return:
|
Recommended behavior:
|
||||||
|
|
||||||
- `202 Accepted` when the job is created
|
- malformed JSON returns `400`
|
||||||
- `400 Bad Request` for an empty body, unsupported model mode, invalid JSON, or invalid generation parameters
|
- invalid image payloads return `400`
|
||||||
- `429 Too Many Requests` when the job queue is full
|
- invalid parameter structure returns `400`
|
||||||
- `500 Internal Server Error` for unexpected server exceptions during submission
|
- queue full returns `429` or `503`
|
||||||
|
- accepted runtime failures transition the job to `failed`
|
||||||
|
- unsupported in-progress cancellation may return `409`
|
||||||
|
|
||||||
### `vid_gen`
|
Recommended retention controls:
|
||||||
|
|
||||||
The following section documents the native async contract for video generation.
|
- pending job limit
|
||||||
|
- completed job TTL
|
||||||
|
- failed job TTL
|
||||||
|
|
||||||
#### `POST /sdcpp/v1/vid_gen`
|
### Future `vid_gen`
|
||||||
|
|
||||||
Submits an async video generation job.
|
Future `vid_gen` should reuse the same async job model:
|
||||||
|
|
||||||
Successful submission returns `202 Accepted`.
|
- `POST /sdcpp/v1/vid_gen`
|
||||||
|
- `GET /sdcpp/v1/jobs/{id}`
|
||||||
|
- `POST /sdcpp/v1/jobs/{id}/cancel`
|
||||||
|
|
||||||
Example response:
|
Its request body should mirror `sd_vid_gen_params_t` in the same way that `img_gen` mirrors `sd_img_gen_params_t`.
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "job_01HTXYZVID",
|
|
||||||
"kind": "vid_gen",
|
|
||||||
"status": "queued",
|
|
||||||
"created": 1775401200,
|
|
||||||
"poll_url": "/sdcpp/v1/jobs/job_01HTXYZVID"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Response fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `id` | `string` |
|
|
||||||
| `kind` | `string` |
|
|
||||||
| `status` | `string` |
|
|
||||||
| `created` | `integer` |
|
|
||||||
| `poll_url` | `string` |
|
|
||||||
|
|
||||||
### Request Body
|
|
||||||
|
|
||||||
Compared with `img_gen`, the `vid_gen` request body:
|
|
||||||
|
|
||||||
- `vid_gen` is a single video sequence job, so `batch_count` is not part of the request schema
|
|
||||||
- `ref_images`, `mask_image`, `control_image`, `control_strength`, and `embed_image_metadata` are not part of the request schema
|
|
||||||
- `vid_gen` adds `end_image`, `control_frames`, `high_noise_sample_params`, `video_frames`, `fps`, `moe_boundary`, and `vace_strength`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"prompt": "a cat walking through a rainy alley",
|
|
||||||
"negative_prompt": "",
|
|
||||||
"clip_skip": -1,
|
|
||||||
"width": 832,
|
|
||||||
"height": 480,
|
|
||||||
"strength": 0.75,
|
|
||||||
"seed": -1,
|
|
||||||
"video_frames": 33,
|
|
||||||
"fps": 16,
|
|
||||||
"moe_boundary": 0.875,
|
|
||||||
"vace_strength": 1.0,
|
|
||||||
|
|
||||||
"init_image": null,
|
|
||||||
"end_image": null,
|
|
||||||
"control_frames": [],
|
|
||||||
|
|
||||||
"sample_params": {
|
|
||||||
"scheduler": "discrete",
|
|
||||||
"sample_method": "euler",
|
|
||||||
"sample_steps": 28,
|
|
||||||
"eta": 1.0,
|
|
||||||
"shifted_timestep": 0,
|
|
||||||
"custom_sigmas": [],
|
|
||||||
"flow_shift": 0.0,
|
|
||||||
"guidance": {
|
|
||||||
"txt_cfg": 7.0,
|
|
||||||
"img_cfg": 7.0,
|
|
||||||
"distilled_guidance": 3.5,
|
|
||||||
"slg": {
|
|
||||||
"layers": [7, 8, 9],
|
|
||||||
"layer_start": 0.01,
|
|
||||||
"layer_end": 0.2,
|
|
||||||
"scale": 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"high_noise_sample_params": {
|
|
||||||
"scheduler": "discrete",
|
|
||||||
"sample_method": "euler",
|
|
||||||
"sample_steps": -1,
|
|
||||||
"eta": 1.0,
|
|
||||||
"shifted_timestep": 0,
|
|
||||||
"flow_shift": 0.0,
|
|
||||||
"guidance": {
|
|
||||||
"txt_cfg": 7.0,
|
|
||||||
"img_cfg": 7.0,
|
|
||||||
"distilled_guidance": 3.5,
|
|
||||||
"slg": {
|
|
||||||
"layers": [7, 8, 9],
|
|
||||||
"layer_start": 0.01,
|
|
||||||
"layer_end": 0.2,
|
|
||||||
"scale": 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"lora": [],
|
|
||||||
|
|
||||||
"vae_tiling_params": {
|
|
||||||
"enabled": false,
|
|
||||||
"temporal_tiling": false,
|
|
||||||
"tile_size_x": 0,
|
|
||||||
"tile_size_y": 0,
|
|
||||||
"target_overlap": 0.5,
|
|
||||||
"rel_size_x": 0.0,
|
|
||||||
"rel_size_y": 0.0,
|
|
||||||
"extra_tiling_args": ""
|
|
||||||
},
|
|
||||||
|
|
||||||
"cache_mode": "disabled",
|
|
||||||
"cache_option": "",
|
|
||||||
"scm_mask": "",
|
|
||||||
"scm_policy_dynamic": true,
|
|
||||||
|
|
||||||
"output_format": "webm",
|
|
||||||
"output_compression": 100
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### LoRA Rules
|
|
||||||
|
|
||||||
- The server only accepts explicit LoRA entries from the `lora` field.
|
|
||||||
- Prompt-embedded `<lora:...>` tags are intentionally unsupported.
|
|
||||||
- `lora[].is_high_noise` controls whether a LoRA applies only to the high-noise stage.
|
|
||||||
|
|
||||||
### Image and Frame Encoding Rules
|
|
||||||
|
|
||||||
Any image field accepts:
|
|
||||||
|
|
||||||
- a raw base64 string, or
|
|
||||||
- a data URL such as `data:image/png;base64,...`
|
|
||||||
|
|
||||||
Channel expectations:
|
|
||||||
|
|
||||||
- `init_image`: 3 channels
|
|
||||||
- `end_image`: 3 channels
|
|
||||||
- `control_frames[]`: 3 channels
|
|
||||||
|
|
||||||
Frame ordering rules:
|
|
||||||
|
|
||||||
- `control_frames[]` order is the conditioning frame order
|
|
||||||
- `control_frames[]` is preserved in request order
|
|
||||||
|
|
||||||
If omitted or null:
|
|
||||||
|
|
||||||
- single-image fields map to an empty `sd_image_t`
|
|
||||||
- array fields map to an empty C-style array, represented as `pointer = nullptr` and `count = 0`
|
|
||||||
|
|
||||||
### Field Mapping Summary
|
|
||||||
|
|
||||||
Top-level scalar fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `prompt` | `string` |
|
|
||||||
| `negative_prompt` | `string` |
|
|
||||||
| `clip_skip` | `integer` |
|
|
||||||
| `width` | `integer` |
|
|
||||||
| `height` | `integer` |
|
|
||||||
| `strength` | `number` |
|
|
||||||
| `seed` | `integer` |
|
|
||||||
| `video_frames` | `integer` |
|
|
||||||
| `fps` | `integer` |
|
|
||||||
| `moe_boundary` | `number` |
|
|
||||||
| `vace_strength` | `number` |
|
|
||||||
|
|
||||||
Image and frame fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `init_image` | `string \| null` |
|
|
||||||
| `end_image` | `string \| null` |
|
|
||||||
| `control_frames` | `array<string>` |
|
|
||||||
|
|
||||||
LoRA fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `lora[].path` | `string` |
|
|
||||||
| `lora[].multiplier` | `number` |
|
|
||||||
| `lora[].is_high_noise` | `boolean` |
|
|
||||||
|
|
||||||
Sampling fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `sample_params.scheduler` | `string` |
|
|
||||||
| `sample_params.sample_method` | `string` |
|
|
||||||
| `sample_params.sample_steps` | `integer` |
|
|
||||||
| `sample_params.eta` | `number` |
|
|
||||||
| `sample_params.shifted_timestep` | `integer` |
|
|
||||||
| `sample_params.custom_sigmas` | `array<number>` |
|
|
||||||
| `sample_params.flow_shift` | `number` |
|
|
||||||
| `sample_params.guidance.txt_cfg` | `number` |
|
|
||||||
| `sample_params.guidance.img_cfg` | `number` |
|
|
||||||
| `sample_params.guidance.distilled_guidance` | `number` |
|
|
||||||
| `sample_params.guidance.slg.layers` | `array<integer>` |
|
|
||||||
| `sample_params.guidance.slg.layer_start` | `number` |
|
|
||||||
| `sample_params.guidance.slg.layer_end` | `number` |
|
|
||||||
| `sample_params.guidance.slg.scale` | `number` |
|
|
||||||
|
|
||||||
High-noise sampling fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `high_noise_sample_params.scheduler` | `string` |
|
|
||||||
| `high_noise_sample_params.sample_method` | `string` |
|
|
||||||
| `high_noise_sample_params.sample_steps` | `integer` |
|
|
||||||
| `high_noise_sample_params.eta` | `number` |
|
|
||||||
| `high_noise_sample_params.shifted_timestep` | `integer` |
|
|
||||||
| `high_noise_sample_params.flow_shift` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.txt_cfg` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.img_cfg` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.distilled_guidance` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layers` | `array<integer>` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layer_start` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.layer_end` | `number` |
|
|
||||||
| `high_noise_sample_params.guidance.slg.scale` | `number` |
|
|
||||||
|
|
||||||
Other native fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `vae_tiling_params` | `object` |
|
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
|
||||||
| `cache_mode` | `string` |
|
|
||||||
| `cache_option` | `string` |
|
|
||||||
| `scm_mask` | `string` |
|
|
||||||
| `scm_policy_dynamic` | `boolean` |
|
|
||||||
|
|
||||||
HTTP-only output fields:
|
|
||||||
|
|
||||||
| Field | Type |
|
|
||||||
| --- | --- |
|
|
||||||
| `output_format` | `string` |
|
|
||||||
| `output_compression` | `integer` |
|
|
||||||
|
|
||||||
For `vid_gen`, `output_format` and `output_compression` control container encoding.
|
|
||||||
`fps` is request metadata for the generated sequence and is echoed in the completed job result.
|
|
||||||
|
|
||||||
Allowed `output_format` values:
|
|
||||||
|
|
||||||
- `webm`
|
|
||||||
- `webp`
|
|
||||||
- `avi`
|
|
||||||
|
|
||||||
Output format behavior:
|
|
||||||
|
|
||||||
- `output_format` defaults to `webm`
|
|
||||||
- `webp` means animated WebP
|
|
||||||
- `avi` means MJPG AVI
|
|
||||||
- `webm` requires the server to be built with WebM support; otherwise the request returns `400`
|
|
||||||
|
|
||||||
### Result Payload
|
|
||||||
|
|
||||||
Completed jobs return one encoded container payload, not a list of per-frame images.
|
|
||||||
|
|
||||||
Result fields:
|
|
||||||
|
|
||||||
- `result.b64_json` contains the whole encoded container file as base64
|
|
||||||
- `result.mime_type` identifies the media type
|
|
||||||
- `result.output_format` echoes the selected container format
|
|
||||||
- `result.fps` echoes the effective playback FPS
|
|
||||||
- `result.frame_count` reports the actual decoded frame count used to build the container
|
|
||||||
|
|
||||||
Expected MIME types:
|
|
||||||
|
|
||||||
| `output_format` | `mime_type` |
|
|
||||||
| --- | --- |
|
|
||||||
| `webm` | `video/webm` |
|
|
||||||
| `webp` | `image/webp` |
|
|
||||||
| `avi` | `video/x-msvideo` |
|
|
||||||
|
|
||||||
### Optional Field Handling
|
|
||||||
|
|
||||||
Optional sampling fields may be omitted.
|
|
||||||
|
|
||||||
When omitted, backend defaults apply to these fields:
|
|
||||||
|
|
||||||
- `sample_params.scheduler`
|
|
||||||
- `sample_params.sample_method`
|
|
||||||
- `sample_params.eta`
|
|
||||||
- `sample_params.flow_shift`
|
|
||||||
- `sample_params.guidance.img_cfg`
|
|
||||||
- `high_noise_sample_params.scheduler`
|
|
||||||
- `high_noise_sample_params.sample_method`
|
|
||||||
- `high_noise_sample_params.eta`
|
|
||||||
- `high_noise_sample_params.flow_shift`
|
|
||||||
- `high_noise_sample_params.guidance.img_cfg`
|
|
||||||
|
|
||||||
`high_noise_sample_params` may also be omitted entirely.
|
|
||||||
|
|
||||||
### Frame Count Semantics
|
|
||||||
|
|
||||||
`video_frames` is the requested target length, but the current core video path internally normalizes the effective frame count to the largest `4n + 1` value that does not exceed the requested count.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- `video_frames = 33` stays `33`
|
|
||||||
- `video_frames = 34` becomes `33`
|
|
||||||
- `video_frames = 32` becomes `29`
|
|
||||||
|
|
||||||
The completed job payload includes the actual decoded `frame_count`.
|
|
||||||
|
|
||||||
### Completion Result
|
|
||||||
|
|
||||||
Example completed job:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "job_01HTXYZVID",
|
|
||||||
"kind": "vid_gen",
|
|
||||||
"status": "completed",
|
|
||||||
"created": 1775401200,
|
|
||||||
"started": 1775401203,
|
|
||||||
"completed": 1775401215,
|
|
||||||
"queue_position": 0,
|
|
||||||
"result": {
|
|
||||||
"output_format": "webm",
|
|
||||||
"mime_type": "video/webm",
|
|
||||||
"fps": 16,
|
|
||||||
"frame_count": 33,
|
|
||||||
"b64_json": "GkXfo59ChoEBQveBAULygQRC84EIQo..."
|
|
||||||
},
|
|
||||||
"error": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The response returns the encoded `.webm`, animated `.webp`, or `.avi` container payload directly.
|
|
||||||
|
|
||||||
### Failure Result
|
|
||||||
|
|
||||||
Example failed job:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "job_01HTXYZVID",
|
|
||||||
"kind": "vid_gen",
|
|
||||||
"status": "failed",
|
|
||||||
"created": 1775401200,
|
|
||||||
"started": 1775401203,
|
|
||||||
"completed": 1775401204,
|
|
||||||
"queue_position": 0,
|
|
||||||
"result": null,
|
|
||||||
"error": {
|
|
||||||
"code": "generation_failed",
|
|
||||||
"message": "generate_video returned no results"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cancelled Result
|
|
||||||
|
|
||||||
Example cancelled job:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "job_01HTXYZVID",
|
|
||||||
"kind": "vid_gen",
|
|
||||||
"status": "cancelled",
|
|
||||||
"created": 1775401200,
|
|
||||||
"started": null,
|
|
||||||
"completed": 1775401202,
|
|
||||||
"queue_position": 0,
|
|
||||||
"result": null,
|
|
||||||
"error": {
|
|
||||||
"code": "cancelled",
|
|
||||||
"message": "job cancelled by client"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Submission Errors
|
|
||||||
|
|
||||||
`POST /sdcpp/v1/vid_gen` may return:
|
|
||||||
|
|
||||||
- `202 Accepted` when the job is created
|
|
||||||
- `400 Bad Request` for an empty body, unsupported model mode, invalid JSON, invalid generation parameters, or an unsupported output format
|
|
||||||
- `429 Too Many Requests` when the job queue is full
|
|
||||||
- `500 Internal Server Error` for unexpected server exceptions during submission
|
|
||||||
|
|||||||
@ -95,10 +95,6 @@ bool cancel_queued_job(AsyncJobManager& manager, AsyncGenerationJob& job) {
|
|||||||
job.status = AsyncJobStatus::Cancelled;
|
job.status = AsyncJobStatus::Cancelled;
|
||||||
job.completed_at = unix_timestamp_now();
|
job.completed_at = unix_timestamp_now();
|
||||||
job.result_images_b64.clear();
|
job.result_images_b64.clear();
|
||||||
job.result_media_b64.clear();
|
|
||||||
job.result_media_mime_type.clear();
|
|
||||||
job.result_frame_count = 0;
|
|
||||||
job.result_fps = 0;
|
|
||||||
job.error_code = "cancelled";
|
job.error_code = "cancelled";
|
||||||
job.error_message = "job cancelled by client";
|
job.error_message = "job cancelled by client";
|
||||||
return true;
|
return true;
|
||||||
@ -126,15 +122,6 @@ json make_async_job_json(const AsyncJobManager& manager, const AsyncGenerationJo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (job.status == AsyncJobStatus::Completed) {
|
if (job.status == AsyncJobStatus::Completed) {
|
||||||
if (job.kind == AsyncJobKind::VidGen) {
|
|
||||||
result["result"] = {
|
|
||||||
{"output_format", job.vid_gen.output_format},
|
|
||||||
{"mime_type", job.result_media_mime_type},
|
|
||||||
{"fps", job.result_fps},
|
|
||||||
{"frame_count", job.result_frame_count},
|
|
||||||
{"b64_json", job.result_media_b64},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
json images = json::array();
|
json images = json::array();
|
||||||
for (size_t i = 0; i < job.result_images_b64.size(); ++i) {
|
for (size_t i = 0; i < job.result_images_b64.size(); ++i) {
|
||||||
images.push_back({{"index", i}, {"b64_json", job.result_images_b64[i]}});
|
images.push_back({{"index", i}, {"b64_json", job.result_images_b64[i]}});
|
||||||
@ -143,7 +130,6 @@ json make_async_job_json(const AsyncJobManager& manager, const AsyncGenerationJo
|
|||||||
{"output_format", job.img_gen.output_format},
|
{"output_format", job.img_gen.output_format},
|
||||||
{"images", images},
|
{"images", images},
|
||||||
};
|
};
|
||||||
}
|
|
||||||
result["error"] = nullptr;
|
result["error"] = nullptr;
|
||||||
} else if (job.status == AsyncJobStatus::Failed ||
|
} else if (job.status == AsyncJobStatus::Failed ||
|
||||||
job.status == AsyncJobStatus::Cancelled) {
|
job.status == AsyncJobStatus::Cancelled) {
|
||||||
@ -170,15 +156,16 @@ bool execute_img_gen_job(ServerRuntime& runtime,
|
|||||||
sd_img_gen_params_t params = job.img_gen.to_sd_img_gen_params_t();
|
sd_img_gen_params_t params = job.img_gen.to_sd_img_gen_params_t();
|
||||||
|
|
||||||
SDImageVec results;
|
SDImageVec results;
|
||||||
|
int num_results = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(*runtime.sd_ctx_mutex);
|
std::lock_guard<std::mutex> lock(*runtime.sd_ctx_mutex);
|
||||||
sd_image_t* raw_results = generate_image(runtime.sd_ctx, ¶ms);
|
sd_image_t* raw_results = generate_image(runtime.sd_ctx, ¶ms);
|
||||||
results.adopt(raw_results, params.batch_count);
|
num_results = params.batch_count;
|
||||||
|
results.adopt(raw_results, num_results);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int num_results = results.count();
|
if (results.empty() || num_results <= 0) {
|
||||||
if (num_results <= 0) {
|
|
||||||
error_message = "generate_image returned no results";
|
error_message = "generate_image returned no results";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -221,54 +208,6 @@ bool execute_img_gen_job(ServerRuntime& runtime,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool execute_vid_gen_job(ServerRuntime& runtime,
|
|
||||||
AsyncGenerationJob& job,
|
|
||||||
std::string& output_media_b64,
|
|
||||||
std::string& output_media_mime_type,
|
|
||||||
int& output_frame_count,
|
|
||||||
int& output_fps,
|
|
||||||
std::string& error_message) {
|
|
||||||
sd_vid_gen_params_t params = job.vid_gen.to_sd_vid_gen_params_t();
|
|
||||||
|
|
||||||
SDImageVec results;
|
|
||||||
int num_results = 0;
|
|
||||||
sd_audio_t* generated_audio = nullptr;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(*runtime.sd_ctx_mutex);
|
|
||||||
sd_image_t* raw_results = nullptr;
|
|
||||||
if (!generate_video(runtime.sd_ctx, ¶ms, &raw_results, &num_results, &generated_audio)) {
|
|
||||||
raw_results = nullptr;
|
|
||||||
}
|
|
||||||
results.adopt(raw_results, num_results);
|
|
||||||
}
|
|
||||||
|
|
||||||
num_results = results.count();
|
|
||||||
if (num_results <= 0) {
|
|
||||||
free_sd_audio(generated_audio);
|
|
||||||
error_message = "generate_video returned no results";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> video_bytes = create_video_from_sd_images_to_vector(job.vid_gen.output_format,
|
|
||||||
results.data(),
|
|
||||||
num_results,
|
|
||||||
job.vid_gen.gen_params.fps,
|
|
||||||
job.vid_gen.output_compression,
|
|
||||||
generated_audio);
|
|
||||||
free_sd_audio(generated_audio);
|
|
||||||
if (video_bytes.empty()) {
|
|
||||||
error_message = "failed to encode generated video container";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output_media_b64 = base64_encode(video_bytes);
|
|
||||||
output_media_mime_type = video_mime_type(job.vid_gen.output_format);
|
|
||||||
output_frame_count = num_results;
|
|
||||||
output_fps = job.vid_gen.gen_params.fps;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void async_job_worker(ServerRuntime& runtime) {
|
void async_job_worker(ServerRuntime& runtime) {
|
||||||
AsyncJobManager& manager = *runtime.async_job_manager;
|
AsyncJobManager& manager = *runtime.async_job_manager;
|
||||||
|
|
||||||
@ -301,23 +240,11 @@ void async_job_worker(ServerRuntime& runtime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> output_images;
|
std::vector<std::string> output_images;
|
||||||
std::string output_media_b64;
|
|
||||||
std::string output_media_mime_type;
|
|
||||||
int output_frame_count = 0;
|
|
||||||
int output_fps = 0;
|
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
if (job->kind == AsyncJobKind::ImgGen) {
|
if (job->kind == AsyncJobKind::ImgGen) {
|
||||||
ok = execute_img_gen_job(runtime, *job, output_images, error_message);
|
ok = execute_img_gen_job(runtime, *job, output_images, error_message);
|
||||||
} else if (job->kind == AsyncJobKind::VidGen) {
|
|
||||||
ok = execute_vid_gen_job(runtime,
|
|
||||||
*job,
|
|
||||||
output_media_b64,
|
|
||||||
output_media_mime_type,
|
|
||||||
output_frame_count,
|
|
||||||
output_fps,
|
|
||||||
error_message);
|
|
||||||
} else {
|
} else {
|
||||||
error_message = "unsupported job kind";
|
error_message = "unsupported job kind";
|
||||||
}
|
}
|
||||||
@ -333,10 +260,6 @@ void async_job_worker(ServerRuntime& runtime) {
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
job->status = AsyncJobStatus::Completed;
|
job->status = AsyncJobStatus::Completed;
|
||||||
job->result_images_b64 = std::move(output_images);
|
job->result_images_b64 = std::move(output_images);
|
||||||
job->result_media_b64 = std::move(output_media_b64);
|
|
||||||
job->result_media_mime_type = std::move(output_media_mime_type);
|
|
||||||
job->result_frame_count = output_frame_count;
|
|
||||||
job->result_fps = output_fps;
|
|
||||||
job->error_code.clear();
|
job->error_code.clear();
|
||||||
job->error_message.clear();
|
job->error_message.clear();
|
||||||
} else {
|
} else {
|
||||||
@ -344,10 +267,6 @@ void async_job_worker(ServerRuntime& runtime) {
|
|||||||
job->error_code = "generation_failed";
|
job->error_code = "generation_failed";
|
||||||
job->error_message = error_message.empty() ? "unknown generation error" : error_message;
|
job->error_message = error_message.empty() ? "unknown generation error" : error_message;
|
||||||
job->result_images_b64.clear();
|
job->result_images_b64.clear();
|
||||||
job->result_media_b64.clear();
|
|
||||||
job->result_media_mime_type.clear();
|
|
||||||
job->result_frame_count = 0;
|
|
||||||
job->result_fps = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
purge_expired_jobs(manager);
|
purge_expired_jobs(manager);
|
||||||
|
|||||||
@ -36,12 +36,7 @@ struct AsyncGenerationJob {
|
|||||||
int64_t started_at = 0;
|
int64_t started_at = 0;
|
||||||
int64_t completed_at = 0;
|
int64_t completed_at = 0;
|
||||||
ImgGenJobRequest img_gen;
|
ImgGenJobRequest img_gen;
|
||||||
VidGenJobRequest vid_gen;
|
|
||||||
std::vector<std::string> result_images_b64;
|
std::vector<std::string> result_images_b64;
|
||||||
std::string result_media_b64;
|
|
||||||
std::string result_media_mime_type;
|
|
||||||
int result_frame_count = 0;
|
|
||||||
int result_fps = 0;
|
|
||||||
std::string error_code;
|
std::string error_code;
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
};
|
};
|
||||||
@ -68,11 +63,4 @@ bool execute_img_gen_job(ServerRuntime& runtime,
|
|||||||
AsyncGenerationJob& job,
|
AsyncGenerationJob& job,
|
||||||
std::vector<std::string>& output_images,
|
std::vector<std::string>& output_images,
|
||||||
std::string& error_message);
|
std::string& error_message);
|
||||||
bool execute_vid_gen_job(ServerRuntime& runtime,
|
|
||||||
AsyncGenerationJob& job,
|
|
||||||
std::string& output_media_b64,
|
|
||||||
std::string& output_media_mime_type,
|
|
||||||
int& output_frame_count,
|
|
||||||
int& output_fps,
|
|
||||||
std::string& error_message);
|
|
||||||
void async_job_worker(ServerRuntime& runtime);
|
void async_job_worker(ServerRuntime& runtime);
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 797ccf80825cc035508ba9b599b2a21953e7f835
|
Subproject commit 740475a7a6794dc07fb23e8ec5dc56e7e80aa8c1
|
||||||
@ -48,9 +48,7 @@ static void parse_args(int argc,
|
|||||||
|
|
||||||
if (!svr_params.resolve_and_validate() ||
|
if (!svr_params.resolve_and_validate() ||
|
||||||
!ctx_params.resolve_and_validate(IMG_GEN) ||
|
!ctx_params.resolve_and_validate(IMG_GEN) ||
|
||||||
!default_gen_params.resolve_and_validate(IMG_GEN,
|
!default_gen_params.resolve_and_validate(IMG_GEN, ctx_params.lora_model_dir)) {
|
||||||
ctx_params.lora_model_dir,
|
|
||||||
ctx_params.hires_upscalers_dir)) {
|
|
||||||
print_usage(argv[0], options_vec);
|
print_usage(argv[0], options_vec);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -97,8 +95,6 @@ int main(int argc, const char** argv) {
|
|||||||
|
|
||||||
std::vector<LoraEntry> lora_cache;
|
std::vector<LoraEntry> lora_cache;
|
||||||
std::mutex lora_mutex;
|
std::mutex lora_mutex;
|
||||||
std::vector<UpscalerEntry> upscaler_cache;
|
|
||||||
std::mutex upscaler_mutex;
|
|
||||||
AsyncJobManager async_job_manager;
|
AsyncJobManager async_job_manager;
|
||||||
ServerRuntime runtime = {
|
ServerRuntime runtime = {
|
||||||
sd_ctx.get(),
|
sd_ctx.get(),
|
||||||
@ -108,8 +104,6 @@ int main(int argc, const char** argv) {
|
|||||||
&default_gen_params,
|
&default_gen_params,
|
||||||
&lora_cache,
|
&lora_cache,
|
||||||
&lora_mutex,
|
&lora_mutex,
|
||||||
&upscaler_cache,
|
|
||||||
&upscaler_mutex,
|
|
||||||
&async_job_manager,
|
&async_job_manager,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -145,7 +139,7 @@ int main(int argc, const char** argv) {
|
|||||||
register_sdapi_endpoints(svr, runtime);
|
register_sdapi_endpoints(svr, runtime);
|
||||||
register_sdcpp_api_endpoints(svr, runtime);
|
register_sdcpp_api_endpoints(svr, runtime);
|
||||||
|
|
||||||
LOG_INFO("listening on: http://%s:%d\n", svr_params.listen_ip.c_str(), svr_params.listen_port);
|
LOG_INFO("listening on: %s:%d\n", svr_params.listen_ip.c_str(), svr_params.listen_port);
|
||||||
svr.listen(svr_params.listen_ip, svr_params.listen_port);
|
svr.listen(svr_params.listen_ip, svr_params.listen_port);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@ -70,7 +70,7 @@ static bool build_openai_generation_request(const httplib::Request& req,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
||||||
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", runtime.ctx_params->hires_upscalers_dir, true)) {
|
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", true)) {
|
||||||
error_message = "invalid params";
|
error_message = "invalid params";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ static bool build_openai_edit_request(const httplib::Request& req,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
||||||
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", runtime.ctx_params->hires_upscalers_dir, true)) {
|
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", true)) {
|
||||||
error_message = "invalid params";
|
error_message = "invalid params";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -253,12 +253,6 @@ void register_openai_api_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
|
|
||||||
svr.Post("/v1/images/generations", [runtime](const httplib::Request& req, httplib::Response& res) {
|
svr.Post("/v1/images/generations", [runtime](const httplib::Request& req, httplib::Response& res) {
|
||||||
try {
|
try {
|
||||||
if (!runtime_supports_generation_mode(*runtime, IMG_GEN)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", unsupported_generation_mode_error(IMG_GEN)}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImgGenJobRequest request;
|
ImgGenJobRequest request;
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
if (!build_openai_generation_request(req, *runtime, request, error_message)) {
|
if (!build_openai_generation_request(req, *runtime, request, error_message)) {
|
||||||
@ -325,12 +319,6 @@ void register_openai_api_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
|
|
||||||
svr.Post("/v1/images/edits", [runtime](const httplib::Request& req, httplib::Response& res) {
|
svr.Post("/v1/images/edits", [runtime](const httplib::Request& req, httplib::Response& res) {
|
||||||
try {
|
try {
|
||||||
if (!runtime_supports_generation_mode(*runtime, IMG_GEN)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", unsupported_generation_mode_error(IMG_GEN)}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImgGenJobRequest request;
|
ImgGenJobRequest request;
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
if (!build_openai_edit_request(req, *runtime, request, error_message)) {
|
if (!build_openai_edit_request(req, *runtime, request, error_message)) {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "routes.h"
|
#include "routes.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@ -36,20 +35,14 @@ static fs::path resolve_display_model_path(const ServerRuntime& runtime) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string lower_ascii(std::string value) {
|
|
||||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
|
|
||||||
return static_cast<char>(std::tolower(c));
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum sample_method_t get_sdapi_sample_method(std::string name) {
|
static enum sample_method_t get_sdapi_sample_method(std::string name) {
|
||||||
enum sample_method_t result = str_to_sample_method(name.c_str());
|
enum sample_method_t result = str_to_sample_method(name.c_str());
|
||||||
if (result != SAMPLE_METHOD_COUNT) {
|
if (result != SAMPLE_METHOD_COUNT) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = lower_ascii(name);
|
std::transform(name.begin(), name.end(), name.begin(),
|
||||||
|
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||||
static const std::unordered_map<std::string_view, sample_method_t> hardcoded{
|
static const std::unordered_map<std::string_view, sample_method_t> hardcoded{
|
||||||
{"euler a", EULER_A_SAMPLE_METHOD},
|
{"euler a", EULER_A_SAMPLE_METHOD},
|
||||||
{"k_euler_a", EULER_A_SAMPLE_METHOD},
|
{"k_euler_a", EULER_A_SAMPLE_METHOD},
|
||||||
@ -67,10 +60,6 @@ static enum sample_method_t get_sdapi_sample_method(std::string name) {
|
|||||||
{"k_res_multistep", RES_MULTISTEP_SAMPLE_METHOD},
|
{"k_res_multistep", RES_MULTISTEP_SAMPLE_METHOD},
|
||||||
{"res 2s", RES_2S_SAMPLE_METHOD},
|
{"res 2s", RES_2S_SAMPLE_METHOD},
|
||||||
{"k_res_2s", RES_2S_SAMPLE_METHOD},
|
{"k_res_2s", RES_2S_SAMPLE_METHOD},
|
||||||
{"euler_cfg_pp", EULER_CFG_PP_SAMPLE_METHOD},
|
|
||||||
{"k_euler_cfg_pp", EULER_CFG_PP_SAMPLE_METHOD},
|
|
||||||
{"euler_a_cfg_pp", EULER_CFG_PP_SAMPLE_METHOD},
|
|
||||||
{"k_euler_a_cfg_pp", EULER_CFG_PP_SAMPLE_METHOD},
|
|
||||||
};
|
};
|
||||||
auto it = hardcoded.find(name);
|
auto it = hardcoded.find(name);
|
||||||
return it != hardcoded.end() ? it->second : SAMPLE_METHOD_COUNT;
|
return it != hardcoded.end() ? it->second : SAMPLE_METHOD_COUNT;
|
||||||
@ -125,18 +114,6 @@ static bool build_sdapi_img_gen_request(const json& j,
|
|||||||
request.gen_params.width = j.value("width", -1);
|
request.gen_params.width = j.value("width", -1);
|
||||||
request.gen_params.height = j.value("height", -1);
|
request.gen_params.height = j.value("height", -1);
|
||||||
|
|
||||||
if (!img2img && j.value("enable_hr", false)) {
|
|
||||||
request.gen_params.hires_enabled = true;
|
|
||||||
request.gen_params.hires_scale = j.value("hr_scale", request.gen_params.hires_scale);
|
|
||||||
request.gen_params.hires_width = j.value("hr_resize_x", request.gen_params.hires_width);
|
|
||||||
request.gen_params.hires_height = j.value("hr_resize_y", request.gen_params.hires_height);
|
|
||||||
request.gen_params.hires_steps = j.value("hr_steps", request.gen_params.hires_steps);
|
|
||||||
request.gen_params.hires_denoising_strength =
|
|
||||||
j.value("denoising_strength", request.gen_params.hires_denoising_strength);
|
|
||||||
|
|
||||||
request.gen_params.hires_upscaler = j.value("hr_upscaler", request.gen_params.hires_upscaler);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(request.gen_params.prompt);
|
std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(request.gen_params.prompt);
|
||||||
if (!sd_cpp_extra_args_str.empty() && !request.gen_params.from_json_str(sd_cpp_extra_args_str)) {
|
if (!sd_cpp_extra_args_str.empty() && !request.gen_params.from_json_str(sd_cpp_extra_args_str)) {
|
||||||
error_message = "invalid sd_cpp_extra_args";
|
error_message = "invalid sd_cpp_extra_args";
|
||||||
@ -251,7 +228,7 @@ static bool build_sdapi_img_gen_request(const json& j,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
||||||
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", runtime.ctx_params->hires_upscalers_dir, true)) {
|
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", true)) {
|
||||||
error_message = "invalid params";
|
error_message = "invalid params";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -269,11 +246,6 @@ void register_sdapi_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
res.set_content(R"({"error":"empty body"})", "application/json");
|
res.set_content(R"({"error":"empty body"})", "application/json");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!runtime_supports_generation_mode(*runtime, IMG_GEN)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", unsupported_generation_mode_error(IMG_GEN)}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
json j = json::parse(req.body);
|
json j = json::parse(req.body);
|
||||||
ImgGenJobRequest request;
|
ImgGenJobRequest request;
|
||||||
@ -370,52 +342,6 @@ void register_sdapi_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
res.set_content(result.dump(), "application/json");
|
res.set_content(result.dump(), "application/json");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get("/sdapi/v1/upscalers", [runtime](const httplib::Request&, httplib::Response& res) {
|
|
||||||
refresh_upscaler_cache(*runtime);
|
|
||||||
|
|
||||||
auto make_builtin = [](const char* name) {
|
|
||||||
json item;
|
|
||||||
item["name"] = name;
|
|
||||||
item["model_name"] = nullptr;
|
|
||||||
item["model_path"] = nullptr;
|
|
||||||
item["model_url"] = nullptr;
|
|
||||||
item["scale"] = 4;
|
|
||||||
return item;
|
|
||||||
};
|
|
||||||
|
|
||||||
json result = json::array();
|
|
||||||
result.push_back(make_builtin("None"));
|
|
||||||
result.push_back(make_builtin("Lanczos"));
|
|
||||||
result.push_back(make_builtin("Nearest"));
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(*runtime->upscaler_mutex);
|
|
||||||
for (const auto& e : *runtime->upscaler_cache) {
|
|
||||||
json item;
|
|
||||||
item["name"] = e.name;
|
|
||||||
item["model_name"] = e.model_name;
|
|
||||||
item["model_path"] = e.fullpath;
|
|
||||||
item["model_url"] = nullptr;
|
|
||||||
item["scale"] = e.scale;
|
|
||||||
result.push_back(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set_content(result.dump(), "application/json");
|
|
||||||
});
|
|
||||||
|
|
||||||
svr.Get("/sdapi/v1/latent-upscale-modes", [](const httplib::Request&, httplib::Response& res) {
|
|
||||||
json result = json::array({
|
|
||||||
{{"name", "Latent"}},
|
|
||||||
{{"name", "Latent (nearest)"}},
|
|
||||||
{{"name", "Latent (nearest-exact)"}},
|
|
||||||
{{"name", "Latent (antialiased)"}},
|
|
||||||
{{"name", "Latent (bicubic)"}},
|
|
||||||
{{"name", "Latent (bicubic antialiased)"}},
|
|
||||||
});
|
|
||||||
res.set_content(result.dump(), "application/json");
|
|
||||||
});
|
|
||||||
|
|
||||||
svr.Get("/sdapi/v1/samplers", [runtime](const httplib::Request&, httplib::Response& res) {
|
svr.Get("/sdapi/v1/samplers", [runtime](const httplib::Request&, httplib::Response& res) {
|
||||||
std::vector<std::string> sampler_names;
|
std::vector<std::string> sampler_names;
|
||||||
sampler_names.push_back("default");
|
sampler_names.push_back("default");
|
||||||
|
|||||||
@ -56,13 +56,11 @@ static const char* capability_sample_method_name(enum sample_method_t sample_met
|
|||||||
static json make_vae_tiling_json(const sd_tiling_params_t& params) {
|
static json make_vae_tiling_json(const sd_tiling_params_t& params) {
|
||||||
return {
|
return {
|
||||||
{"enabled", params.enabled},
|
{"enabled", params.enabled},
|
||||||
{"temporal_tiling", params.temporal_tiling},
|
|
||||||
{"tile_size_x", params.tile_size_x},
|
{"tile_size_x", params.tile_size_x},
|
||||||
{"tile_size_y", params.tile_size_y},
|
{"tile_size_y", params.tile_size_y},
|
||||||
{"target_overlap", params.target_overlap},
|
{"target_overlap", params.target_overlap},
|
||||||
{"rel_size_x", params.rel_size_x},
|
{"rel_size_x", params.rel_size_x},
|
||||||
{"rel_size_y", params.rel_size_y},
|
{"rel_size_y", params.rel_size_y},
|
||||||
{"extra_tiling_args", params.extra_tiling_args ? params.extra_tiling_args : ""},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,9 +75,61 @@ static fs::path resolve_display_model_path(const ServerRuntime& runtime) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static json make_sample_params_json(const sd_sample_params_t& sample_params, const std::vector<int>& skip_layers) {
|
static json make_capabilities_json(ServerRuntime& runtime) {
|
||||||
|
refresh_lora_cache(runtime);
|
||||||
|
|
||||||
|
AsyncJobManager& manager = *runtime.async_job_manager;
|
||||||
|
const auto& defaults = *runtime.default_gen_params;
|
||||||
|
const auto& sample_params = defaults.sample_params;
|
||||||
const auto& guidance = sample_params.guidance;
|
const auto& guidance = sample_params.guidance;
|
||||||
return {
|
const fs::path model_path = resolve_display_model_path(runtime);
|
||||||
|
json samplers = json::array();
|
||||||
|
json schedulers = json::array();
|
||||||
|
json output_formats = json::array({"png", "jpeg"});
|
||||||
|
json available_loras = json::array();
|
||||||
|
|
||||||
|
for (int i = 0; i < SAMPLE_METHOD_COUNT; ++i) {
|
||||||
|
samplers.push_back(sd_sample_method_name((sample_method_t)i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < SCHEDULER_COUNT; ++i) {
|
||||||
|
schedulers.push_back(sd_scheduler_name((scheduler_t)i));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SD_USE_WEBP
|
||||||
|
output_formats.push_back("webp");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(*runtime.lora_mutex);
|
||||||
|
for (const auto& entry : *runtime.lora_cache) {
|
||||||
|
available_loras.push_back({
|
||||||
|
{"name", entry.name},
|
||||||
|
{"path", entry.path},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json result;
|
||||||
|
result["model"] = {
|
||||||
|
{"name", model_path.filename().u8string()},
|
||||||
|
{"stem", model_path.stem().u8string()},
|
||||||
|
{"path", model_path.u8string()},
|
||||||
|
};
|
||||||
|
result["defaults"] = {
|
||||||
|
{"prompt", defaults.prompt},
|
||||||
|
{"negative_prompt", defaults.negative_prompt},
|
||||||
|
{"clip_skip", defaults.clip_skip},
|
||||||
|
{"width", defaults.width > 0 ? defaults.width : 512},
|
||||||
|
{"height", defaults.height > 0 ? defaults.height : 512},
|
||||||
|
{"strength", defaults.strength},
|
||||||
|
{"seed", defaults.seed},
|
||||||
|
{"batch_count", defaults.batch_count},
|
||||||
|
{"auto_resize_ref_image", defaults.auto_resize_ref_image},
|
||||||
|
{"increase_ref_index", defaults.increase_ref_index},
|
||||||
|
{"control_strength", defaults.control_strength},
|
||||||
|
{"sample_params",
|
||||||
|
{
|
||||||
{"scheduler", capability_scheduler_name(sample_params.scheduler)},
|
{"scheduler", capability_scheduler_name(sample_params.scheduler)},
|
||||||
{"sample_method", capability_sample_method_name(sample_params.sample_method)},
|
{"sample_method", capability_sample_method_name(sample_params.sample_method)},
|
||||||
{"sample_steps", sample_params.sample_steps},
|
{"sample_steps", sample_params.sample_steps},
|
||||||
@ -93,239 +143,21 @@ static json make_sample_params_json(const sd_sample_params_t& sample_params, con
|
|||||||
{"distilled_guidance", guidance.distilled_guidance},
|
{"distilled_guidance", guidance.distilled_guidance},
|
||||||
{"slg",
|
{"slg",
|
||||||
{
|
{
|
||||||
{"layers", skip_layers},
|
{"layers", defaults.skip_layers},
|
||||||
{"layer_start", guidance.slg.layer_start},
|
{"layer_start", guidance.slg.layer_start},
|
||||||
{"layer_end", guidance.slg.layer_end},
|
{"layer_end", guidance.slg.layer_end},
|
||||||
{"scale", guidance.slg.scale},
|
{"scale", guidance.slg.scale},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
};
|
}},
|
||||||
}
|
|
||||||
|
|
||||||
static json make_hires_json(const SDGenerationParams& defaults) {
|
|
||||||
return {
|
|
||||||
{"enabled", defaults.hires_enabled},
|
|
||||||
{"upscaler", defaults.hires_upscaler},
|
|
||||||
{"scale", defaults.hires_scale},
|
|
||||||
{"target_width", defaults.hires_width},
|
|
||||||
{"target_height", defaults.hires_height},
|
|
||||||
{"steps", defaults.hires_steps},
|
|
||||||
{"denoising_strength", defaults.hires_denoising_strength},
|
|
||||||
{"custom_sigmas", defaults.hires_custom_sigmas},
|
|
||||||
{"upscale_tile_size", defaults.hires_upscale_tile_size},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static json make_img_gen_defaults_json(const SDGenerationParams& defaults, const std::string& output_format) {
|
|
||||||
return {
|
|
||||||
{"prompt", defaults.prompt},
|
|
||||||
{"negative_prompt", defaults.negative_prompt},
|
|
||||||
{"clip_skip", defaults.clip_skip},
|
|
||||||
{"width", defaults.width > 0 ? defaults.width : 512},
|
|
||||||
{"height", defaults.height > 0 ? defaults.height : 512},
|
|
||||||
{"strength", defaults.strength},
|
|
||||||
{"seed", defaults.seed},
|
|
||||||
{"batch_count", defaults.batch_count},
|
|
||||||
{"auto_resize_ref_image", defaults.auto_resize_ref_image},
|
|
||||||
{"increase_ref_index", defaults.increase_ref_index},
|
|
||||||
{"control_strength", defaults.control_strength},
|
|
||||||
{"sample_params", make_sample_params_json(defaults.sample_params, defaults.skip_layers)},
|
|
||||||
{"hires", make_hires_json(defaults)},
|
|
||||||
{"vae_tiling_params", make_vae_tiling_json(defaults.vae_tiling_params)},
|
{"vae_tiling_params", make_vae_tiling_json(defaults.vae_tiling_params)},
|
||||||
{"cache_mode", defaults.cache_mode},
|
{"cache_mode", defaults.cache_mode},
|
||||||
{"cache_option", defaults.cache_option},
|
{"cache_option", defaults.cache_option},
|
||||||
{"scm_mask", defaults.scm_mask},
|
{"scm_mask", defaults.scm_mask},
|
||||||
{"scm_policy_dynamic", defaults.scm_policy_dynamic},
|
{"scm_policy_dynamic", defaults.scm_policy_dynamic},
|
||||||
{"output_format", output_format},
|
{"output_format", "png"},
|
||||||
{"output_compression", 100},
|
{"output_compression", 100},
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
static json make_vid_gen_defaults_json(const SDGenerationParams& defaults, const std::string& output_format) {
|
|
||||||
return {
|
|
||||||
{"prompt", defaults.prompt},
|
|
||||||
{"negative_prompt", defaults.negative_prompt},
|
|
||||||
{"clip_skip", defaults.clip_skip},
|
|
||||||
{"width", defaults.width > 0 ? defaults.width : 512},
|
|
||||||
{"height", defaults.height > 0 ? defaults.height : 512},
|
|
||||||
{"strength", defaults.strength},
|
|
||||||
{"seed", defaults.seed},
|
|
||||||
{"video_frames", defaults.video_frames},
|
|
||||||
{"fps", defaults.fps},
|
|
||||||
{"moe_boundary", defaults.moe_boundary},
|
|
||||||
{"vace_strength", defaults.vace_strength},
|
|
||||||
{"sample_params", make_sample_params_json(defaults.sample_params, defaults.skip_layers)},
|
|
||||||
{"high_noise_sample_params", make_sample_params_json(defaults.high_noise_sample_params, defaults.high_noise_skip_layers)},
|
|
||||||
{"hires", make_hires_json(defaults)},
|
|
||||||
{"vae_tiling_params", make_vae_tiling_json(defaults.vae_tiling_params)},
|
|
||||||
{"cache_mode", defaults.cache_mode},
|
|
||||||
{"cache_option", defaults.cache_option},
|
|
||||||
{"scm_mask", defaults.scm_mask},
|
|
||||||
{"scm_policy_dynamic", defaults.scm_policy_dynamic},
|
|
||||||
{"output_format", output_format},
|
|
||||||
{"output_compression", 100},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static json make_img_gen_features_json() {
|
|
||||||
return {
|
|
||||||
{"init_image", true},
|
|
||||||
{"mask_image", true},
|
|
||||||
{"control_image", true},
|
|
||||||
{"ref_images", true},
|
|
||||||
{"lora", true},
|
|
||||||
{"vae_tiling", true},
|
|
||||||
{"hires", true},
|
|
||||||
{"cache", true},
|
|
||||||
{"cancel_queued", true},
|
|
||||||
{"cancel_generating", false},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static json make_vid_gen_features_json() {
|
|
||||||
return {
|
|
||||||
{"init_image", true},
|
|
||||||
{"end_image", true},
|
|
||||||
{"control_frames", true},
|
|
||||||
{"high_noise_sample_params", true},
|
|
||||||
{"lora", true},
|
|
||||||
{"vae_tiling", true},
|
|
||||||
{"cache", true},
|
|
||||||
{"cancel_queued", true},
|
|
||||||
{"cancel_generating", false},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static json make_capabilities_json(ServerRuntime& runtime) {
|
|
||||||
refresh_lora_cache(runtime);
|
|
||||||
refresh_upscaler_cache(runtime);
|
|
||||||
|
|
||||||
AsyncJobManager& manager = *runtime.async_job_manager;
|
|
||||||
const auto& defaults = *runtime.default_gen_params;
|
|
||||||
const fs::path model_path = resolve_display_model_path(runtime);
|
|
||||||
const bool supports_img = runtime_supports_generation_mode(runtime, IMG_GEN);
|
|
||||||
const bool supports_vid = runtime_supports_generation_mode(runtime, VID_GEN);
|
|
||||||
json samplers = json::array();
|
|
||||||
json schedulers = json::array();
|
|
||||||
json image_output_formats = supported_img_output_formats();
|
|
||||||
json video_output_formats = supported_vid_output_formats();
|
|
||||||
json available_loras = json::array();
|
|
||||||
json available_upscalers = json::array();
|
|
||||||
json supported_modes = json::array();
|
|
||||||
|
|
||||||
for (int i = 0; i < SAMPLE_METHOD_COUNT; ++i) {
|
|
||||||
samplers.push_back(sd_sample_method_name((sample_method_t)i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < SCHEDULER_COUNT; ++i) {
|
|
||||||
schedulers.push_back(sd_scheduler_name((scheduler_t)i));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(*runtime.lora_mutex);
|
|
||||||
for (const auto& entry : *runtime.lora_cache) {
|
|
||||||
available_loras.push_back({
|
|
||||||
{"name", entry.name},
|
|
||||||
{"path", entry.path},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "None"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Lanczos"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Nearest"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent (nearest)"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent (nearest-exact)"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent (antialiased)"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent (bicubic)"},
|
|
||||||
});
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", "Latent (bicubic antialiased)"},
|
|
||||||
});
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(*runtime.upscaler_mutex);
|
|
||||||
for (const auto& entry : *runtime.upscaler_cache) {
|
|
||||||
available_upscalers.push_back({
|
|
||||||
{"name", entry.name},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (supports_img) {
|
|
||||||
supported_modes.push_back("img_gen");
|
|
||||||
}
|
|
||||||
if (supports_vid) {
|
|
||||||
supported_modes.push_back("vid_gen");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string default_img_output_format = "png";
|
|
||||||
std::string default_vid_output_format = "avi";
|
|
||||||
if (!image_output_formats.empty()) {
|
|
||||||
default_img_output_format = image_output_formats[0].get<std::string>();
|
|
||||||
}
|
|
||||||
if (!video_output_formats.empty()) {
|
|
||||||
default_vid_output_format = video_output_formats[0].get<std::string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
json defaults_by_mode = json::object();
|
|
||||||
json output_formats_by_mode = json::object();
|
|
||||||
json features_by_mode = json::object();
|
|
||||||
if (supports_img) {
|
|
||||||
defaults_by_mode["img_gen"] = make_img_gen_defaults_json(defaults, default_img_output_format);
|
|
||||||
output_formats_by_mode["img_gen"] = image_output_formats;
|
|
||||||
features_by_mode["img_gen"] = make_img_gen_features_json();
|
|
||||||
}
|
|
||||||
if (supports_vid) {
|
|
||||||
defaults_by_mode["vid_gen"] = make_vid_gen_defaults_json(defaults, default_vid_output_format);
|
|
||||||
output_formats_by_mode["vid_gen"] = video_output_formats;
|
|
||||||
features_by_mode["vid_gen"] = make_vid_gen_features_json();
|
|
||||||
}
|
|
||||||
|
|
||||||
json top_level_defaults = json::object();
|
|
||||||
json top_level_output_formats = json::array();
|
|
||||||
json top_level_features = {
|
|
||||||
{"cancel_queued", true},
|
|
||||||
{"cancel_generating", false},
|
|
||||||
};
|
|
||||||
std::string current_mode = "";
|
|
||||||
if (supports_img) {
|
|
||||||
current_mode = "img_gen";
|
|
||||||
top_level_defaults = defaults_by_mode["img_gen"];
|
|
||||||
top_level_output_formats = output_formats_by_mode["img_gen"];
|
|
||||||
top_level_features = features_by_mode["img_gen"];
|
|
||||||
} else if (supports_vid) {
|
|
||||||
current_mode = "vid_gen";
|
|
||||||
top_level_defaults = defaults_by_mode["vid_gen"];
|
|
||||||
top_level_output_formats = output_formats_by_mode["vid_gen"];
|
|
||||||
top_level_features = features_by_mode["vid_gen"];
|
|
||||||
}
|
|
||||||
|
|
||||||
json result;
|
|
||||||
result["model"] = {
|
|
||||||
{"name", model_path.filename().u8string()},
|
|
||||||
{"stem", model_path.stem().u8string()},
|
|
||||||
{"path", model_path.u8string()},
|
|
||||||
};
|
|
||||||
result["current_mode"] = current_mode;
|
|
||||||
result["supported_modes"] = supported_modes;
|
|
||||||
result["defaults"] = top_level_defaults;
|
|
||||||
result["defaults_by_mode"] = defaults_by_mode;
|
|
||||||
result["limits"] = {
|
result["limits"] = {
|
||||||
{"min_width", 64},
|
{"min_width", 64},
|
||||||
{"max_width", 4096},
|
{"max_width", 4096},
|
||||||
@ -336,12 +168,19 @@ static json make_capabilities_json(ServerRuntime& runtime) {
|
|||||||
};
|
};
|
||||||
result["samplers"] = samplers;
|
result["samplers"] = samplers;
|
||||||
result["schedulers"] = schedulers;
|
result["schedulers"] = schedulers;
|
||||||
result["output_formats"] = top_level_output_formats;
|
result["output_formats"] = output_formats;
|
||||||
result["output_formats_by_mode"] = output_formats_by_mode;
|
result["features"] = {
|
||||||
result["features"] = top_level_features;
|
{"init_image", true},
|
||||||
result["features_by_mode"] = features_by_mode;
|
{"mask_image", true},
|
||||||
|
{"control_image", true},
|
||||||
|
{"ref_images", true},
|
||||||
|
{"lora", true},
|
||||||
|
{"vae_tiling", true},
|
||||||
|
{"cache", true},
|
||||||
|
{"cancel_queued", true},
|
||||||
|
{"cancel_generating", false},
|
||||||
|
};
|
||||||
result["loras"] = available_loras;
|
result["loras"] = available_loras;
|
||||||
result["upscalers"] = available_upscalers;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,34 +204,7 @@ static bool parse_img_gen_request(const json& body,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
||||||
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", runtime.ctx_params->hires_upscalers_dir, true)) {
|
if (!request.gen_params.resolve_and_validate(IMG_GEN, "", true)) {
|
||||||
error_message = "invalid generation parameters";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool parse_vid_gen_request(const json& body,
|
|
||||||
ServerRuntime& runtime,
|
|
||||||
VidGenJobRequest& request,
|
|
||||||
std::string& error_message) {
|
|
||||||
request.gen_params = *runtime.default_gen_params;
|
|
||||||
|
|
||||||
refresh_lora_cache(runtime);
|
|
||||||
if (!request.gen_params.from_json_str(body.dump(), [&](const std::string& path) {
|
|
||||||
return get_lora_full_path(runtime, path);
|
|
||||||
})) {
|
|
||||||
error_message = "invalid generation parameters";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string output_format = body.value("output_format", "webm");
|
|
||||||
int output_compression = body.value("output_compression", 100);
|
|
||||||
if (!assign_output_options(request, output_format, output_compression, error_message)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Intentionally disable prompt-embedded LoRA tag parsing for server APIs.
|
|
||||||
if (!request.gen_params.resolve_and_validate(VID_GEN, "", runtime.ctx_params->hires_upscalers_dir, true)) {
|
|
||||||
error_message = "invalid generation parameters";
|
error_message = "invalid generation parameters";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -414,11 +226,6 @@ void register_sdcpp_api_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
res.set_content(R"({"error":"empty body"})", "application/json");
|
res.set_content(R"({"error":"empty body"})", "application/json");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!runtime_supports_generation_mode(*runtime, IMG_GEN)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", unsupported_generation_mode_error(IMG_GEN)}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
json body = json::parse(req.body);
|
json body = json::parse(req.body);
|
||||||
ImgGenJobRequest request;
|
ImgGenJobRequest request;
|
||||||
@ -469,66 +276,9 @@ void register_sdcpp_api_endpoints(httplib::Server& svr, ServerRuntime& rt) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Post("/sdcpp/v1/vid_gen", [runtime](const httplib::Request& req, httplib::Response& res) {
|
svr.Post("/sdcpp/v1/vid_gen", [](const httplib::Request&, httplib::Response& res) {
|
||||||
try {
|
res.status = 501;
|
||||||
if (req.body.empty()) {
|
res.set_content(R"({"error":"vid_gen is reserved and not implemented yet"})", "application/json");
|
||||||
res.status = 400;
|
|
||||||
res.set_content(R"({"error":"empty body"})", "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!runtime_supports_generation_mode(*runtime, VID_GEN)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", unsupported_generation_mode_error(VID_GEN)}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
json body = json::parse(req.body);
|
|
||||||
VidGenJobRequest request;
|
|
||||||
std::string error_message;
|
|
||||||
if (!parse_vid_gen_request(body, *runtime, request, error_message)) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", error_message}}).dump(), "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncJobManager& manager = *runtime->async_job_manager;
|
|
||||||
std::shared_ptr<AsyncGenerationJob> job = std::make_shared<AsyncGenerationJob>();
|
|
||||||
job->kind = AsyncJobKind::VidGen;
|
|
||||||
job->status = AsyncJobStatus::Queued;
|
|
||||||
job->created_at = unix_timestamp_now();
|
|
||||||
job->vid_gen = std::move(request);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(manager.mutex);
|
|
||||||
purge_expired_jobs(manager);
|
|
||||||
if (count_pending_jobs(manager) >= manager.max_pending_jobs) {
|
|
||||||
res.status = 429;
|
|
||||||
res.set_content(R"({"error":"job queue is full"})", "application/json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
job->id = make_async_job_id(manager);
|
|
||||||
manager.jobs[job->id] = job;
|
|
||||||
manager.queue.push_back(job->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.cv.notify_one();
|
|
||||||
|
|
||||||
json out;
|
|
||||||
out["id"] = job->id;
|
|
||||||
out["kind"] = async_job_kind_name(job->kind);
|
|
||||||
out["status"] = async_job_status_name(job->status);
|
|
||||||
out["created"] = job->created_at;
|
|
||||||
out["poll_url"] = "/sdcpp/v1/jobs/" + job->id;
|
|
||||||
|
|
||||||
res.status = 202;
|
|
||||||
res.set_content(out.dump(), "application/json");
|
|
||||||
} catch (const json::parse_error& e) {
|
|
||||||
res.status = 400;
|
|
||||||
res.set_content(json({{"error", "invalid json"}, {"message", e.what()}}).dump(), "application/json");
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
res.status = 500;
|
|
||||||
res.set_content(json({{"error", "server_error"}, {"message", e.what()}}).dump(), "application/json");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.Get(R"(/sdcpp/v1/jobs/([A-Za-z0-9_\-]+))", [runtime](const httplib::Request& req, httplib::Response& res) {
|
svr.Get(R"(/sdcpp/v1/jobs/([A-Za-z0-9_\-]+))", [runtime](const httplib::Request& req, httplib::Response& res) {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -14,18 +13,6 @@
|
|||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
static std::string lower_ascii(std::string value) {
|
|
||||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
|
|
||||||
return static_cast<char>(std::tolower(c));
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_supported_model_ext(const fs::path& p) {
|
|
||||||
auto ext = lower_ascii(p.extension().string());
|
|
||||||
return ext == ".gguf" || ext == ".pt" || ext == ".pth" || ext == ".safetensors";
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::string k_base64_chars =
|
static const std::string k_base64_chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
@ -58,44 +45,6 @@ std::string normalize_output_format(std::string output_format) {
|
|||||||
return output_format;
|
return output_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> supported_img_output_formats(bool allow_webp) {
|
|
||||||
std::vector<std::string> formats = {"png", "jpeg"};
|
|
||||||
#ifdef SD_USE_WEBP
|
|
||||||
if (allow_webp) {
|
|
||||||
formats.push_back("webp");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
(void)allow_webp;
|
|
||||||
#endif
|
|
||||||
return formats;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> supported_vid_output_formats() {
|
|
||||||
std::vector<std::string> formats;
|
|
||||||
#ifdef SD_USE_WEBM
|
|
||||||
formats.push_back("webm");
|
|
||||||
#endif
|
|
||||||
#ifdef SD_USE_WEBP
|
|
||||||
formats.push_back("webp");
|
|
||||||
#endif
|
|
||||||
formats.push_back("avi");
|
|
||||||
return formats;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string valid_vid_output_formats_message() {
|
|
||||||
const std::vector<std::string> formats = supported_vid_output_formats();
|
|
||||||
|
|
||||||
std::string message = "invalid output_format, must be one of [";
|
|
||||||
for (size_t i = 0; i < formats.size(); ++i) {
|
|
||||||
if (i > 0) {
|
|
||||||
message += ", ";
|
|
||||||
}
|
|
||||||
message += formats[i];
|
|
||||||
}
|
|
||||||
message += "]";
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool assign_output_options(ImgGenJobRequest& request,
|
bool assign_output_options(ImgGenJobRequest& request,
|
||||||
std::string output_format,
|
std::string output_format,
|
||||||
int output_compression,
|
int output_compression,
|
||||||
@ -104,88 +53,19 @@ bool assign_output_options(ImgGenJobRequest& request,
|
|||||||
request.output_format = normalize_output_format(std::move(output_format));
|
request.output_format = normalize_output_format(std::move(output_format));
|
||||||
request.output_compression = std::clamp(output_compression, 0, 100);
|
request.output_compression = std::clamp(output_compression, 0, 100);
|
||||||
|
|
||||||
const std::vector<std::string> valid_formats = supported_img_output_formats(allow_webp);
|
const bool valid_format = request.output_format == "png" ||
|
||||||
const bool valid_format = std::find(valid_formats.begin(),
|
request.output_format == "jpeg" ||
|
||||||
valid_formats.end(),
|
(allow_webp && request.output_format == "webp");
|
||||||
request.output_format) != valid_formats.end();
|
|
||||||
if (!valid_format) {
|
if (!valid_format) {
|
||||||
error_message = "invalid output_format, must be one of [";
|
error_message = allow_webp
|
||||||
for (size_t i = 0; i < valid_formats.size(); ++i) {
|
? "invalid output_format, must be one of [png, jpeg, webp]"
|
||||||
if (i > 0) {
|
: "invalid output_format, must be one of [png, jpeg]";
|
||||||
error_message += ", ";
|
|
||||||
}
|
|
||||||
error_message += valid_formats[i];
|
|
||||||
}
|
|
||||||
error_message += "]";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool assign_output_options(VidGenJobRequest& request,
|
|
||||||
std::string output_format,
|
|
||||||
int output_compression,
|
|
||||||
std::string& error_message) {
|
|
||||||
request.output_format = normalize_output_format(std::move(output_format));
|
|
||||||
request.output_compression = std::clamp(output_compression, 0, 100);
|
|
||||||
|
|
||||||
if (request.output_format == "avi") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.output_format == "webm") {
|
|
||||||
#ifdef SD_USE_WEBM
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
error_message = valid_vid_output_formats_message();
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.output_format == "webp") {
|
|
||||||
#ifdef SD_USE_WEBP
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
error_message = valid_vid_output_formats_message();
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
error_message = valid_vid_output_formats_message();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string video_mime_type(const std::string& output_format) {
|
|
||||||
if (output_format == "webm") {
|
|
||||||
return "video/webm";
|
|
||||||
}
|
|
||||||
if (output_format == "webp") {
|
|
||||||
return "image/webp";
|
|
||||||
}
|
|
||||||
return "video/x-msvideo";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool runtime_supports_generation_mode(const ServerRuntime& runtime, SDMode mode) {
|
|
||||||
if (mode == VID_GEN) {
|
|
||||||
return sd_ctx_supports_video_generation(runtime.sd_ctx);
|
|
||||||
}
|
|
||||||
if (mode == IMG_GEN) {
|
|
||||||
return sd_ctx_supports_image_generation(runtime.sd_ctx);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string unsupported_generation_mode_error(SDMode mode) {
|
|
||||||
if (mode == VID_GEN) {
|
|
||||||
return "loaded model does not support vid_gen";
|
|
||||||
}
|
|
||||||
if (mode == IMG_GEN) {
|
|
||||||
return "loaded model does not support img_gen";
|
|
||||||
}
|
|
||||||
return "loaded model does not support requested mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
ArgOptions SDSvrParams::get_options() {
|
ArgOptions SDSvrParams::get_options() {
|
||||||
ArgOptions options;
|
ArgOptions options;
|
||||||
|
|
||||||
@ -203,9 +83,8 @@ ArgOptions SDSvrParams::get_options() {
|
|||||||
{"", "--color", "colors the logging tags according to level", true, &color},
|
{"", "--color", "colors the logging tags according to level", true, &color},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto on_help_arg = [&](int, const char**, int, bool& valid) {
|
auto on_help_arg = [&](int, const char**, int) {
|
||||||
normal_exit = true;
|
normal_exit = true;
|
||||||
valid = true;
|
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -255,12 +134,20 @@ void refresh_lora_cache(ServerRuntime& rt) {
|
|||||||
|
|
||||||
fs::path lora_dir = rt.ctx_params->lora_model_dir;
|
fs::path lora_dir = rt.ctx_params->lora_model_dir;
|
||||||
if (fs::exists(lora_dir) && fs::is_directory(lora_dir)) {
|
if (fs::exists(lora_dir) && fs::is_directory(lora_dir)) {
|
||||||
for (auto& entry : fs::recursive_directory_iterator(lora_dir, fs::directory_options::skip_permission_denied)) {
|
auto is_lora_ext = [](const fs::path& p) {
|
||||||
|
auto ext = p.extension().string();
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) {
|
||||||
|
return static_cast<char>(std::tolower(c));
|
||||||
|
});
|
||||||
|
return ext == ".gguf" || ext == ".pt" || ext == ".pth" || ext == ".safetensors";
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& entry : fs::recursive_directory_iterator(lora_dir)) {
|
||||||
if (!entry.is_regular_file()) {
|
if (!entry.is_regular_file()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const fs::path& p = entry.path();
|
const fs::path& p = entry.path();
|
||||||
if (!is_supported_model_ext(p)) {
|
if (!is_lora_ext(p)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,40 +179,6 @@ std::string get_lora_full_path(ServerRuntime& rt, const std::string& path) {
|
|||||||
return it != rt.lora_cache->end() ? it->fullpath : "";
|
return it != rt.lora_cache->end() ? it->fullpath : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void refresh_upscaler_cache(ServerRuntime& rt) {
|
|
||||||
std::vector<UpscalerEntry> new_cache;
|
|
||||||
|
|
||||||
fs::path upscaler_dir = rt.ctx_params->hires_upscalers_dir;
|
|
||||||
if (fs::exists(upscaler_dir) && fs::is_directory(upscaler_dir)) {
|
|
||||||
for (auto& entry : fs::directory_iterator(upscaler_dir)) {
|
|
||||||
if (!entry.is_regular_file()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const fs::path& p = entry.path();
|
|
||||||
if (!is_supported_model_ext(p)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpscalerEntry upscaler_entry;
|
|
||||||
upscaler_entry.name = p.stem().u8string();
|
|
||||||
upscaler_entry.fullpath = fs::absolute(p).lexically_normal().u8string();
|
|
||||||
upscaler_entry.model_name = "ESRGAN_4x";
|
|
||||||
upscaler_entry.path = p.filename().u8string();
|
|
||||||
|
|
||||||
new_cache.push_back(std::move(upscaler_entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(new_cache.begin(), new_cache.end(), [](const UpscalerEntry& a, const UpscalerEntry& b) {
|
|
||||||
return a.name < b.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(*rt.upscaler_mutex);
|
|
||||||
*rt.upscaler_cache = std::move(new_cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t unix_timestamp_now() {
|
int64_t unix_timestamp_now() {
|
||||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
|||||||
@ -37,14 +37,6 @@ struct LoraEntry {
|
|||||||
std::string fullpath;
|
std::string fullpath;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UpscalerEntry {
|
|
||||||
std::string name;
|
|
||||||
std::string path;
|
|
||||||
std::string fullpath;
|
|
||||||
std::string model_name;
|
|
||||||
int scale = 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ServerRuntime {
|
struct ServerRuntime {
|
||||||
sd_ctx_t* sd_ctx;
|
sd_ctx_t* sd_ctx;
|
||||||
std::mutex* sd_ctx_mutex;
|
std::mutex* sd_ctx_mutex;
|
||||||
@ -53,8 +45,6 @@ struct ServerRuntime {
|
|||||||
const SDGenerationParams* default_gen_params;
|
const SDGenerationParams* default_gen_params;
|
||||||
std::vector<LoraEntry>* lora_cache;
|
std::vector<LoraEntry>* lora_cache;
|
||||||
std::mutex* lora_mutex;
|
std::mutex* lora_mutex;
|
||||||
std::vector<UpscalerEntry>* upscaler_cache;
|
|
||||||
std::mutex* upscaler_mutex;
|
|
||||||
AsyncJobManager* async_job_manager;
|
AsyncJobManager* async_job_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,33 +58,13 @@ struct ImgGenJobRequest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VidGenJobRequest {
|
|
||||||
SDGenerationParams gen_params;
|
|
||||||
std::string output_format = "webm";
|
|
||||||
int output_compression = 100;
|
|
||||||
|
|
||||||
sd_vid_gen_params_t to_sd_vid_gen_params_t() {
|
|
||||||
return gen_params.to_sd_vid_gen_params_t();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string base64_encode(const std::vector<uint8_t>& bytes);
|
std::string base64_encode(const std::vector<uint8_t>& bytes);
|
||||||
std::string normalize_output_format(std::string output_format);
|
std::string normalize_output_format(std::string output_format);
|
||||||
std::vector<std::string> supported_img_output_formats(bool allow_webp = true);
|
|
||||||
std::vector<std::string> supported_vid_output_formats();
|
|
||||||
bool assign_output_options(ImgGenJobRequest& request,
|
bool assign_output_options(ImgGenJobRequest& request,
|
||||||
std::string output_format,
|
std::string output_format,
|
||||||
int output_compression,
|
int output_compression,
|
||||||
bool allow_webp,
|
bool allow_webp,
|
||||||
std::string& error_message);
|
std::string& error_message);
|
||||||
bool assign_output_options(VidGenJobRequest& request,
|
|
||||||
std::string output_format,
|
|
||||||
int output_compression,
|
|
||||||
std::string& error_message);
|
|
||||||
std::string video_mime_type(const std::string& output_format);
|
|
||||||
bool runtime_supports_generation_mode(const ServerRuntime& runtime, SDMode mode);
|
|
||||||
std::string unsupported_generation_mode_error(SDMode mode);
|
|
||||||
void refresh_lora_cache(ServerRuntime& rt);
|
void refresh_lora_cache(ServerRuntime& rt);
|
||||||
std::string get_lora_full_path(ServerRuntime& rt, const std::string& path);
|
std::string get_lora_full_path(ServerRuntime& rt, const std::string& path);
|
||||||
void refresh_upscaler_cache(ServerRuntime& rt);
|
|
||||||
int64_t unix_timestamp_now();
|
int64_t unix_timestamp_now();
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
for f in src/*.cpp src/*.h src/*.hpp \
|
for f in src/*.cpp src/*.h src/*.hpp src/tokenizers/*.h src/tokenizers/*.cpp src/tokenizers/vocab/*.h src/tokenizers/vocab/*.cpp \
|
||||||
src/conditioning/*.cpp src/conditioning/*.h src/conditioning/*.hpp \
|
examples/cli/*.cpp examples/cli/*.h examples/server/*.cpp \
|
||||||
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; do
|
examples/common/*.hpp examples/common/*.h examples/common/*.cpp; do
|
||||||
[[ -e "$f" ]] || continue
|
|
||||||
[[ "$f" == vocab* ]] && continue
|
[[ "$f" == vocab* ]] && continue
|
||||||
echo "formatting '$f'"
|
echo "formatting '$f'"
|
||||||
# if [ "$f" != "stable-diffusion.h" ]; then
|
# if [ "$f" != "stable-diffusion.h" ]; then
|
||||||
|
|||||||
2
ggml
2
ggml
@ -1 +1 @@
|
|||||||
Subproject commit 0ce7ad348a3151e1da9f65d962044546bcaad421
|
Subproject commit 404fcb9d7c96989569e68c9e7881ee3465a05c50
|
||||||
@ -51,9 +51,6 @@ enum sample_method_t {
|
|||||||
RES_MULTISTEP_SAMPLE_METHOD,
|
RES_MULTISTEP_SAMPLE_METHOD,
|
||||||
RES_2S_SAMPLE_METHOD,
|
RES_2S_SAMPLE_METHOD,
|
||||||
ER_SDE_SAMPLE_METHOD,
|
ER_SDE_SAMPLE_METHOD,
|
||||||
EULER_CFG_PP_SAMPLE_METHOD,
|
|
||||||
EULER_A_CFG_PP_SAMPLE_METHOD,
|
|
||||||
EULER_GE_SAMPLE_METHOD,
|
|
||||||
SAMPLE_METHOD_COUNT
|
SAMPLE_METHOD_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,7 +66,6 @@ enum scheduler_t {
|
|||||||
KL_OPTIMAL_SCHEDULER,
|
KL_OPTIMAL_SCHEDULER,
|
||||||
LCM_SCHEDULER,
|
LCM_SCHEDULER,
|
||||||
BONG_TANGENT_SCHEDULER,
|
BONG_TANGENT_SCHEDULER,
|
||||||
LTX2_SCHEDULER,
|
|
||||||
SCHEDULER_COUNT
|
SCHEDULER_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,8 +122,7 @@ enum sd_type_t {
|
|||||||
// SD_TYPE_IQ4_NL_8_8 = 38,
|
// SD_TYPE_IQ4_NL_8_8 = 38,
|
||||||
SD_TYPE_MXFP4 = 39, // MXFP4 (1 block)
|
SD_TYPE_MXFP4 = 39, // MXFP4 (1 block)
|
||||||
SD_TYPE_NVFP4 = 40, // NVFP4 (4 blocks, E4M3 scale)
|
SD_TYPE_NVFP4 = 40, // NVFP4 (4 blocks, E4M3 scale)
|
||||||
SD_TYPE_Q1_0 = 41,
|
SD_TYPE_COUNT = 41,
|
||||||
SD_TYPE_COUNT = 42,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sd_log_level_t {
|
enum sd_log_level_t {
|
||||||
@ -154,13 +149,11 @@ enum lora_apply_mode_t {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool temporal_tiling;
|
|
||||||
int tile_size_x;
|
int tile_size_x;
|
||||||
int tile_size_y;
|
int tile_size_y;
|
||||||
float target_overlap;
|
float target_overlap;
|
||||||
float rel_size_x;
|
float rel_size_x;
|
||||||
float rel_size_y;
|
float rel_size_y;
|
||||||
const char* extra_tiling_args;
|
|
||||||
} sd_tiling_params_t;
|
} sd_tiling_params_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -168,14 +161,6 @@ typedef struct {
|
|||||||
const char* path;
|
const char* path;
|
||||||
} sd_embedding_t;
|
} sd_embedding_t;
|
||||||
|
|
||||||
enum sd_vae_format_t {
|
|
||||||
SD_VAE_FORMAT_AUTO = -1,
|
|
||||||
SD_VAE_FORMAT_FLUX,
|
|
||||||
SD_VAE_FORMAT_SD3,
|
|
||||||
SD_VAE_FORMAT_FLUX2,
|
|
||||||
SD_VAE_FORMAT_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* model_path;
|
const char* model_path;
|
||||||
const char* clip_l_path;
|
const char* clip_l_path;
|
||||||
@ -186,10 +171,7 @@ typedef struct {
|
|||||||
const char* llm_vision_path;
|
const char* llm_vision_path;
|
||||||
const char* diffusion_model_path;
|
const char* diffusion_model_path;
|
||||||
const char* high_noise_diffusion_model_path;
|
const char* high_noise_diffusion_model_path;
|
||||||
const char* uncond_diffusion_model_path;
|
|
||||||
const char* embeddings_connectors_path;
|
|
||||||
const char* vae_path;
|
const char* vae_path;
|
||||||
const char* audio_vae_path;
|
|
||||||
const char* taesd_path;
|
const char* taesd_path;
|
||||||
const char* control_net_path;
|
const char* control_net_path;
|
||||||
const sd_embedding_t* embeddings;
|
const sd_embedding_t* embeddings;
|
||||||
@ -221,20 +203,8 @@ typedef struct {
|
|||||||
bool chroma_use_t5_mask;
|
bool chroma_use_t5_mask;
|
||||||
int chroma_t5_mask_pad;
|
int chroma_t5_mask_pad;
|
||||||
bool qwen_image_zero_cond_t;
|
bool qwen_image_zero_cond_t;
|
||||||
enum sd_vae_format_t vae_format;
|
|
||||||
float max_vram; // GiB budget for graph-cut segmented param offload (0 = disabled, -1 = auto free VRAM minus 1 GiB)
|
|
||||||
bool stream_layers; // Enable residency+prefetch streaming on top of --max-vram (no effect without --max-vram)
|
|
||||||
const char* backend;
|
|
||||||
const char* params_backend;
|
|
||||||
} sd_ctx_params_t;
|
} sd_ctx_params_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t sample_rate;
|
|
||||||
uint32_t channels;
|
|
||||||
uint64_t sample_count;
|
|
||||||
float* data;
|
|
||||||
} sd_audio_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
@ -267,7 +237,6 @@ typedef struct {
|
|||||||
float* custom_sigmas;
|
float* custom_sigmas;
|
||||||
int custom_sigmas_count;
|
int custom_sigmas_count;
|
||||||
float flow_shift;
|
float flow_shift;
|
||||||
const char* extra_sample_args;
|
|
||||||
} sd_sample_params_t;
|
} sd_sample_params_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -320,34 +289,6 @@ typedef struct {
|
|||||||
const char* path;
|
const char* path;
|
||||||
} sd_lora_t;
|
} sd_lora_t;
|
||||||
|
|
||||||
enum sd_hires_upscaler_t {
|
|
||||||
SD_HIRES_UPSCALER_NONE,
|
|
||||||
SD_HIRES_UPSCALER_LATENT,
|
|
||||||
SD_HIRES_UPSCALER_LATENT_NEAREST,
|
|
||||||
SD_HIRES_UPSCALER_LATENT_NEAREST_EXACT,
|
|
||||||
SD_HIRES_UPSCALER_LATENT_ANTIALIASED,
|
|
||||||
SD_HIRES_UPSCALER_LATENT_BICUBIC,
|
|
||||||
SD_HIRES_UPSCALER_LATENT_BICUBIC_ANTIALIASED,
|
|
||||||
SD_HIRES_UPSCALER_LANCZOS,
|
|
||||||
SD_HIRES_UPSCALER_NEAREST,
|
|
||||||
SD_HIRES_UPSCALER_MODEL,
|
|
||||||
SD_HIRES_UPSCALER_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool enabled;
|
|
||||||
enum sd_hires_upscaler_t upscaler;
|
|
||||||
const char* model_path;
|
|
||||||
float scale;
|
|
||||||
int target_width;
|
|
||||||
int target_height;
|
|
||||||
int steps;
|
|
||||||
float denoising_strength;
|
|
||||||
int upscale_tile_size;
|
|
||||||
float* custom_sigmas;
|
|
||||||
int custom_sigmas_count;
|
|
||||||
} sd_hires_params_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const sd_lora_t* loras;
|
const sd_lora_t* loras;
|
||||||
uint32_t lora_count;
|
uint32_t lora_count;
|
||||||
@ -371,7 +312,6 @@ typedef struct {
|
|||||||
sd_pm_params_t pm_params;
|
sd_pm_params_t pm_params;
|
||||||
sd_tiling_params_t vae_tiling_params;
|
sd_tiling_params_t vae_tiling_params;
|
||||||
sd_cache_params_t cache;
|
sd_cache_params_t cache;
|
||||||
sd_hires_params_t hires;
|
|
||||||
} sd_img_gen_params_t;
|
} sd_img_gen_params_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -392,11 +332,9 @@ typedef struct {
|
|||||||
float strength;
|
float strength;
|
||||||
int64_t seed;
|
int64_t seed;
|
||||||
int video_frames;
|
int video_frames;
|
||||||
int fps;
|
|
||||||
float vace_strength;
|
float vace_strength;
|
||||||
sd_tiling_params_t vae_tiling_params;
|
sd_tiling_params_t vae_tiling_params;
|
||||||
sd_cache_params_t cache;
|
sd_cache_params_t cache;
|
||||||
sd_hires_params_t hires;
|
|
||||||
} sd_vid_gen_params_t;
|
} sd_vid_gen_params_t;
|
||||||
|
|
||||||
typedef struct sd_ctx_t sd_ctx_t;
|
typedef struct sd_ctx_t sd_ctx_t;
|
||||||
@ -410,8 +348,6 @@ SD_API void sd_set_progress_callback(sd_progress_cb_t cb, void* data);
|
|||||||
SD_API void sd_set_preview_callback(sd_preview_cb_t cb, enum preview_t mode, int interval, bool denoised, bool noisy, void* data);
|
SD_API void sd_set_preview_callback(sd_preview_cb_t cb, enum preview_t mode, int interval, bool denoised, bool noisy, void* data);
|
||||||
SD_API int32_t sd_get_num_physical_cores();
|
SD_API int32_t sd_get_num_physical_cores();
|
||||||
SD_API const char* sd_get_system_info();
|
SD_API const char* sd_get_system_info();
|
||||||
SD_API bool sd_ctx_supports_image_generation(const sd_ctx_t* sd_ctx);
|
|
||||||
SD_API bool sd_ctx_supports_video_generation(const sd_ctx_t* sd_ctx);
|
|
||||||
|
|
||||||
SD_API const char* sd_type_name(enum sd_type_t type);
|
SD_API const char* sd_type_name(enum sd_type_t type);
|
||||||
SD_API enum sd_type_t str_to_sd_type(const char* str);
|
SD_API enum sd_type_t str_to_sd_type(const char* str);
|
||||||
@ -427,18 +363,14 @@ SD_API const char* sd_preview_name(enum preview_t preview);
|
|||||||
SD_API enum preview_t str_to_preview(const char* str);
|
SD_API enum preview_t str_to_preview(const char* str);
|
||||||
SD_API const char* sd_lora_apply_mode_name(enum lora_apply_mode_t mode);
|
SD_API const char* sd_lora_apply_mode_name(enum lora_apply_mode_t mode);
|
||||||
SD_API enum lora_apply_mode_t str_to_lora_apply_mode(const char* str);
|
SD_API enum lora_apply_mode_t str_to_lora_apply_mode(const char* str);
|
||||||
SD_API const char* sd_hires_upscaler_name(enum sd_hires_upscaler_t upscaler);
|
|
||||||
SD_API enum sd_hires_upscaler_t str_to_sd_hires_upscaler(const char* str);
|
|
||||||
|
|
||||||
SD_API void sd_cache_params_init(sd_cache_params_t* cache_params);
|
SD_API void sd_cache_params_init(sd_cache_params_t* cache_params);
|
||||||
SD_API void sd_hires_params_init(sd_hires_params_t* hires_params);
|
|
||||||
|
|
||||||
SD_API void sd_ctx_params_init(sd_ctx_params_t* sd_ctx_params);
|
SD_API void sd_ctx_params_init(sd_ctx_params_t* sd_ctx_params);
|
||||||
SD_API char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params);
|
SD_API char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params);
|
||||||
|
|
||||||
SD_API sd_ctx_t* new_sd_ctx(const sd_ctx_params_t* sd_ctx_params);
|
SD_API sd_ctx_t* new_sd_ctx(const sd_ctx_params_t* sd_ctx_params);
|
||||||
SD_API void free_sd_ctx(sd_ctx_t* sd_ctx);
|
SD_API void free_sd_ctx(sd_ctx_t* sd_ctx);
|
||||||
SD_API void free_sd_audio(sd_audio_t* audio);
|
|
||||||
|
|
||||||
SD_API void sd_sample_params_init(sd_sample_params_t* sample_params);
|
SD_API void sd_sample_params_init(sd_sample_params_t* sample_params);
|
||||||
SD_API char* sd_sample_params_to_str(const sd_sample_params_t* sample_params);
|
SD_API char* sd_sample_params_to_str(const sd_sample_params_t* sample_params);
|
||||||
@ -451,11 +383,7 @@ SD_API char* sd_img_gen_params_to_str(const sd_img_gen_params_t* sd_img_gen_para
|
|||||||
SD_API sd_image_t* generate_image(sd_ctx_t* sd_ctx, const sd_img_gen_params_t* sd_img_gen_params);
|
SD_API sd_image_t* generate_image(sd_ctx_t* sd_ctx, const sd_img_gen_params_t* sd_img_gen_params);
|
||||||
|
|
||||||
SD_API void sd_vid_gen_params_init(sd_vid_gen_params_t* sd_vid_gen_params);
|
SD_API void sd_vid_gen_params_init(sd_vid_gen_params_t* sd_vid_gen_params);
|
||||||
SD_API bool generate_video(sd_ctx_t* sd_ctx,
|
SD_API sd_image_t* generate_video(sd_ctx_t* sd_ctx, const sd_vid_gen_params_t* sd_vid_gen_params, int* num_frames_out);
|
||||||
const sd_vid_gen_params_t* sd_vid_gen_params,
|
|
||||||
sd_image_t** frames_out,
|
|
||||||
int* num_frames_out,
|
|
||||||
sd_audio_t** audio_out);
|
|
||||||
|
|
||||||
typedef struct upscaler_ctx_t upscaler_ctx_t;
|
typedef struct upscaler_ctx_t upscaler_ctx_t;
|
||||||
|
|
||||||
@ -463,9 +391,7 @@ SD_API upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path,
|
|||||||
bool offload_params_to_cpu,
|
bool offload_params_to_cpu,
|
||||||
bool direct,
|
bool direct,
|
||||||
int n_threads,
|
int n_threads,
|
||||||
int tile_size,
|
int tile_size);
|
||||||
const char* backend,
|
|
||||||
const char* params_backend);
|
|
||||||
SD_API void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
|
SD_API void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
|
||||||
|
|
||||||
SD_API sd_image_t upscale(upscaler_ctx_t* upscaler_ctx,
|
SD_API sd_image_t upscale(upscaler_ctx_t* upscaler_ctx,
|
||||||
|
|||||||
@ -1,283 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import math
|
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
from collections import Counter
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import torch
|
|
||||||
from safetensors import safe_open
|
|
||||||
|
|
||||||
|
|
||||||
FLOAT_DTYPES = {
|
|
||||||
"BF16",
|
|
||||||
"F16",
|
|
||||||
"F32",
|
|
||||||
"F64",
|
|
||||||
"F8_E4M3",
|
|
||||||
"F8_E4M3FN",
|
|
||||||
"F8_E5M2",
|
|
||||||
}
|
|
||||||
|
|
||||||
FP8_DTYPES = {
|
|
||||||
"F8_E4M3",
|
|
||||||
"F8_E4M3FN",
|
|
||||||
"F8_E5M2",
|
|
||||||
}
|
|
||||||
|
|
||||||
DTYPE_SIZES = {
|
|
||||||
"BOOL": 1,
|
|
||||||
"U8": 1,
|
|
||||||
"I8": 1,
|
|
||||||
"F8_E4M3": 1,
|
|
||||||
"F8_E4M3FN": 1,
|
|
||||||
"F8_E5M2": 1,
|
|
||||||
"U16": 2,
|
|
||||||
"I16": 2,
|
|
||||||
"F16": 2,
|
|
||||||
"BF16": 2,
|
|
||||||
"U32": 4,
|
|
||||||
"I32": 4,
|
|
||||||
"F32": 4,
|
|
||||||
"U64": 8,
|
|
||||||
"I64": 8,
|
|
||||||
"F64": 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def read_safetensors_header(path: Path):
|
|
||||||
with path.open("rb") as f:
|
|
||||||
header_len = struct.unpack("<Q", f.read(8))[0]
|
|
||||||
header = f.read(header_len).decode("utf-8").rstrip()
|
|
||||||
return json.loads(header)
|
|
||||||
|
|
||||||
|
|
||||||
def numel(shape):
|
|
||||||
return math.prod(shape) if shape else 1
|
|
||||||
|
|
||||||
|
|
||||||
def scale_key_for_weight(name: str):
|
|
||||||
if name.endswith(".weight"):
|
|
||||||
return name[:-len(".weight")] + ".weight_scale"
|
|
||||||
if name.endswith("weight"):
|
|
||||||
return name + "_scale"
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def tensor_nbytes(dtype: str, shape):
|
|
||||||
return numel(shape) * DTYPE_SIZES[dtype]
|
|
||||||
|
|
||||||
|
|
||||||
def build_output_plan(header):
|
|
||||||
entries = {k: v for k, v in header.items() if k != "__metadata__"}
|
|
||||||
paired_scale_keys = set()
|
|
||||||
plan = []
|
|
||||||
|
|
||||||
for name, info in entries.items():
|
|
||||||
scale_key = scale_key_for_weight(name)
|
|
||||||
if info["dtype"] in FP8_DTYPES and scale_key in entries:
|
|
||||||
paired_scale_keys.add(scale_key)
|
|
||||||
|
|
||||||
for name, info in entries.items():
|
|
||||||
if name in paired_scale_keys:
|
|
||||||
continue
|
|
||||||
|
|
||||||
dtype = info["dtype"]
|
|
||||||
shape = info["shape"]
|
|
||||||
scale_key = scale_key_for_weight(name)
|
|
||||||
|
|
||||||
if dtype in FP8_DTYPES and scale_key in entries:
|
|
||||||
scale_info = entries[scale_key]
|
|
||||||
plan.append(
|
|
||||||
{
|
|
||||||
"name": name,
|
|
||||||
"source_dtype": dtype,
|
|
||||||
"output_dtype": "BF16",
|
|
||||||
"shape": shape,
|
|
||||||
"mode": "fp8_scaled_weight",
|
|
||||||
"scale_key": scale_key,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if dtype in FLOAT_DTYPES:
|
|
||||||
plan.append(
|
|
||||||
{
|
|
||||||
"name": name,
|
|
||||||
"source_dtype": dtype,
|
|
||||||
"output_dtype": "BF16",
|
|
||||||
"shape": shape,
|
|
||||||
"mode": "float_to_bf16",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
plan.append(
|
|
||||||
{
|
|
||||||
"name": name,
|
|
||||||
"source_dtype": dtype,
|
|
||||||
"output_dtype": dtype,
|
|
||||||
"shape": shape,
|
|
||||||
"mode": "copy",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
metadata = dict(header.get("__metadata__", {}) or {})
|
|
||||||
metadata["format"] = "pt"
|
|
||||||
metadata["conversion"] = "fp8_weight_scale_to_bf16"
|
|
||||||
|
|
||||||
output_header = {"__metadata__": metadata}
|
|
||||||
offset = 0
|
|
||||||
for item in plan:
|
|
||||||
size = tensor_nbytes(item["output_dtype"], item["shape"])
|
|
||||||
output_header[item["name"]] = {
|
|
||||||
"dtype": item["output_dtype"],
|
|
||||||
"shape": item["shape"],
|
|
||||||
"data_offsets": [offset, offset + size],
|
|
||||||
}
|
|
||||||
offset += size
|
|
||||||
|
|
||||||
return plan, output_header, offset
|
|
||||||
|
|
||||||
|
|
||||||
def write_tensor_bytes(out, tensor):
|
|
||||||
tensor = tensor.detach().cpu().contiguous()
|
|
||||||
if tensor.numel() == 0:
|
|
||||||
return
|
|
||||||
if tensor.dtype == torch.bfloat16:
|
|
||||||
tensor.view(torch.uint16).numpy().tofile(out)
|
|
||||||
elif tensor.dtype in (getattr(torch, "float8_e4m3fn", None), getattr(torch, "float8_e5m2", None)):
|
|
||||||
tensor.view(torch.uint8).numpy().tofile(out)
|
|
||||||
else:
|
|
||||||
tensor.numpy().tofile(out)
|
|
||||||
|
|
||||||
|
|
||||||
def scale_view_for_chunk(scale, chunk, first_dim_start=0, first_dim_end=None):
|
|
||||||
scale = scale.to(torch.float32)
|
|
||||||
|
|
||||||
if scale.numel() == 1:
|
|
||||||
return scale.reshape((1,) * chunk.ndim)
|
|
||||||
|
|
||||||
if chunk.ndim > 0 and scale.ndim == 1:
|
|
||||||
if first_dim_end is not None and scale.shape[0] >= first_dim_end:
|
|
||||||
scale = scale[first_dim_start:first_dim_end]
|
|
||||||
if scale.shape[0] == chunk.shape[0]:
|
|
||||||
return scale.reshape((scale.shape[0],) + (1,) * (chunk.ndim - 1))
|
|
||||||
|
|
||||||
return scale
|
|
||||||
|
|
||||||
|
|
||||||
def write_scaled_fp8_weight(out, weight, scale, chunk_rows):
|
|
||||||
if weight.ndim == 0:
|
|
||||||
result = weight.to(torch.float32) * scale_view_for_chunk(scale, weight)
|
|
||||||
write_tensor_bytes(out, result.to(torch.bfloat16))
|
|
||||||
return
|
|
||||||
|
|
||||||
rows = weight.shape[0]
|
|
||||||
for start in range(0, rows, chunk_rows):
|
|
||||||
end = min(start + chunk_rows, rows)
|
|
||||||
chunk = weight[start:end].to(torch.float32)
|
|
||||||
scale_view = scale_view_for_chunk(scale, chunk, start, end)
|
|
||||||
result = chunk * scale_view
|
|
||||||
write_tensor_bytes(out, result.to(torch.bfloat16))
|
|
||||||
|
|
||||||
|
|
||||||
def write_float_as_bf16(out, tensor, chunk_rows):
|
|
||||||
if tensor.dtype == torch.bfloat16:
|
|
||||||
write_tensor_bytes(out, tensor)
|
|
||||||
return
|
|
||||||
|
|
||||||
if tensor.ndim == 0:
|
|
||||||
write_tensor_bytes(out, tensor.to(torch.bfloat16))
|
|
||||||
return
|
|
||||||
|
|
||||||
rows = tensor.shape[0]
|
|
||||||
for start in range(0, rows, chunk_rows):
|
|
||||||
end = min(start + chunk_rows, rows)
|
|
||||||
write_tensor_bytes(out, tensor[start:end].to(torch.bfloat16))
|
|
||||||
|
|
||||||
|
|
||||||
def convert(input_path: Path, output_path: Path, chunk_rows: int, dry_run: bool):
|
|
||||||
header = read_safetensors_header(input_path)
|
|
||||||
plan, output_header, data_size = build_output_plan(header)
|
|
||||||
|
|
||||||
source_counts = Counter(item["source_dtype"] for item in plan)
|
|
||||||
output_counts = Counter(item["output_dtype"] for item in plan)
|
|
||||||
scaled_count = sum(item["mode"] == "fp8_scaled_weight" for item in plan)
|
|
||||||
dropped_scales = sum(item["mode"] == "fp8_scaled_weight" for item in plan)
|
|
||||||
header_bytes = json.dumps(output_header, separators=(",", ":")).encode("utf-8")
|
|
||||||
expected_size = 8 + len(header_bytes) + data_size
|
|
||||||
|
|
||||||
print(f"input: {input_path}")
|
|
||||||
print(f"output: {output_path}")
|
|
||||||
print(f"tensors written: {len(plan)}")
|
|
||||||
print(f"scaled fp8 weights dequantized: {scaled_count}")
|
|
||||||
print(f"weight_scale tensors dropped: {dropped_scales}")
|
|
||||||
print(f"source dtypes: {dict(sorted(source_counts.items()))}")
|
|
||||||
print(f"output dtypes: {dict(sorted(output_counts.items()))}")
|
|
||||||
print(f"expected output size: {expected_size / (1024 ** 3):.2f} GiB")
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
return
|
|
||||||
|
|
||||||
if output_path.exists():
|
|
||||||
raise FileExistsError(f"{output_path} already exists; pass --overwrite to replace it")
|
|
||||||
|
|
||||||
tmp_path = output_path.with_suffix(output_path.suffix + ".tmp")
|
|
||||||
if tmp_path.exists():
|
|
||||||
raise FileExistsError(f"{tmp_path} already exists; remove it or choose another output")
|
|
||||||
|
|
||||||
with safe_open(str(input_path), framework="pt", device="cpu") as sf, tmp_path.open("wb") as out:
|
|
||||||
out.write(struct.pack("<Q", len(header_bytes)))
|
|
||||||
out.write(header_bytes)
|
|
||||||
|
|
||||||
for index, item in enumerate(plan, 1):
|
|
||||||
name = item["name"]
|
|
||||||
print(f"[{index:04d}/{len(plan):04d}] {name} -> {item['output_dtype']}")
|
|
||||||
|
|
||||||
tensor = sf.get_tensor(name)
|
|
||||||
if item["mode"] == "fp8_scaled_weight":
|
|
||||||
scale = sf.get_tensor(item["scale_key"])
|
|
||||||
write_scaled_fp8_weight(out, tensor, scale, chunk_rows)
|
|
||||||
elif item["mode"] == "float_to_bf16":
|
|
||||||
write_float_as_bf16(out, tensor, chunk_rows)
|
|
||||||
else:
|
|
||||||
write_tensor_bytes(out, tensor)
|
|
||||||
|
|
||||||
actual_size = out.tell()
|
|
||||||
|
|
||||||
if actual_size != expected_size:
|
|
||||||
tmp_path.unlink(missing_ok=True)
|
|
||||||
raise RuntimeError(f"wrote {actual_size} bytes, expected {expected_size} bytes")
|
|
||||||
|
|
||||||
tmp_path.replace(output_path)
|
|
||||||
print("done")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Convert an fp8 safetensors checkpoint with weight_scale tensors to bf16."
|
|
||||||
)
|
|
||||||
parser.add_argument("--input", default="ideogram4_fp8.safetensors", type=Path)
|
|
||||||
parser.add_argument("--output", default="ideogram4_bf16.safetensors", type=Path)
|
|
||||||
parser.add_argument("--chunk-rows", default=1024, type=int)
|
|
||||||
parser.add_argument("--dry-run", action="store_true")
|
|
||||||
parser.add_argument("--overwrite", action="store_true")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
input_path = args.input.resolve()
|
|
||||||
output_path = args.output.resolve()
|
|
||||||
|
|
||||||
if args.chunk_rows < 1:
|
|
||||||
raise ValueError("--chunk-rows must be >= 1")
|
|
||||||
if not input_path.exists():
|
|
||||||
raise FileNotFoundError(input_path)
|
|
||||||
if args.overwrite and output_path.exists():
|
|
||||||
output_path.unlink()
|
|
||||||
|
|
||||||
convert(input_path, output_path, args.chunk_rows, args.dry_run)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,61 +1,18 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_ANIMA_HPP__
|
#ifndef __ANIMA_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_ANIMA_HPP__
|
#define __ANIMA_HPP__
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "model/common/block.hpp"
|
#include "common_block.hpp"
|
||||||
#include "model/common/rope.hpp"
|
#include "flux.hpp"
|
||||||
#include "model/diffusion/flux.hpp"
|
#include "rope.hpp"
|
||||||
#include "model/diffusion/model.hpp"
|
|
||||||
|
|
||||||
namespace Anima {
|
namespace Anima {
|
||||||
constexpr int ANIMA_GRAPH_SIZE = 65536;
|
constexpr int ANIMA_GRAPH_SIZE = 65536;
|
||||||
|
|
||||||
struct AnimaConfig {
|
|
||||||
int64_t in_channels = 16;
|
|
||||||
int64_t out_channels = 16;
|
|
||||||
int64_t hidden_size = 2048;
|
|
||||||
int64_t text_embed_dim = 1024;
|
|
||||||
int64_t num_heads = 16;
|
|
||||||
int64_t head_dim = 128;
|
|
||||||
int patch_size = 2;
|
|
||||||
int64_t num_layers = 28;
|
|
||||||
std::vector<int> axes_dim = {44, 42, 42};
|
|
||||||
int theta = 10000;
|
|
||||||
|
|
||||||
static AnimaConfig detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
|
||||||
AnimaConfig config;
|
|
||||||
int64_t detected_layers = 0;
|
|
||||||
std::string layer_tag = prefix.empty() ? "blocks." : prefix + ".blocks.";
|
|
||||||
for (const auto& [name, _] : tensor_storage_map) {
|
|
||||||
size_t pos = name.find(layer_tag);
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
size_t start = pos + layer_tag.size();
|
|
||||||
size_t end = name.find('.', start);
|
|
||||||
if (end == std::string::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int64_t layer_id = atoll(name.substr(start, end - start).c_str());
|
|
||||||
detected_layers = std::max(detected_layers, layer_id + 1);
|
|
||||||
}
|
|
||||||
if (detected_layers > 0) {
|
|
||||||
config.num_layers = detected_layers;
|
|
||||||
LOG_DEBUG("anima: num_layers = %" PRId64 ", hidden_size = %" PRId64 ", num_heads = %" PRId64 ", head_dim = %" PRId64,
|
|
||||||
config.num_layers,
|
|
||||||
config.hidden_size,
|
|
||||||
config.num_heads,
|
|
||||||
config.head_dim);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* apply_gate(ggml_context* ctx,
|
__STATIC_INLINE__ ggml_tensor* apply_gate(ggml_context* ctx,
|
||||||
ggml_tensor* x,
|
ggml_tensor* x,
|
||||||
ggml_tensor* gate) {
|
ggml_tensor* gate) {
|
||||||
@ -460,22 +417,31 @@ namespace Anima {
|
|||||||
|
|
||||||
struct AnimaNet : public GGMLBlock {
|
struct AnimaNet : public GGMLBlock {
|
||||||
public:
|
public:
|
||||||
AnimaConfig config;
|
int64_t in_channels = 16;
|
||||||
|
int64_t out_channels = 16;
|
||||||
|
int64_t hidden_size = 2048;
|
||||||
|
int64_t text_embed_dim = 1024;
|
||||||
|
int64_t num_heads = 16;
|
||||||
|
int64_t head_dim = 128;
|
||||||
|
int patch_size = 2;
|
||||||
|
int64_t num_layers = 28;
|
||||||
|
std::vector<int> axes_dim = {44, 42, 42};
|
||||||
|
int theta = 10000;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AnimaNet() = default;
|
AnimaNet() = default;
|
||||||
explicit AnimaNet(AnimaConfig config)
|
explicit AnimaNet(int64_t num_layers)
|
||||||
: config(config) {
|
: num_layers(num_layers) {
|
||||||
blocks["x_embedder"] = std::make_shared<XEmbedder>((config.in_channels + 1) * config.patch_size * config.patch_size, config.hidden_size);
|
blocks["x_embedder"] = std::make_shared<XEmbedder>((in_channels + 1) * patch_size * patch_size, hidden_size);
|
||||||
blocks["t_embedder"] = std::make_shared<TimestepEmbedder>(config.hidden_size, config.hidden_size * 3);
|
blocks["t_embedder"] = std::make_shared<TimestepEmbedder>(hidden_size, hidden_size * 3);
|
||||||
blocks["t_embedding_norm"] = std::make_shared<RMSNorm>(config.hidden_size, 1e-6f);
|
blocks["t_embedding_norm"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
||||||
for (int i = 0; i < config.num_layers; i++) {
|
for (int i = 0; i < num_layers; i++) {
|
||||||
blocks["blocks." + std::to_string(i)] = std::make_shared<TransformerBlock>(config.hidden_size,
|
blocks["blocks." + std::to_string(i)] = std::make_shared<TransformerBlock>(hidden_size,
|
||||||
config.text_embed_dim,
|
text_embed_dim,
|
||||||
config.num_heads,
|
num_heads,
|
||||||
config.head_dim);
|
head_dim);
|
||||||
}
|
}
|
||||||
blocks["final_layer"] = std::make_shared<FinalLayer>(config.hidden_size, config.patch_size, config.out_channels);
|
blocks["final_layer"] = std::make_shared<FinalLayer>(hidden_size, patch_size, out_channels);
|
||||||
blocks["llm_adapter"] = std::make_shared<LLMAdapter>(1024, 1024, 1024, 6, 16);
|
blocks["llm_adapter"] = std::make_shared<LLMAdapter>(1024, 1024, 1024, 6, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,11 +468,11 @@ namespace Anima {
|
|||||||
auto padding_mask = ggml_ext_zeros(ctx->ggml_ctx, x->ne[0], x->ne[1], 1, x->ne[3]);
|
auto padding_mask = ggml_ext_zeros(ctx->ggml_ctx, x->ne[0], x->ne[1], 1, x->ne[3]);
|
||||||
x = ggml_concat(ctx->ggml_ctx, x, padding_mask, 2); // [N, C + 1, H, W]
|
x = ggml_concat(ctx->ggml_ctx, x, padding_mask, 2); // [N, C + 1, H, W]
|
||||||
|
|
||||||
x = DiT::pad_and_patchify(ctx, x, config.patch_size, config.patch_size); // [N, h*w, (C+1)*ph*pw]
|
x = DiT::pad_and_patchify(ctx, x, patch_size, patch_size); // [N, h*w, (C+1)*ph*pw]
|
||||||
|
|
||||||
x = x_embedder->forward(ctx, x);
|
x = x_embedder->forward(ctx, x);
|
||||||
|
|
||||||
auto timestep_proj = ggml_ext_timestep_embedding(ctx->ggml_ctx, timestep, static_cast<int>(config.hidden_size));
|
auto timestep_proj = ggml_ext_timestep_embedding(ctx->ggml_ctx, timestep, static_cast<int>(hidden_size));
|
||||||
auto temb = t_embedder->forward(ctx, timestep_proj);
|
auto temb = t_embedder->forward(ctx, timestep_proj);
|
||||||
auto embedded_timestep = t_embedding_norm->forward(ctx, timestep_proj);
|
auto embedded_timestep = t_embedding_norm->forward(ctx, timestep_proj);
|
||||||
|
|
||||||
@ -533,40 +499,53 @@ namespace Anima {
|
|||||||
encoder_hidden_states = adapted_context;
|
encoder_hidden_states = adapted_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "anima.prelude", "x");
|
for (int i = 0; i < num_layers; i++) {
|
||||||
sd::ggml_graph_cut::mark_graph_cut(embedded_timestep, "anima.prelude", "embedded_timestep");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(temb, "anima.prelude", "temb");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(encoder_hidden_states, "anima.prelude", "context");
|
|
||||||
|
|
||||||
for (int i = 0; i < config.num_layers; i++) {
|
|
||||||
auto block = std::dynamic_pointer_cast<TransformerBlock>(blocks["blocks." + std::to_string(i)]);
|
auto block = std::dynamic_pointer_cast<TransformerBlock>(blocks["blocks." + std::to_string(i)]);
|
||||||
x = block->forward(ctx, x, encoder_hidden_states, embedded_timestep, temb, image_pe);
|
x = block->forward(ctx, x, encoder_hidden_states, embedded_timestep, temb, image_pe);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "anima.blocks." + std::to_string(i), "x");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x = final_layer->forward(ctx, x, embedded_timestep, temb); // [N, h*w, ph*pw*C]
|
x = final_layer->forward(ctx, x, embedded_timestep, temb); // [N, h*w, ph*pw*C]
|
||||||
|
|
||||||
x = DiT::unpatchify_and_crop(ctx->ggml_ctx, x, H, W, config.patch_size, config.patch_size, false); // [N, C, H, W]
|
x = DiT::unpatchify_and_crop(ctx->ggml_ctx, x, H, W, patch_size, patch_size, false); // [N, C, H, W]
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimaRunner : public DiffusionModelRunner {
|
struct AnimaRunner : public GGMLRunner {
|
||||||
public:
|
public:
|
||||||
std::vector<float> image_pe_vec;
|
std::vector<float> image_pe_vec;
|
||||||
std::vector<float> adapter_q_pe_vec;
|
std::vector<float> adapter_q_pe_vec;
|
||||||
std::vector<float> adapter_k_pe_vec;
|
std::vector<float> adapter_k_pe_vec;
|
||||||
AnimaConfig config;
|
|
||||||
AnimaNet net;
|
AnimaNet net;
|
||||||
|
|
||||||
AnimaRunner(ggml_backend_t backend,
|
AnimaRunner(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
const std::string prefix = "model.diffusion_model")
|
const std::string prefix = "model.diffusion_model")
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||||
config(AnimaConfig::detect_from_weights(tensor_storage_map, prefix + ".net")) {
|
int64_t num_layers = 0;
|
||||||
net = AnimaNet(config);
|
std::string layer_tag = prefix + ".net.blocks.";
|
||||||
|
for (const auto& kv : tensor_storage_map) {
|
||||||
|
const std::string& tensor_name = kv.first;
|
||||||
|
size_t pos = tensor_name.find(layer_tag);
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t start = pos + layer_tag.size();
|
||||||
|
size_t end = tensor_name.find('.', start);
|
||||||
|
if (end == std::string::npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int64_t layer_id = atoll(tensor_name.substr(start, end - start).c_str());
|
||||||
|
num_layers = std::max(num_layers, layer_id + 1);
|
||||||
|
}
|
||||||
|
if (num_layers <= 0) {
|
||||||
|
num_layers = 28;
|
||||||
|
}
|
||||||
|
LOG_INFO("anima net layers: %" PRId64, num_layers);
|
||||||
|
|
||||||
|
net = AnimaNet(num_layers);
|
||||||
net.init(params_ctx, tensor_storage_map, prefix + ".net");
|
net.init(params_ctx, tensor_storage_map, prefix + ".net");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,7 +553,7 @@ namespace Anima {
|
|||||||
return "anima";
|
return "anima";
|
||||||
}
|
}
|
||||||
|
|
||||||
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, const std::string prefix) {
|
||||||
net.get_param_tensors(tensors, prefix + ".net");
|
net.get_param_tensors(tensors, prefix + ".net");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,8 +592,7 @@ namespace Anima {
|
|||||||
{},
|
{},
|
||||||
empty_ref_latents,
|
empty_ref_latents,
|
||||||
false,
|
false,
|
||||||
1.0f,
|
1.0f);
|
||||||
false);
|
|
||||||
|
|
||||||
std::vector<float> axis_thetas = {
|
std::vector<float> axis_thetas = {
|
||||||
static_cast<float>(theta) * calc_ntk_factor(t_extrapolation_ratio, axes_dim[0]),
|
static_cast<float>(theta) * calc_ntk_factor(t_extrapolation_ratio, axes_dim[0]),
|
||||||
@ -637,22 +615,22 @@ namespace Anima {
|
|||||||
GGML_ASSERT(x->ne[3] == 1);
|
GGML_ASSERT(x->ne[3] == 1);
|
||||||
ggml_cgraph* gf = new_graph_custom(ANIMA_GRAPH_SIZE);
|
ggml_cgraph* gf = new_graph_custom(ANIMA_GRAPH_SIZE);
|
||||||
|
|
||||||
int64_t pad_h = (config.patch_size - x->ne[1] % config.patch_size) % config.patch_size;
|
int64_t pad_h = (net.patch_size - x->ne[1] % net.patch_size) % net.patch_size;
|
||||||
int64_t pad_w = (config.patch_size - x->ne[0] % config.patch_size) % config.patch_size;
|
int64_t pad_w = (net.patch_size - x->ne[0] % net.patch_size) % net.patch_size;
|
||||||
int64_t h_pad = x->ne[1] + pad_h;
|
int64_t h_pad = x->ne[1] + pad_h;
|
||||||
int64_t w_pad = x->ne[0] + pad_w;
|
int64_t w_pad = x->ne[0] + pad_w;
|
||||||
|
|
||||||
image_pe_vec = gen_anima_image_pe_vec(1,
|
image_pe_vec = gen_anima_image_pe_vec(1,
|
||||||
static_cast<int>(h_pad),
|
static_cast<int>(h_pad),
|
||||||
static_cast<int>(w_pad),
|
static_cast<int>(w_pad),
|
||||||
static_cast<int>(config.patch_size),
|
static_cast<int>(net.patch_size),
|
||||||
config.theta,
|
net.theta,
|
||||||
config.axes_dim,
|
net.axes_dim,
|
||||||
4.0f,
|
4.0f,
|
||||||
4.0f,
|
4.0f,
|
||||||
1.0f);
|
1.0f);
|
||||||
int64_t image_pos_len = static_cast<int64_t>(image_pe_vec.size()) / (2 * 2 * (config.head_dim / 2));
|
int64_t image_pos_len = static_cast<int64_t>(image_pe_vec.size()) / (2 * 2 * (net.head_dim / 2));
|
||||||
auto image_pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, config.head_dim / 2, image_pos_len);
|
auto image_pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, net.head_dim / 2, image_pos_len);
|
||||||
set_backend_tensor_data(image_pe, image_pe_vec.data());
|
set_backend_tensor_data(image_pe, image_pe_vec.data());
|
||||||
|
|
||||||
ggml_tensor* adapter_q_pe = nullptr;
|
ggml_tensor* adapter_q_pe = nullptr;
|
||||||
@ -699,20 +677,7 @@ namespace Anima {
|
|||||||
};
|
};
|
||||||
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), x.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
const auto* extra = diffusion_extra_as<AnimaDiffusionExtra>(diffusion_params);
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context),
|
|
||||||
tensor_or_empty(extra->t5_ids),
|
|
||||||
tensor_or_empty(extra->t5_weights));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace Anima
|
} // namespace Anima
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_ANIMA_HPP__
|
#endif // __ANIMA_HPP__
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef __SD_MODEL_VAE_AUTO_ENCODER_KL_HPP__
|
#ifndef __AUTO_ENCODER_KL_HPP__
|
||||||
#define __SD_MODEL_VAE_AUTO_ENCODER_KL_HPP__
|
#define __AUTO_ENCODER_KL_HPP__
|
||||||
|
|
||||||
#include "model/vae/vae.hpp"
|
#include "vae.hpp"
|
||||||
|
|
||||||
/*================================================== AutoEncoderKL ===================================================*/
|
/*================================================== AutoEncoderKL ===================================================*/
|
||||||
|
|
||||||
@ -328,7 +328,6 @@ public:
|
|||||||
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
auto conv_out = std::dynamic_pointer_cast<Conv2d>(blocks["conv_out"]);
|
||||||
|
|
||||||
auto h = conv_in->forward(ctx, x); // [N, ch, h, w]
|
auto h = conv_in->forward(ctx, x); // [N, ch, h, w]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.encoder.prelude", "h");
|
|
||||||
|
|
||||||
// downsampling
|
// downsampling
|
||||||
size_t num_resolutions = ch_mult.size();
|
size_t num_resolutions = ch_mult.size();
|
||||||
@ -338,14 +337,12 @@ public:
|
|||||||
auto down_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
auto down_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||||
|
|
||||||
h = down_block->forward(ctx, h);
|
h = down_block->forward(ctx, h);
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.encoder.down." + std::to_string(i) + ".block." + std::to_string(j), "h");
|
|
||||||
}
|
}
|
||||||
if (i != num_resolutions - 1) {
|
if (i != num_resolutions - 1) {
|
||||||
std::string name = "down." + std::to_string(i) + ".downsample";
|
std::string name = "down." + std::to_string(i) + ".downsample";
|
||||||
auto down_sample = std::dynamic_pointer_cast<DownSampleBlock>(blocks[name]);
|
auto down_sample = std::dynamic_pointer_cast<DownSampleBlock>(blocks[name]);
|
||||||
|
|
||||||
h = down_sample->forward(ctx, h);
|
h = down_sample->forward(ctx, h);
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.encoder.down." + std::to_string(i) + ".downsample", "h");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +350,6 @@ public:
|
|||||||
h = mid_block_1->forward(ctx, h);
|
h = mid_block_1->forward(ctx, h);
|
||||||
h = mid_attn_1->forward(ctx, h);
|
h = mid_attn_1->forward(ctx, h);
|
||||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.encoder.mid", "h");
|
|
||||||
|
|
||||||
// end
|
// end
|
||||||
h = norm_out->forward(ctx, h);
|
h = norm_out->forward(ctx, h);
|
||||||
@ -454,7 +450,6 @@ public:
|
|||||||
|
|
||||||
// conv_in
|
// conv_in
|
||||||
auto h = conv_in->forward(ctx, z); // [N, block_in, h, w]
|
auto h = conv_in->forward(ctx, z); // [N, block_in, h, w]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.decoder.prelude", "h");
|
|
||||||
|
|
||||||
// middle
|
// middle
|
||||||
h = mid_block_1->forward(ctx, h);
|
h = mid_block_1->forward(ctx, h);
|
||||||
@ -462,7 +457,6 @@ public:
|
|||||||
|
|
||||||
h = mid_attn_1->forward(ctx, h);
|
h = mid_attn_1->forward(ctx, h);
|
||||||
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
h = mid_block_2->forward(ctx, h); // [N, block_in, h, w]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.decoder.mid", "h");
|
|
||||||
|
|
||||||
// upsampling
|
// upsampling
|
||||||
int num_resolutions = static_cast<int>(ch_mult.size());
|
int num_resolutions = static_cast<int>(ch_mult.size());
|
||||||
@ -472,14 +466,12 @@ public:
|
|||||||
auto up_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
auto up_block = std::dynamic_pointer_cast<ResnetBlock>(blocks[name]);
|
||||||
|
|
||||||
h = up_block->forward(ctx, h);
|
h = up_block->forward(ctx, h);
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.decoder.up." + std::to_string(i) + ".block." + std::to_string(j), "h");
|
|
||||||
}
|
}
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
std::string name = "up." + std::to_string(i) + ".upsample";
|
std::string name = "up." + std::to_string(i) + ".upsample";
|
||||||
auto up_sample = std::dynamic_pointer_cast<UpSampleBlock>(blocks[name]);
|
auto up_sample = std::dynamic_pointer_cast<UpSampleBlock>(blocks[name]);
|
||||||
|
|
||||||
h = up_sample->forward(ctx, h);
|
h = up_sample->forward(ctx, h);
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(h, "vae.decoder.up." + std::to_string(i) + ".upsample", "h");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,7 +599,6 @@ public:
|
|||||||
if (use_quant) {
|
if (use_quant) {
|
||||||
auto post_quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["post_quant_conv"]);
|
auto post_quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["post_quant_conv"]);
|
||||||
z = post_quant_conv->forward(ctx, z); // [N, z_channels, h, w]
|
z = post_quant_conv->forward(ctx, z); // [N, z_channels, h, w]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(z, "vae.decode.prelude", "z");
|
|
||||||
}
|
}
|
||||||
auto decoder = std::dynamic_pointer_cast<Decoder>(blocks["decoder"]);
|
auto decoder = std::dynamic_pointer_cast<Decoder>(blocks["decoder"]);
|
||||||
|
|
||||||
@ -625,7 +616,6 @@ public:
|
|||||||
if (use_quant) {
|
if (use_quant) {
|
||||||
auto quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["quant_conv"]);
|
auto quant_conv = std::dynamic_pointer_cast<Conv2d>(blocks["quant_conv"]);
|
||||||
z = quant_conv->forward(ctx, z); // [N, 2*embed_dim, h/8, w/8]
|
z = quant_conv->forward(ctx, z); // [N, 2*embed_dim, h/8, w/8]
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(z, "vae.encode.final", "z");
|
|
||||||
}
|
}
|
||||||
if (sd_version_uses_flux2_vae(version)) {
|
if (sd_version_uses_flux2_vae(version)) {
|
||||||
z = ggml_ext_chunk(ctx->ggml_ctx, z, 2, 2)[0];
|
z = ggml_ext_chunk(ctx->ggml_ctx, z, 2, 2)[0];
|
||||||
@ -664,13 +654,13 @@ struct AutoEncoderKL : public VAE {
|
|||||||
AutoEncoderKLModel ae;
|
AutoEncoderKLModel ae;
|
||||||
|
|
||||||
AutoEncoderKL(ggml_backend_t backend,
|
AutoEncoderKL(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map,
|
const String2TensorStorage& tensor_storage_map,
|
||||||
const std::string prefix,
|
const std::string prefix,
|
||||||
bool decode_only = false,
|
bool decode_only = false,
|
||||||
bool use_video_decoder = false,
|
bool use_video_decoder = false,
|
||||||
SDVersion version = VERSION_SD1)
|
SDVersion version = VERSION_SD1)
|
||||||
: decode_only(decode_only), VAE(version, backend, params_backend) {
|
: decode_only(decode_only), VAE(version, backend, offload_params_to_cpu) {
|
||||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||||
scale_factor = 0.18215f;
|
scale_factor = 0.18215f;
|
||||||
shift_factor = 0.f;
|
shift_factor = 0.f;
|
||||||
@ -680,7 +670,7 @@ struct AutoEncoderKL : public VAE {
|
|||||||
} else if (sd_version_is_sd3(version)) {
|
} else if (sd_version_is_sd3(version)) {
|
||||||
scale_factor = 1.5305f;
|
scale_factor = 1.5305f;
|
||||||
shift_factor = 0.0609f;
|
shift_factor = 0.0609f;
|
||||||
} else if (sd_version_is_flux(version) || sd_version_is_z_image(version) || sd_version_is_longcat(version)) {
|
} else if (sd_version_is_flux(version) || sd_version_is_z_image(version)) {
|
||||||
scale_factor = 0.3611f;
|
scale_factor = 0.3611f;
|
||||||
shift_factor = 0.1159f;
|
shift_factor = 0.1159f;
|
||||||
} else if (sd_version_uses_flux2_vae(version)) {
|
} else if (sd_version_uses_flux2_vae(version)) {
|
||||||
@ -886,4 +876,4 @@ struct AutoEncoderKL : public VAE {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_VAE_AUTO_ENCODER_KL_HPP__
|
#endif // __AUTO_ENCODER_KL_HPP__
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef __SD_RUNTIME_CACHE_DIT_HPP__
|
#ifndef __CACHE_DIT_HPP__
|
||||||
#define __SD_RUNTIME_CACHE_DIT_HPP__
|
#define __CACHE_DIT_HPP__
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -8,9 +8,9 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "condition_cache_utils.hpp"
|
||||||
#include "core/tensor.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "runtime/condition_cache_utils.hpp"
|
#include "tensor.hpp"
|
||||||
|
|
||||||
struct DBCacheConfig {
|
struct DBCacheConfig {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
@ -893,4 +893,4 @@ struct CacheDitConditionState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_RUNTIME_CACHE_DIT_HPP__
|
#endif
|
||||||
@ -1,13 +1,13 @@
|
|||||||
#ifndef __SD_MODEL_TE_CLIP_HPP__
|
#ifndef __CLIP_HPP__
|
||||||
#define __SD_MODEL_TE_CLIP_HPP__
|
#define __CLIP_HPP__
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "tokenizers/clip_tokenizer.h"
|
#include "tokenizers/clip_tokenizer.h"
|
||||||
|
|
||||||
/*================================================ FrozenCLIPEmbedder ================================================*/
|
/*================================================ FrozenCLIPEmbedder ================================================*/
|
||||||
|
|
||||||
// Ref: https://github.com/huggingface/transformers/blob/main/src/transformers/model/clip/modeling_clip.py
|
// Ref: https://github.com/huggingface/transformers/blob/main/src/transformers/models/clip/modeling_clip.py
|
||||||
|
|
||||||
struct CLIPMLP : public GGMLBlock {
|
struct CLIPMLP : public GGMLBlock {
|
||||||
protected:
|
protected:
|
||||||
@ -96,8 +96,7 @@ public:
|
|||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||||
ggml_tensor* x,
|
ggml_tensor* x,
|
||||||
ggml_tensor* mask = nullptr,
|
ggml_tensor* mask = nullptr,
|
||||||
int clip_skip = -1,
|
int clip_skip = -1) {
|
||||||
const std::string& graph_cut_prefix = "") {
|
|
||||||
// x: [N, n_token, d_model]
|
// x: [N, n_token, d_model]
|
||||||
int layer_idx = n_layer - 1;
|
int layer_idx = n_layer - 1;
|
||||||
// LOG_DEBUG("clip_skip %d", clip_skip);
|
// LOG_DEBUG("clip_skip %d", clip_skip);
|
||||||
@ -113,9 +112,6 @@ public:
|
|||||||
std::string name = "layers." + std::to_string(i);
|
std::string name = "layers." + std::to_string(i);
|
||||||
auto layer = std::dynamic_pointer_cast<CLIPLayer>(blocks[name]);
|
auto layer = std::dynamic_pointer_cast<CLIPLayer>(blocks[name]);
|
||||||
x = layer->forward(ctx, x, mask); // [N, n_token, d_model]
|
x = layer->forward(ctx, x, mask); // [N, n_token, d_model]
|
||||||
if (!graph_cut_prefix.empty()) {
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, graph_cut_prefix + ".layers." + std::to_string(i), "x");
|
|
||||||
}
|
|
||||||
// LOG_DEBUG("layer %d", i);
|
// LOG_DEBUG("layer %d", i);
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
@ -308,8 +304,7 @@ public:
|
|||||||
auto final_layer_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["final_layer_norm"]);
|
auto final_layer_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["final_layer_norm"]);
|
||||||
|
|
||||||
auto x = embeddings->forward(ctx, input_ids, tkn_embeddings); // [N, n_token, hidden_size]
|
auto x = embeddings->forward(ctx, input_ids, tkn_embeddings); // [N, n_token, hidden_size]
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "clip_text.prelude", "x");
|
x = encoder->forward(ctx, x, mask, return_pooled ? -1 : clip_skip);
|
||||||
x = encoder->forward(ctx, x, mask, return_pooled ? -1 : clip_skip, "clip_text");
|
|
||||||
if (return_pooled || with_final_ln) {
|
if (return_pooled || with_final_ln) {
|
||||||
x = final_layer_norm->forward(ctx, x);
|
x = final_layer_norm->forward(ctx, x);
|
||||||
}
|
}
|
||||||
@ -373,8 +368,7 @@ public:
|
|||||||
|
|
||||||
auto x = embeddings->forward(ctx, pixel_values); // [N, num_positions, embed_dim]
|
auto x = embeddings->forward(ctx, pixel_values); // [N, num_positions, embed_dim]
|
||||||
x = pre_layernorm->forward(ctx, x);
|
x = pre_layernorm->forward(ctx, x);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "clip_vision.prelude", "x");
|
x = encoder->forward(ctx, x, nullptr, clip_skip);
|
||||||
x = encoder->forward(ctx, x, nullptr, clip_skip, "clip_vision");
|
|
||||||
|
|
||||||
auto last_hidden_state = x;
|
auto last_hidden_state = x;
|
||||||
|
|
||||||
@ -469,13 +463,13 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
|||||||
std::vector<float> attention_mask_vec;
|
std::vector<float> attention_mask_vec;
|
||||||
|
|
||||||
CLIPTextModelRunner(ggml_backend_t backend,
|
CLIPTextModelRunner(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map,
|
const String2TensorStorage& tensor_storage_map,
|
||||||
const std::string prefix,
|
const std::string prefix,
|
||||||
CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
||||||
bool with_final_ln = true,
|
bool with_final_ln = true,
|
||||||
bool force_clip_f32 = false)
|
bool force_clip_f32 = false)
|
||||||
: GGMLRunner(backend, params_backend) {
|
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||||
bool proj_in = false;
|
bool proj_in = false;
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||||
if (!starts_with(name, prefix)) {
|
if (!starts_with(name, prefix)) {
|
||||||
@ -579,4 +573,4 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_TE_CLIP_HPP__
|
#endif // __CLIP_HPP__
|
||||||
@ -1,9 +1,7 @@
|
|||||||
#ifndef __SD_MODEL_COMMON_BLOCK_HPP__
|
#ifndef __COMMON_BLOCK_HPP__
|
||||||
#define __SD_MODEL_COMMON_BLOCK_HPP__
|
#define __COMMON_BLOCK_HPP__
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "core/util.h"
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
|
|
||||||
class DownSampleBlock : public GGMLBlock {
|
class DownSampleBlock : public GGMLBlock {
|
||||||
protected:
|
protected:
|
||||||
@ -227,37 +225,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Mlp : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
Mlp(int64_t in_features,
|
|
||||||
int64_t hidden_features = -1,
|
|
||||||
int64_t out_features = -1,
|
|
||||||
bool bias = true) {
|
|
||||||
// act_layer is always lambda: nn.GELU(approximate="tanh")
|
|
||||||
// norm_layer is always None
|
|
||||||
// use_conv is always False
|
|
||||||
if (hidden_features == -1) {
|
|
||||||
hidden_features = in_features;
|
|
||||||
}
|
|
||||||
if (out_features == -1) {
|
|
||||||
out_features = in_features;
|
|
||||||
}
|
|
||||||
blocks["fc1"] = std::shared_ptr<GGMLBlock>(new Linear(in_features, hidden_features, bias));
|
|
||||||
blocks["fc2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_features, out_features, bias));
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
// x: [N, n_token, in_features]
|
|
||||||
auto fc1 = std::dynamic_pointer_cast<Linear>(blocks["fc1"]);
|
|
||||||
auto fc2 = std::dynamic_pointer_cast<Linear>(blocks["fc2"]);
|
|
||||||
|
|
||||||
x = fc1->forward(ctx, x);
|
|
||||||
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
|
||||||
x = fc2->forward(ctx, x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FeedForward : public GGMLBlock {
|
class FeedForward : public GGMLBlock {
|
||||||
public:
|
public:
|
||||||
enum class Activation {
|
enum class Activation {
|
||||||
@ -281,6 +248,9 @@ public:
|
|||||||
float scale = 1.f;
|
float scale = 1.f;
|
||||||
if (precision_fix) {
|
if (precision_fix) {
|
||||||
scale = 1.f / 128.f;
|
scale = 1.f / 128.f;
|
||||||
|
#ifdef SD_USE_VULKAN
|
||||||
|
force_prec_f32 = true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
||||||
// For example, when using Vulkan without enabling force_prec_f32,
|
// For example, when using Vulkan without enabling force_prec_f32,
|
||||||
@ -294,9 +264,6 @@ public:
|
|||||||
|
|
||||||
auto net_0 = std::dynamic_pointer_cast<UnaryBlock>(blocks["net.0"]);
|
auto net_0 = std::dynamic_pointer_cast<UnaryBlock>(blocks["net.0"]);
|
||||||
auto net_2 = std::dynamic_pointer_cast<Linear>(blocks["net.2"]);
|
auto net_2 = std::dynamic_pointer_cast<Linear>(blocks["net.2"]);
|
||||||
if (sd_backend_is(ctx->backend, "Vulkan")) {
|
|
||||||
net_2->set_force_prec_f32(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
x = net_0->forward(ctx, x); // [ne3, ne2, ne1, inner_dim]
|
x = net_0->forward(ctx, x); // [ne3, ne2, ne1, inner_dim]
|
||||||
x = net_2->forward(ctx, x); // [ne3, ne2, ne1, dim_out]
|
x = net_2->forward(ctx, x); // [ne3, ne2, ne1, dim_out]
|
||||||
@ -634,4 +601,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_COMMON_BLOCK_HPP__
|
#endif // __COMMON_BLOCK_HPP__
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_DIT_HPP__
|
#ifndef __COMMON_DIT_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_DIT_HPP__
|
#define __COMMON_DIT_HPP__
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
|
|
||||||
namespace DiT {
|
namespace DiT {
|
||||||
inline ggml_tensor* patchify(ggml_context* ctx,
|
inline ggml_tensor* patchify(ggml_context* ctx,
|
||||||
@ -103,64 +103,6 @@ namespace DiT {
|
|||||||
x = ggml_ext_slice(ctx, x, 0, 0, W); // [N, C, H, W]
|
x = ggml_ext_slice(ctx, x, 0, 0, W); // [N, C, H, W]
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ggml_tensor* patchify(ggml_context* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
int pt,
|
|
||||||
int ph,
|
|
||||||
int pw,
|
|
||||||
int64_t N = 1) {
|
|
||||||
// x: [N*C, T, H, W]
|
|
||||||
// return: [N, h*w, C*pt*ph*pw]
|
|
||||||
int64_t C = x->ne[3] / N;
|
|
||||||
int64_t T = x->ne[2];
|
|
||||||
int64_t H = x->ne[1];
|
|
||||||
int64_t W = x->ne[0];
|
|
||||||
int64_t t_len = T / pt;
|
|
||||||
int64_t h_len = H / ph;
|
|
||||||
int64_t w_len = W / pw;
|
|
||||||
|
|
||||||
GGML_ASSERT(C * N == x->ne[3]);
|
|
||||||
GGML_ASSERT(t_len * pt == T && h_len * ph == H && w_len * pw == W);
|
|
||||||
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * w_len, ph * h_len, pt, t_len * C * N); // [N*C*t_len, pt, h_len*ph, w_len*pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len, h_len*ph, pt, w_len*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * w_len, pt, ph, h_len * t_len * C * N); // [N*C*t_len*h_len, ph, pt, w_len*pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len*h_len, pt, ph, w_len*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw, w_len, ph * pt, h_len * t_len * C * N); // [N*C*t_len*h_len, pt*ph, w_len, pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len*h_len, w_len, pt*ph, pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * ph * pt, w_len * h_len * t_len, C, N); // [N, C, t_len*h_len*w_len, pt*ph*pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N, t_len*h_len*w_len, C, pt*ph*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * ph * pt * C, w_len * h_len * t_len, N, 1); // [N, t_len*h_len*w_len, C*pt*ph*pw]
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ggml_tensor* unpatchify(ggml_context* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
int64_t t_len,
|
|
||||||
int64_t h_len,
|
|
||||||
int64_t w_len,
|
|
||||||
int pt,
|
|
||||||
int ph,
|
|
||||||
int pw) {
|
|
||||||
// x: [N, t_len*h_len*w_len, pt*ph*pw*C]
|
|
||||||
// return: [N*C, t_len*pt, h_len*ph, w_len*pw]
|
|
||||||
int64_t N = x->ne[3];
|
|
||||||
int64_t C = x->ne[0] / pt / ph / pw;
|
|
||||||
|
|
||||||
GGML_ASSERT(C * pt * ph * pw == x->ne[0]);
|
|
||||||
|
|
||||||
x = ggml_reshape_4d(ctx, x, C, pw * ph * pt, w_len * h_len * t_len, N); // [N, t_len*h_len*w_len, pt*ph*pw, C]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 1, 2, 0, 3)); // [N, C, t_len*h_len*w_len, pt*ph*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw, ph * pt, w_len, h_len * t_len * C * N); // [N*C*t_len*h_len, w_len, pt*ph, pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len*h_len, pt*ph, w_len, pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * w_len, ph, pt, h_len * t_len * C * N); // [N*C*t_len*h_len, pt, ph, w_len*pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len*h_len, ph, pt, w_len*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * w_len, pt, ph * h_len, t_len * C * N); // [N*C*t_len, h_len*ph, pt, w_len*pw]
|
|
||||||
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [N*C*t_len, pt, h_len*ph, w_len*pw]
|
|
||||||
x = ggml_reshape_4d(ctx, x, pw * w_len, ph * h_len, pt * t_len, C * N); // [N*C, t_len*pt, h_len*ph, w_len*pw]
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
} // namespace DiT
|
} // namespace DiT
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_DIT_HPP__
|
#endif // __COMMON_DIT_HPP__
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#ifndef __SD_RUNTIME_CONDITION_CACHE_UTILS_HPP__
|
#ifndef __CONDITION_CACHE_UTILS_HPP__
|
||||||
#define __SD_RUNTIME_CONDITION_CACHE_UTILS_HPP__
|
#define __CONDITION_CACHE_UTILS_HPP__
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/tensor.hpp"
|
#include "tensor.hpp"
|
||||||
|
|
||||||
namespace sd {
|
namespace sd {
|
||||||
|
|
||||||
@ -61,4 +61,4 @@ namespace sd {
|
|||||||
|
|
||||||
} // namespace sd
|
} // namespace sd
|
||||||
|
|
||||||
#endif // __SD_RUNTIME_CONDITION_CACHE_UTILS_HPP__
|
#endif // __CONDITION_CACHE_UTILS_HPP__
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
#ifndef __CONTROL_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
#define __CONTROL_HPP__
|
||||||
|
|
||||||
#include "model/common/block.hpp"
|
#include "common_block.hpp"
|
||||||
#include "model_loader.h"
|
#include "model.h"
|
||||||
|
|
||||||
#define CONTROL_NET_GRAPH_SIZE 1536
|
#define CONTROL_NET_GRAPH_SIZE 1536
|
||||||
|
|
||||||
@ -319,10 +319,10 @@ struct ControlNet : public GGMLRunner {
|
|||||||
bool guided_hint_cached = false;
|
bool guided_hint_cached = false;
|
||||||
|
|
||||||
ControlNet(ggml_backend_t backend,
|
ControlNet(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
SDVersion version = VERSION_SD1)
|
SDVersion version = VERSION_SD1)
|
||||||
: GGMLRunner(backend, params_backend), control_net(version) {
|
: GGMLRunner(backend, offload_params_to_cpu), control_net(version) {
|
||||||
control_net.init(params_ctx, tensor_storage_map, "");
|
control_net.init(params_ctx, tensor_storage_map, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,11 +457,7 @@ struct ControlNet : public GGMLRunner {
|
|||||||
|
|
||||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||||
LOG_INFO("loading control net from '%s'", file_path.c_str());
|
LOG_INFO("loading control net from '%s'", file_path.c_str());
|
||||||
if (!alloc_params_buffer()) {
|
alloc_params_buffer();
|
||||||
LOG_ERROR("control net model buffer allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ggml_tensor*> tensors;
|
std::map<std::string, ggml_tensor*> tensors;
|
||||||
control_net.get_param_tensors(tensors);
|
control_net.get_param_tensors(tensors);
|
||||||
std::set<std::string> ignore_tensors;
|
std::set<std::string> ignore_tensors;
|
||||||
@ -484,4 +480,4 @@ struct ControlNet : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_CONTROL_HPP__
|
#endif // __CONTROL_HPP__
|
||||||
138
src/convert.cpp
138
src/convert.cpp
@ -1,138 +0,0 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <mutex>
|
|
||||||
#include <regex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "model_io/gguf_io.h"
|
|
||||||
#include "model_io/safetensors_io.h"
|
|
||||||
#include "model_loader.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include "ggml_extend_backend.h"
|
|
||||||
|
|
||||||
static ggml_type get_export_tensor_type(ModelLoader& model_loader,
|
|
||||||
const TensorStorage& tensor_storage,
|
|
||||||
ggml_type type,
|
|
||||||
const TensorTypeRules& tensor_type_rules) {
|
|
||||||
const std::string& name = tensor_storage.name;
|
|
||||||
ggml_type tensor_type = tensor_storage.type;
|
|
||||||
ggml_type dst_type = type;
|
|
||||||
|
|
||||||
for (const auto& tensor_type_rule : tensor_type_rules) {
|
|
||||||
std::regex pattern(tensor_type_rule.first);
|
|
||||||
if (std::regex_search(name, pattern)) {
|
|
||||||
dst_type = tensor_type_rule.second;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model_loader.tensor_should_be_converted(tensor_storage, dst_type)) {
|
|
||||||
tensor_type = dst_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tensor_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool load_tensors_for_export(ModelLoader& model_loader,
|
|
||||||
ggml_context* ggml_ctx,
|
|
||||||
ggml_type type,
|
|
||||||
const TensorTypeRules& tensor_type_rules,
|
|
||||||
std::vector<TensorWriteInfo>& tensors) {
|
|
||||||
std::mutex tensor_mutex;
|
|
||||||
auto on_new_tensor_cb = [&](const TensorStorage& tensor_storage, ggml_tensor** dst_tensor) -> bool {
|
|
||||||
const std::string& name = tensor_storage.name;
|
|
||||||
ggml_type tensor_type = get_export_tensor_type(model_loader, tensor_storage, type, tensor_type_rules);
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(tensor_mutex);
|
|
||||||
ggml_tensor* tensor = ggml_new_tensor(ggml_ctx, tensor_type, tensor_storage.n_dims, tensor_storage.ne);
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
LOG_ERROR("ggml_new_tensor failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ggml_set_name(tensor, name.c_str());
|
|
||||||
|
|
||||||
if (!tensor->data) {
|
|
||||||
GGML_ASSERT(ggml_nelements(tensor) == 0);
|
|
||||||
// Avoid crashing writers by setting a dummy pointer for zero-sized tensors.
|
|
||||||
LOG_DEBUG("setting dummy pointer for zero-sized tensor %s", name.c_str());
|
|
||||||
tensor->data = ggml_get_mem_buffer(ggml_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TensorWriteInfo write_info;
|
|
||||||
write_info.tensor = tensor;
|
|
||||||
write_info.n_dims = tensor_storage.n_dims;
|
|
||||||
for (int i = 0; i < tensor_storage.n_dims; ++i) {
|
|
||||||
write_info.ne[i] = tensor_storage.ne[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst_tensor = tensor;
|
|
||||||
tensors.push_back(std::move(write_info));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool success = model_loader.load_tensors(on_new_tensor_cb);
|
|
||||||
LOG_INFO("load tensors done");
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool convert(const char* input_path,
|
|
||||||
const char* vae_path,
|
|
||||||
const char* output_path,
|
|
||||||
sd_type_t output_type,
|
|
||||||
const char* tensor_type_rules,
|
|
||||||
bool convert_name) {
|
|
||||||
ModelLoader model_loader;
|
|
||||||
|
|
||||||
if (!model_loader.init_from_file(input_path)) {
|
|
||||||
LOG_ERROR("init model loader from file failed: '%s'", input_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vae_path != nullptr && strlen(vae_path) > 0) {
|
|
||||||
if (!model_loader.init_from_file(vae_path, "vae.")) {
|
|
||||||
LOG_ERROR("init model loader from file failed: '%s'", vae_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (convert_name) {
|
|
||||||
model_loader.convert_tensors_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_type type = (ggml_type)output_type;
|
|
||||||
bool output_is_safetensors = ends_with(output_path, ".safetensors");
|
|
||||||
TensorTypeRules type_rules = parse_tensor_type_rules(tensor_type_rules);
|
|
||||||
|
|
||||||
auto backend = sd_backend_cpu_init();
|
|
||||||
size_t mem_size = 1 * 1024 * 1024; // for padding
|
|
||||||
mem_size += model_loader.get_tensor_storage_map().size() * ggml_tensor_overhead();
|
|
||||||
mem_size += model_loader.get_params_mem_size(backend, type);
|
|
||||||
LOG_INFO("model tensors mem size: %.2fMB", mem_size / 1024.f / 1024.f);
|
|
||||||
ggml_context* ggml_ctx = ggml_init({mem_size, nullptr, false});
|
|
||||||
|
|
||||||
if (ggml_ctx == nullptr) {
|
|
||||||
LOG_ERROR("ggml_init failed for converter");
|
|
||||||
ggml_backend_free(backend);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<TensorWriteInfo> tensors;
|
|
||||||
bool success = load_tensors_for_export(model_loader, ggml_ctx, type, type_rules, tensors);
|
|
||||||
ggml_backend_free(backend);
|
|
||||||
|
|
||||||
std::string error;
|
|
||||||
if (success) {
|
|
||||||
if (output_is_safetensors) {
|
|
||||||
success = write_safetensors_file(output_path, tensors, &error);
|
|
||||||
} else {
|
|
||||||
success = write_gguf_file(output_path, tensors, &error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success && !error.empty()) {
|
|
||||||
LOG_ERROR("%s", error.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_free(ggml_ctx);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
@ -1,656 +0,0 @@
|
|||||||
#include "core/ggml_extend_backend.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <mutex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/util.h"
|
|
||||||
#include "stable-diffusion.h"
|
|
||||||
|
|
||||||
static std::string trim_copy(const std::string& value) {
|
|
||||||
size_t begin = 0;
|
|
||||||
while (begin < value.size() && std::isspace(static_cast<unsigned char>(value[begin]))) {
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
size_t end = value.size();
|
|
||||||
while (end > begin && std::isspace(static_cast<unsigned char>(value[end - 1]))) {
|
|
||||||
--end;
|
|
||||||
}
|
|
||||||
return value.substr(begin, end - begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string lower_copy(std::string value) {
|
|
||||||
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
|
|
||||||
return static_cast<char>(std::tolower(c));
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<std::string> split_copy(const std::string& value, char delimiter) {
|
|
||||||
std::vector<std::string> parts;
|
|
||||||
std::string part;
|
|
||||||
std::istringstream stream(value);
|
|
||||||
while (std::getline(stream, part, delimiter)) {
|
|
||||||
parts.push_back(part);
|
|
||||||
}
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_default_backend_token(const std::string& name) {
|
|
||||||
const std::string lower = lower_copy(trim_copy(name));
|
|
||||||
return lower.empty() || lower == "default" || lower == "auto";
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
name.erase(std::remove(name.begin(), name.end(), '_'), name.end());
|
|
||||||
|
|
||||||
if (name == "diffusion" || name == "model" || name == "unet" || name == "dit") {
|
|
||||||
*module = SDBackendModule::DIFFUSION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "te" || name == "clip" || name == "text" || name == "textencoder" || name == "textencoders" || name == "conditioner" || name == "cond" || name == "llm" || name == "t5" || name == "t5xxl") {
|
|
||||||
*module = SDBackendModule::TE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "clipvision" || name == "vision") {
|
|
||||||
*module = SDBackendModule::CLIP_VISION;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "vae" || name == "firststage" || name == "autoencoder" || name == "tae") {
|
|
||||||
*module = SDBackendModule::VAE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "controlnet" || name == "control") {
|
|
||||||
*module = SDBackendModule::CONTROL_NET;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "photomaker" || name == "photomakerid" || name == "pmid" || name == "photo") {
|
|
||||||
*module = SDBackendModule::PHOTOMAKER;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (name == "upscaler" || name == "esrgan" || name == "hires") {
|
|
||||||
*module = SDBackendModule::UPSCALER;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string module_assignment_name(const SDBackendAssignment& assignment, SDBackendModule module) {
|
|
||||||
auto it = assignment.module_names.find(module);
|
|
||||||
if (it != assignment.module_names.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return assignment.default_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string backend_cache_key(ggml_backend_t backend) {
|
|
||||||
if (backend == nullptr) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_get_device(backend);
|
|
||||||
if (dev != nullptr) {
|
|
||||||
return lower_copy(ggml_backend_dev_name(dev));
|
|
||||||
}
|
|
||||||
const char* backend_name = ggml_backend_name(backend);
|
|
||||||
return backend_name != nullptr ? lower_copy(backend_name) : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string resolve_first_device_by_type(enum ggml_backend_dev_type type) {
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_dev_by_type(type);
|
|
||||||
if (dev == nullptr) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return ggml_backend_dev_name(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ggml_backend_buffer_t ggml_backend_tensor_buffer(const struct ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ggml_backend_tensor_is_host_accessible(const struct ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr || tensor->data == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_buffer_t buffer = ggml_backend_tensor_buffer(tensor);
|
|
||||||
return buffer == nullptr || ggml_backend_buffer_is_host(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t ggml_backend_tensor_offset(const struct ggml_tensor* tensor, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
|
||||||
return static_cast<size_t>(i0 * tensor->nb[0] + i1 * tensor->nb[1] + i2 * tensor->nb[2] + i3 * tensor->nb[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static void ggml_backend_tensor_write_scalar(const struct ggml_tensor* tensor, int64_t i0, int64_t i1, int64_t i2, int64_t i3, T value) {
|
|
||||||
const size_t offset = ggml_backend_tensor_offset(tensor, i0, i1, i2, i3);
|
|
||||||
|
|
||||||
if (ggml_backend_tensor_is_host_accessible(tensor)) {
|
|
||||||
auto* dst = reinterpret_cast<T*>(reinterpret_cast<char*>(tensor->data) + offset);
|
|
||||||
*dst = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_tensor_set(const_cast<struct ggml_tensor*>(tensor), &value, offset, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ggml_set_f32_nd(const struct ggml_tensor* tensor, int64_t i0, int64_t i1, int64_t i2, int64_t i3, float value) {
|
|
||||||
switch (tensor->type) {
|
|
||||||
case GGML_TYPE_I8:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, static_cast<int8_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_I16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, static_cast<int16_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_I32:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, static_cast<int32_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_F16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, ggml_fp32_to_fp16(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_BF16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, ggml_fp32_to_bf16(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_F32:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i0, i1, i2, i3, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GGML_ABORT("fatal error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ggml_ext_im_set_f32_1d(const struct ggml_tensor* tensor, int i, float value) {
|
|
||||||
if (!ggml_is_contiguous(tensor)) {
|
|
||||||
int64_t id[4] = {0, 0, 0, 0};
|
|
||||||
ggml_unravel_index(tensor, i, &id[0], &id[1], &id[2], &id[3]);
|
|
||||||
ggml_set_f32_nd(tensor, id[0], id[1], id[2], id[3], value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (tensor->type) {
|
|
||||||
case GGML_TYPE_I8:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, static_cast<int8_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_I16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, static_cast<int16_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_I32:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, static_cast<int32_t>(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_F16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, ggml_fp32_to_fp16(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_BF16:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, ggml_fp32_to_bf16(value));
|
|
||||||
break;
|
|
||||||
case GGML_TYPE_F32:
|
|
||||||
ggml_backend_tensor_write_scalar(tensor, i, 0, 0, 0, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GGML_ABORT("fatal error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// completed and avoid rescanning the default paths.
|
|
||||||
if (ggml_backend_dev_count() > 0 && ggml_backend_reg_by_name("CPU") != nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// In dynamic-backend mode the backend modules are discovered at runtime,
|
|
||||||
// so we must load them before asking for the CPU backend or its proc table.
|
|
||||||
// If the host preloaded only a subset of backends, allow one default-path
|
|
||||||
// scan so missing modules can still be discovered.
|
|
||||||
static std::once_flag once;
|
|
||||||
std::call_once(once, []() {
|
|
||||||
if (ggml_backend_dev_count() > 0 && ggml_backend_reg_by_name("CPU") != nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ggml_backend_load_all();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sd_backend_is(ggml_backend_t backend, const std::string& name) {
|
|
||||||
if (!backend) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_get_device(backend);
|
|
||||||
if (!dev) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::string dev_name = ggml_backend_dev_name(dev);
|
|
||||||
return lower_copy(dev_name).find(lower_copy(name)) != std::string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string get_default_backend_name() {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
// should pick the same backend preference as ggml_backend_init_best
|
|
||||||
std::string name = resolve_first_device_by_type(GGML_BACKEND_DEVICE_TYPE_GPU);
|
|
||||||
if (!name.empty()) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
name = resolve_first_device_by_type(GGML_BACKEND_DEVICE_TYPE_IGPU);
|
|
||||||
if (!name.empty()) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return resolve_first_device_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string sd_resolve_backend_name(const std::string& name) {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
std::string requested = trim_copy(name);
|
|
||||||
std::string lower = lower_copy(requested);
|
|
||||||
|
|
||||||
if (is_default_backend_token(lower)) {
|
|
||||||
return get_default_backend_name();
|
|
||||||
}
|
|
||||||
if (lower == "gpu") {
|
|
||||||
std::string result = resolve_first_device_by_type(GGML_BACKEND_DEVICE_TYPE_GPU);
|
|
||||||
if (!result.empty()) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return resolve_first_device_by_type(GGML_BACKEND_DEVICE_TYPE_IGPU);
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t device_count = ggml_backend_dev_count();
|
|
||||||
for (size_t i = 0; i < device_count; ++i) {
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_dev_get(i);
|
|
||||||
std::string dev_name = ggml_backend_dev_name(dev);
|
|
||||||
if (lower_copy(dev_name) == lower) {
|
|
||||||
return dev_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < device_count; ++i) {
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_dev_get(i);
|
|
||||||
std::string dev_name = ggml_backend_dev_name(dev);
|
|
||||||
std::string dev_lower = lower_copy(dev_name);
|
|
||||||
if (dev_lower.rfind(lower, 0) == 0) {
|
|
||||||
return dev_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool backend_name_exists(const std::string& name) {
|
|
||||||
return !sd_resolve_backend_name(name).empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ggml_backend_t init_named_backend(const std::string& name) {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
LOG_DEBUG("Initializing backend: %s", name.c_str());
|
|
||||||
if (trim_copy(name).empty()) {
|
|
||||||
return ggml_backend_init_best();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string resolved = sd_resolve_backend_name(name);
|
|
||||||
if (resolved.empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return ggml_backend_init_by_name(resolved.c_str(), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sd_backend_is_cpu(ggml_backend_t backend) {
|
|
||||||
if (backend == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto dev = ggml_backend_get_device(backend);
|
|
||||||
return dev != nullptr && ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_t sd_backend_cpu_init() {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
return ggml_backend_init_by_type(GGML_BACKEND_DEVICE_TYPE_CPU, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sd_backend_cpu_set_n_threads(ggml_backend_t backend, int n_threads) {
|
|
||||||
if (backend == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto dev = ggml_backend_get_device(backend);
|
|
||||||
if (dev != nullptr && ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU) {
|
|
||||||
auto reg = ggml_backend_dev_backend_reg(dev);
|
|
||||||
auto ggml_backend_set_n_threads_fn = (ggml_backend_set_n_threads_t)ggml_backend_reg_get_proc_address(reg, "ggml_backend_set_n_threads");
|
|
||||||
if (ggml_backend_set_n_threads_fn != nullptr) {
|
|
||||||
ggml_backend_set_n_threads_fn(backend, n_threads);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* sd_get_system_info() {
|
|
||||||
static std::string cache_info = []() -> std::string {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "System Info: \n";
|
|
||||||
auto dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
|
|
||||||
if (dev != nullptr) {
|
|
||||||
auto reg = ggml_backend_dev_backend_reg(dev);
|
|
||||||
auto ggml_backend_get_features_fn = (ggml_backend_get_features_t)ggml_backend_reg_get_proc_address(reg, "ggml_backend_get_features");
|
|
||||||
if (ggml_backend_get_features_fn != nullptr) {
|
|
||||||
ggml_backend_feature* feat = ggml_backend_get_features_fn(reg);
|
|
||||||
while (feat->name && feat->value) {
|
|
||||||
ss << " " << feat->name << " = " << feat->value << " | ";
|
|
||||||
feat++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_WARN("unable to get CPU features");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_WARN("unable to get CPU features");
|
|
||||||
}
|
|
||||||
return ss.str();
|
|
||||||
}();
|
|
||||||
return cache_info.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ggml_backend_t sd_get_default_backend() {
|
|
||||||
ggml_backend_load_all_once();
|
|
||||||
static std::once_flag once;
|
|
||||||
std::call_once(once, []() {
|
|
||||||
size_t dev_count = ggml_backend_dev_count();
|
|
||||||
if (dev_count == 0) {
|
|
||||||
LOG_ERROR("No devices found!");
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG("Found %zu backend devices:", dev_count);
|
|
||||||
for (size_t i = 0; i < dev_count; ++i) {
|
|
||||||
auto dev = ggml_backend_dev_get(i);
|
|
||||||
LOG_DEBUG("#%zu: %s", i, ggml_backend_dev_name(dev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ggml_backend_t backend = nullptr;
|
|
||||||
const char* SD_VK_DEVICE = getenv("SD_VK_DEVICE");
|
|
||||||
if (SD_VK_DEVICE != nullptr) {
|
|
||||||
std::string sd_vk_device_str = SD_VK_DEVICE;
|
|
||||||
try {
|
|
||||||
unsigned long long device = std::stoull(sd_vk_device_str);
|
|
||||||
std::string vk_device_name = "Vulkan" + std::to_string(device);
|
|
||||||
if (backend_name_exists(vk_device_name)) {
|
|
||||||
LOG_INFO("Selecting %s as main device by env var SD_VK_DEVICE", vk_device_name.c_str());
|
|
||||||
backend = init_named_backend(vk_device_name);
|
|
||||||
if (!backend) {
|
|
||||||
LOG_WARN("Device %s requested by SD_VK_DEVICE failed to init. Falling back to the default device.", vk_device_name.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_WARN("Device %s requested by SD_VK_DEVICE was not found. Falling back to the default device.", vk_device_name.c_str());
|
|
||||||
}
|
|
||||||
} catch (const std::invalid_argument&) {
|
|
||||||
LOG_WARN("SD_VK_DEVICE environment variable is not a valid integer (%s). Falling back to the default device.", SD_VK_DEVICE);
|
|
||||||
} catch (const std::out_of_range&) {
|
|
||||||
LOG_WARN("SD_VK_DEVICE environment variable value is out of range for `unsigned long long` type (%s). Falling back to the default device.", SD_VK_DEVICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backend) {
|
|
||||||
std::string dev_name = get_default_backend_name();
|
|
||||||
backend = init_named_backend(dev_name);
|
|
||||||
if (!backend && !dev_name.empty()) {
|
|
||||||
LOG_WARN("device %s failed to init", dev_name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backend) {
|
|
||||||
LOG_WARN("loading CPU backend");
|
|
||||||
backend = sd_backend_cpu_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sd_backend_is_cpu(backend)) {
|
|
||||||
LOG_DEBUG("Using CPU backend");
|
|
||||||
}
|
|
||||||
|
|
||||||
return backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sd_parse_backend_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
|
|
||||||
if (assignment == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*assignment = {};
|
|
||||||
const std::string in = trim_copy(spec);
|
|
||||||
if (in.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& raw_part : split_copy(in, ',')) {
|
|
||||||
const std::string part = trim_copy(raw_part);
|
|
||||||
if (part.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t eq = part.find('=');
|
|
||||||
if (eq == std::string::npos) {
|
|
||||||
assignment->set_default(part);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string key = trim_copy(part.substr(0, eq));
|
|
||||||
const std::string value = trim_copy(part.substr(eq + 1));
|
|
||||||
if (key.empty() || value.empty()) {
|
|
||||||
if (error != nullptr) {
|
|
||||||
*error = "invalid backend assignment '" + part + "'";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string key_lower = lower_copy(key);
|
|
||||||
if (key_lower == "all" || key_lower == "default" || key_lower == "*") {
|
|
||||||
assignment->set_default(value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDBackendModule module = SDBackendModule::DIFFUSION;
|
|
||||||
if (!parse_backend_module(key, &module)) {
|
|
||||||
if (error != nullptr) {
|
|
||||||
*error = "unknown backend module '" + key + "'";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
assignment->set_module(module, value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDBackendAssignment::empty() const {
|
|
||||||
return default_name.empty() && module_names.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SDBackendAssignment::get(SDBackendModule module) const {
|
|
||||||
return module_assignment_name(*this, module);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDBackendAssignment::set_default(const std::string& name) {
|
|
||||||
default_name = trim_copy(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDBackendAssignment::set_module(SDBackendModule module, const std::string& name) {
|
|
||||||
module_names[module] = trim_copy(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDBackendHandleDeleter::operator()(ggml_backend_t backend) const {
|
|
||||||
ggml_backend_free(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDBackendManager::~SDBackendManager() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDBackendManager::reset() {
|
|
||||||
backends_.clear();
|
|
||||||
runtime_assignment_ = {};
|
|
||||||
params_assignment_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_t SDBackendManager::runtime_backend(SDBackendModule module) {
|
|
||||||
return init_cached_backend(runtime_assignment_.get(module));
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_t SDBackendManager::params_backend(SDBackendModule module) {
|
|
||||||
std::string name = params_assignment_.get(module);
|
|
||||||
if (name.empty()) {
|
|
||||||
return runtime_backend(module);
|
|
||||||
}
|
|
||||||
return init_cached_backend(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDBackendManager::runtime_backend_is_cpu(SDBackendModule module) {
|
|
||||||
return sd_backend_is_cpu(runtime_backend(module));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDBackendManager::params_backend_is_cpu(SDBackendModule module) {
|
|
||||||
return sd_backend_is_cpu(params_backend(module));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SDBackendManager::runtime_backend_supports_host_buffer(SDBackendModule module) {
|
|
||||||
ggml_backend_t backend = runtime_backend(module);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (!sd_parse_backend_assignment(SAFE_STR(backend_spec), &runtime_assignment_, error)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!sd_parse_backend_assignment(SAFE_STR(params_backend_spec), ¶ms_assignment_, error)) {
|
|
||||||
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 {
|
|
||||||
if (is_default_backend_token(name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!sd_resolve_backend_name(name).empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (error != nullptr) {
|
|
||||||
*error = "backend '" + name + "' was not found";
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!validate_name(runtime_assignment_.default_name) ||
|
|
||||||
!validate_name(params_assignment_.default_name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto& kv : runtime_assignment_.module_names) {
|
|
||||||
if (!validate_name(kv.second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto& kv : params_assignment_.module_names) {
|
|
||||||
if (!validate_name(kv.second)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_t SDBackendManager::init_cached_backend(const std::string& name) {
|
|
||||||
std::string resolved = sd_resolve_backend_name(name);
|
|
||||||
std::string key = lower_copy(resolved);
|
|
||||||
ggml_backend_t backend = nullptr;
|
|
||||||
|
|
||||||
if (!key.empty()) {
|
|
||||||
auto it = backends_.find(key);
|
|
||||||
if (it != backends_.end()) {
|
|
||||||
return it->second.get();
|
|
||||||
}
|
|
||||||
} else if (!is_default_backend_token(name)) {
|
|
||||||
LOG_ERROR("backend '%s' was not found", name.c_str());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend = is_default_backend_token(name) ? sd_get_default_backend() : init_named_backend(resolved);
|
|
||||||
if (backend == nullptr) {
|
|
||||||
LOG_ERROR("failed to initialize backend '%s'", name.c_str());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string actual_key = backend_cache_key(backend);
|
|
||||||
if (actual_key.empty()) {
|
|
||||||
actual_key = !key.empty() ? key : lower_copy(trim_copy(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = backends_.find(actual_key);
|
|
||||||
if (it != backends_.end()) {
|
|
||||||
ggml_backend_free(backend);
|
|
||||||
return it->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
SDBackendHandle handle(backend);
|
|
||||||
backends_.emplace(actual_key, std::move(handle));
|
|
||||||
return backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* sd_backend_module_name(SDBackendModule module) {
|
|
||||||
switch (module) {
|
|
||||||
case SDBackendModule::DIFFUSION:
|
|
||||||
return "diffusion";
|
|
||||||
case SDBackendModule::TE:
|
|
||||||
return "te";
|
|
||||||
case SDBackendModule::CLIP_VISION:
|
|
||||||
return "clip_vision";
|
|
||||||
case SDBackendModule::VAE:
|
|
||||||
return "vae";
|
|
||||||
case SDBackendModule::CONTROL_NET:
|
|
||||||
return "controlnet";
|
|
||||||
case SDBackendModule::PHOTOMAKER:
|
|
||||||
return "photomaker";
|
|
||||||
case SDBackendModule::UPSCALER:
|
|
||||||
return "upscaler";
|
|
||||||
}
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
#ifndef __SD_CORE_GGML_EXTEND_BACKEND_H__
|
|
||||||
#define __SD_CORE_GGML_EXTEND_BACKEND_H__
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
#include "ggml.h"
|
|
||||||
|
|
||||||
enum class SDBackendModule {
|
|
||||||
DIFFUSION,
|
|
||||||
TE,
|
|
||||||
CLIP_VISION,
|
|
||||||
VAE,
|
|
||||||
CONTROL_NET,
|
|
||||||
PHOTOMAKER,
|
|
||||||
UPSCALER,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDBackendAssignment {
|
|
||||||
std::string default_name;
|
|
||||||
std::unordered_map<SDBackendModule, std::string> module_names;
|
|
||||||
|
|
||||||
bool empty() const;
|
|
||||||
std::string get(SDBackendModule module) const;
|
|
||||||
void set_default(const std::string& name);
|
|
||||||
void set_module(SDBackendModule module, const std::string& name);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDBackendHandleDeleter {
|
|
||||||
void operator()(ggml_backend_t backend) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
using SDBackendHandle = std::unique_ptr<struct ggml_backend, SDBackendHandleDeleter>;
|
|
||||||
|
|
||||||
class SDBackendManager {
|
|
||||||
private:
|
|
||||||
SDBackendAssignment runtime_assignment_;
|
|
||||||
SDBackendAssignment params_assignment_;
|
|
||||||
std::unordered_map<std::string, SDBackendHandle> backends_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SDBackendManager() = default;
|
|
||||||
~SDBackendManager();
|
|
||||||
|
|
||||||
SDBackendManager(const SDBackendManager&) = delete;
|
|
||||||
SDBackendManager& operator=(const SDBackendManager&) = delete;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
ggml_backend_t runtime_backend(SDBackendModule module);
|
|
||||||
ggml_backend_t params_backend(SDBackendModule module);
|
|
||||||
|
|
||||||
bool runtime_backend_is_cpu(SDBackendModule module);
|
|
||||||
bool params_backend_is_cpu(SDBackendModule module);
|
|
||||||
bool runtime_backend_supports_host_buffer(SDBackendModule module);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool validate(std::string* error) const;
|
|
||||||
ggml_backend_t init_cached_backend(const std::string& name);
|
|
||||||
};
|
|
||||||
|
|
||||||
bool sd_backend_is(ggml_backend_t backend, const std::string& name);
|
|
||||||
bool sd_backend_is_cpu(ggml_backend_t backend);
|
|
||||||
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);
|
|
||||||
#endif // __SD_CORE_GGML_EXTEND_BACKEND_H__
|
|
||||||
@ -1,806 +0,0 @@
|
|||||||
#include "core/ggml_graph_cut.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stack>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "core/util.h"
|
|
||||||
#include "ggml-alloc.h"
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
|
|
||||||
#include "ggml/src/ggml-impl.h"
|
|
||||||
|
|
||||||
namespace sd::ggml_graph_cut {
|
|
||||||
|
|
||||||
static constexpr double MAX_VRAM_BYTES_PER_GIB = 1024.0 * 1024.0 * 1024.0;
|
|
||||||
|
|
||||||
static std::string graph_cut_tensor_display_name(const ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return "<null>";
|
|
||||||
}
|
|
||||||
if (tensor->name[0] != '\0') {
|
|
||||||
return tensor->name;
|
|
||||||
}
|
|
||||||
return sd_format("<tensor@%p>", (const void*)tensor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int graph_leaf_index(ggml_cgraph* gf, const ggml_tensor* tensor) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
GGML_ASSERT(tensor != nullptr);
|
|
||||||
for (int i = 0; i < gf->n_leafs; ++i) {
|
|
||||||
if (gf->leafs[i] == tensor) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_params_tensor(const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return params_tensor_set.find(tensor) != params_tensor_set.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int graph_node_index_by_name(ggml_cgraph* gf, const char* name) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (name == nullptr || name[0] == '\0') {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const int n_nodes = ggml_graph_n_nodes(gf);
|
|
||||||
for (int i = 0; i < n_nodes; ++i) {
|
|
||||||
ggml_tensor* node = ggml_graph_node(gf, i);
|
|
||||||
if (node != nullptr && std::strcmp(node->name, name) == 0) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Plan::InputShape input_shape(const ggml_tensor* tensor) {
|
|
||||||
Plan::InputShape shape;
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
shape.type = tensor->type;
|
|
||||||
for (int i = 0; i < GGML_MAX_DIMS; ++i) {
|
|
||||||
shape.ne[static_cast<size_t>(i)] = tensor->ne[i];
|
|
||||||
}
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t graph_cut_segment_vram_bytes(const Segment& segment) {
|
|
||||||
return segment.compute_buffer_size +
|
|
||||||
segment.input_param_bytes +
|
|
||||||
segment.input_previous_cut_bytes +
|
|
||||||
segment.output_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t max_vram_gib_to_bytes(float max_vram) {
|
|
||||||
if (max_vram <= 0.f) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(static_cast<double>(max_vram) * MAX_VRAM_BYTES_PER_GIB);
|
|
||||||
}
|
|
||||||
|
|
||||||
static float max_vram_bytes_to_gib(size_t max_vram_bytes) {
|
|
||||||
return static_cast<float>(static_cast<double>(max_vram_bytes) / MAX_VRAM_BYTES_PER_GIB);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t resolve_auto_max_vram_bytes(float spare_vram, ggml_backend_t backend) {
|
|
||||||
if (backend == nullptr) {
|
|
||||||
LOG_WARN("--max-vram < 0 requested, but no backend is available; disabling graph splitting");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_dev_t dev = ggml_backend_get_device(backend);
|
|
||||||
if (dev == nullptr) {
|
|
||||||
LOG_WARN("--max-vram < 0 requested, but no backend device is available; disabling graph splitting");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU) {
|
|
||||||
LOG_WARN("--max-vram < 0 requested, but the main backend is CPU; disabling graph splitting");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t free_vram = 0;
|
|
||||||
size_t total_vram = 0;
|
|
||||||
ggml_backend_dev_memory(dev, &free_vram, &total_vram);
|
|
||||||
size_t spare_bytes = static_cast<size_t>(MAX_VRAM_BYTES_PER_GIB * spare_vram);
|
|
||||||
|
|
||||||
if (free_vram <= spare_bytes) {
|
|
||||||
LOG_WARN("--max-vram < 0 requested, but free VRAM is %.2f GiB; reserving %.2f GiB leaves no graph budget",
|
|
||||||
free_vram / MAX_VRAM_BYTES_PER_GIB, spare_vram);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t max_vram_bytes = free_vram - spare_bytes;
|
|
||||||
LOG_INFO("--max-vram < 0 auto-detected %.2f GiB free VRAM (%.2f GiB total), reserving %.2f GiB; using %.2f GiB",
|
|
||||||
free_vram / MAX_VRAM_BYTES_PER_GIB,
|
|
||||||
total_vram / MAX_VRAM_BYTES_PER_GIB,
|
|
||||||
spare_vram,
|
|
||||||
max_vram_bytes / MAX_VRAM_BYTES_PER_GIB);
|
|
||||||
return max_vram_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
float resolve_max_vram_gib(float max_vram, ggml_backend_t backend) {
|
|
||||||
if (max_vram >= 0.f) {
|
|
||||||
return max_vram;
|
|
||||||
}
|
|
||||||
return max_vram_bytes_to_gib(resolve_auto_max_vram_bytes(-max_vram, backend));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Segment make_segment_seed(const Plan& plan,
|
|
||||||
size_t start_segment_index,
|
|
||||||
size_t end_segment_index) {
|
|
||||||
GGML_ASSERT(start_segment_index < plan.segments.size());
|
|
||||||
GGML_ASSERT(end_segment_index < plan.segments.size());
|
|
||||||
GGML_ASSERT(start_segment_index <= end_segment_index);
|
|
||||||
|
|
||||||
Segment seed;
|
|
||||||
const auto& start_segment = plan.segments[start_segment_index];
|
|
||||||
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) {
|
|
||||||
for (int output_node_index : plan.segments[seg_idx].output_node_indices) {
|
|
||||||
if (seen_output_node_indices.insert(output_node_index).second) {
|
|
||||||
seed.output_node_indices.push_back(output_node_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (start_segment_index == end_segment_index) {
|
|
||||||
seed.group_name = target_segment.group_name;
|
|
||||||
} else {
|
|
||||||
seed.group_name = sd_format("%s..%s",
|
|
||||||
start_segment.group_name.c_str(),
|
|
||||||
target_segment.group_name.c_str());
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void build_segment(ggml_cgraph* gf,
|
|
||||||
Plan& plan,
|
|
||||||
Segment& segment,
|
|
||||||
const std::unordered_map<const ggml_tensor*, int>& producer_index,
|
|
||||||
std::unordered_set<int>& available_cut_output_node_indices,
|
|
||||||
ggml_backend_t backend,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc) {
|
|
||||||
std::set<int> internal_nodes;
|
|
||||||
std::unordered_set<const ggml_tensor*> input_seen;
|
|
||||||
std::vector<Segment::InputRef> input_refs;
|
|
||||||
|
|
||||||
std::stack<ggml_tensor*> work_stack;
|
|
||||||
for (int output_node_index : segment.output_node_indices) {
|
|
||||||
ggml_tensor* output = ggml_graph_node(gf, output_node_index);
|
|
||||||
if (output != nullptr) {
|
|
||||||
work_stack.push(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!work_stack.empty()) {
|
|
||||||
ggml_tensor* tensor = work_stack.top();
|
|
||||||
work_stack.pop();
|
|
||||||
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto producer_it = producer_index.find(tensor);
|
|
||||||
if (producer_it == producer_index.end()) {
|
|
||||||
if (input_seen.insert(tensor).second) {
|
|
||||||
Segment::InputRef input_ref;
|
|
||||||
input_ref.type = is_params_tensor(params_tensor_set, tensor) ? Segment::INPUT_PARAM : Segment::INPUT_EXTERNAL;
|
|
||||||
input_ref.display_name = graph_cut_tensor_display_name(tensor);
|
|
||||||
input_ref.leaf_index = graph_leaf_index(gf, tensor);
|
|
||||||
input_refs.push_back(std::move(input_ref));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int node_idx = producer_it->second;
|
|
||||||
if (available_cut_output_node_indices.find(node_idx) != available_cut_output_node_indices.end()) {
|
|
||||||
if (input_seen.insert(tensor).second) {
|
|
||||||
Segment::InputRef input_ref;
|
|
||||||
input_ref.type = Segment::INPUT_PREVIOUS_CUT;
|
|
||||||
input_ref.display_name = graph_cut_tensor_display_name(tensor);
|
|
||||||
input_ref.node_index = node_idx;
|
|
||||||
input_refs.push_back(std::move(input_ref));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!internal_nodes.insert(node_idx).second) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* node = ggml_graph_node(gf, node_idx);
|
|
||||||
for (int src_idx = 0; src_idx < GGML_MAX_SRC; ++src_idx) {
|
|
||||||
if (node->src[src_idx] != nullptr) {
|
|
||||||
work_stack.push(node->src[src_idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!internal_nodes.empty()) {
|
|
||||||
segment.internal_node_indices.assign(internal_nodes.begin(), internal_nodes.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(input_refs.begin(),
|
|
||||||
input_refs.end(),
|
|
||||||
[](const Segment::InputRef& a, const Segment::InputRef& b) {
|
|
||||||
if (a.type != b.type) {
|
|
||||||
return a.type < b.type;
|
|
||||||
}
|
|
||||||
return a.display_name < b.display_name;
|
|
||||||
});
|
|
||||||
segment.input_refs = input_refs;
|
|
||||||
for (const auto& input : input_refs) {
|
|
||||||
ggml_tensor* current_input = input_tensor(gf, input);
|
|
||||||
size_t tensor_bytes = current_input == nullptr
|
|
||||||
? 0
|
|
||||||
: (input.type == Segment::INPUT_PREVIOUS_CUT
|
|
||||||
? cache_tensor_bytes(current_input)
|
|
||||||
: ggml_nbytes(current_input));
|
|
||||||
switch (input.type) {
|
|
||||||
case Segment::INPUT_PREVIOUS_CUT:
|
|
||||||
segment.input_previous_cut_bytes += tensor_bytes;
|
|
||||||
break;
|
|
||||||
case Segment::INPUT_PARAM:
|
|
||||||
segment.input_param_bytes += tensor_bytes;
|
|
||||||
break;
|
|
||||||
case Segment::INPUT_EXTERNAL:
|
|
||||||
default:
|
|
||||||
segment.input_external_bytes += tensor_bytes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int output_node_index : segment.output_node_indices) {
|
|
||||||
ggml_tensor* output = ggml_graph_node(gf, output_node_index);
|
|
||||||
segment.output_bytes += cache_tensor_bytes(output);
|
|
||||||
}
|
|
||||||
segment.compute_buffer_size = measure_segment_compute_buffer(backend, gf, segment, log_desc);
|
|
||||||
|
|
||||||
for (int output_node_index : segment.output_node_indices) {
|
|
||||||
available_cut_output_node_indices.insert(output_node_index);
|
|
||||||
}
|
|
||||||
plan.segments.push_back(std::move(segment));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_graph_cut_tensor(const ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr || tensor->name[0] == '\0') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return std::strncmp(tensor->name, GGML_RUNNER_CUT_PREFIX, std::strlen(GGML_RUNNER_CUT_PREFIX)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string make_graph_cut_name(const std::string& group, const std::string& output) {
|
|
||||||
return std::string(GGML_RUNNER_CUT_PREFIX) + group + "|" + output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mark_graph_cut(ggml_tensor* tensor, const std::string& group, const std::string& output) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto name = make_graph_cut_name(group, output);
|
|
||||||
ggml_set_name(tensor, name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int leaf_count(ggml_cgraph* gf) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
return gf->n_leafs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* leaf_tensor(ggml_cgraph* gf, int leaf_index) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (leaf_index < 0 || leaf_index >= gf->n_leafs) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return gf->leafs[leaf_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_backend_buffer_t tensor_buffer(const ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return tensor->view_src ? tensor->view_src->buffer : tensor->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* cache_source_tensor(ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (tensor_buffer(tensor) == nullptr && tensor->src[0] != nullptr &&
|
|
||||||
ggml_nelements(tensor->src[0]) == ggml_nelements(tensor) &&
|
|
||||||
ggml_nbytes(tensor->src[0]) == ggml_nbytes(tensor)) {
|
|
||||||
return cache_source_tensor(tensor->src[0]);
|
|
||||||
}
|
|
||||||
return tensor->view_src ? tensor->view_src : tensor;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t cache_tensor_bytes(const ggml_tensor* tensor) {
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const ggml_tensor* cache_src = tensor->view_src ? tensor->view_src : tensor;
|
|
||||||
return ggml_nbytes(cache_src);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool plan_matches_graph(ggml_cgraph* gf, const Plan& plan) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (ggml_graph_n_nodes(gf) != plan.n_nodes || gf->n_leafs != plan.n_leafs) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto& input_shape_ref : plan.input_shapes) {
|
|
||||||
if (input_shape_ref.leaf_index < 0 || input_shape_ref.leaf_index >= gf->n_leafs) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ggml_tensor* leaf = gf->leafs[input_shape_ref.leaf_index];
|
|
||||||
if (leaf == nullptr || input_shape_ref.type != leaf->type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int d = 0; d < GGML_MAX_DIMS; ++d) {
|
|
||||||
if (input_shape_ref.ne[static_cast<size_t>(d)] != leaf->ne[d]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* output_tensor(ggml_cgraph* gf, const Segment& segment, size_t output_index) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (output_index >= segment.output_node_indices.size()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
int node_index = segment.output_node_indices[output_index];
|
|
||||||
if (node_index < 0 || node_index >= ggml_graph_n_nodes(gf)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return ggml_graph_node(gf, node_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* input_tensor(ggml_cgraph* gf, const Segment::InputRef& input_ref) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (input_ref.type == Segment::INPUT_PREVIOUS_CUT) {
|
|
||||||
if (input_ref.node_index < 0 || input_ref.node_index >= ggml_graph_n_nodes(gf)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return ggml_graph_node(gf, input_ref.node_index);
|
|
||||||
}
|
|
||||||
if (input_ref.leaf_index < 0 || input_ref.leaf_index >= gf->n_leafs) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return leaf_tensor(gf, input_ref.leaf_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ggml_tensor*> param_tensors(ggml_cgraph* gf, const Segment& segment) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
std::vector<ggml_tensor*> tensors;
|
|
||||||
std::unordered_set<ggml_tensor*> seen_tensors;
|
|
||||||
tensors.reserve(segment.input_refs.size());
|
|
||||||
seen_tensors.reserve(segment.input_refs.size());
|
|
||||||
for (const auto& input_ref : segment.input_refs) {
|
|
||||||
if (input_ref.type != Segment::INPUT_PARAM) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_tensor* tensor = input_tensor(gf, input_ref);
|
|
||||||
if (tensor == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (seen_tensors.insert(tensor).second) {
|
|
||||||
tensors.push_back(tensor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
std::unordered_set<std::string> future_input_names;
|
|
||||||
for (size_t seg_idx = current_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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_tensor* current_input = input_tensor(gf, input_ref);
|
|
||||||
if (current_input != nullptr && current_input->name[0] != '\0') {
|
|
||||||
future_input_names.insert(current_input->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return future_input_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_segment_graph(ggml_cgraph* gf,
|
|
||||||
const Segment& segment,
|
|
||||||
ggml_context** graph_ctx_out) {
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
GGML_ASSERT(graph_ctx_out != nullptr);
|
|
||||||
|
|
||||||
const size_t graph_size = segment.internal_node_indices.size() + segment.input_refs.size() + 8;
|
|
||||||
ggml_init_params params = {
|
|
||||||
/*.mem_size =*/ggml_graph_overhead_custom(graph_size, false) + 1024,
|
|
||||||
/*.mem_buffer =*/nullptr,
|
|
||||||
/*.no_alloc =*/true,
|
|
||||||
};
|
|
||||||
ggml_context* graph_ctx = ggml_init(params);
|
|
||||||
GGML_ASSERT(graph_ctx != nullptr);
|
|
||||||
ggml_cgraph* segment_graph = ggml_new_graph_custom(graph_ctx, graph_size, false);
|
|
||||||
GGML_ASSERT(segment_graph != nullptr);
|
|
||||||
|
|
||||||
for (const auto& input : segment.input_refs) {
|
|
||||||
ggml_tensor* current_input = input_tensor(gf, input);
|
|
||||||
if (current_input == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GGML_ASSERT(segment_graph->n_leafs < segment_graph->size);
|
|
||||||
segment_graph->leafs[segment_graph->n_leafs++] = current_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int output_node_index : segment.output_node_indices) {
|
|
||||||
ggml_tensor* output = ggml_graph_node(gf, output_node_index);
|
|
||||||
if (output == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ggml_set_output(output);
|
|
||||||
}
|
|
||||||
for (int node_idx : segment.internal_node_indices) {
|
|
||||||
ggml_graph_add_node(segment_graph, ggml_graph_node(gf, node_idx));
|
|
||||||
}
|
|
||||||
*graph_ctx_out = graph_ctx;
|
|
||||||
return segment_graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t measure_segment_compute_buffer(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
const Segment& segment,
|
|
||||||
const char* log_desc) {
|
|
||||||
GGML_ASSERT(backend != nullptr);
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
if (segment.internal_node_indices.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
size_t sizes[1] = {0};
|
|
||||||
ggml_gallocr_reserve_n_size(
|
|
||||||
allocr,
|
|
||||||
segment_graph,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
sizes);
|
|
||||||
size_t buffer_size = sizes[0];
|
|
||||||
|
|
||||||
ggml_gallocr_free(allocr);
|
|
||||||
ggml_free(graph_ctx);
|
|
||||||
return buffer_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan build_plan(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc) {
|
|
||||||
GGML_ASSERT(backend != nullptr);
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
Plan plan;
|
|
||||||
plan.available = true;
|
|
||||||
const int n_nodes = ggml_graph_n_nodes(gf);
|
|
||||||
if (n_nodes <= 0) {
|
|
||||||
return plan;
|
|
||||||
}
|
|
||||||
plan.n_nodes = n_nodes;
|
|
||||||
plan.n_leafs = gf->n_leafs;
|
|
||||||
for (int i = 0; i < gf->n_leafs; ++i) {
|
|
||||||
ggml_tensor* leaf = gf->leafs[i];
|
|
||||||
if (is_params_tensor(params_tensor_set, leaf)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto shape = input_shape(leaf);
|
|
||||||
shape.leaf_index = i;
|
|
||||||
plan.input_shapes.push_back(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<const ggml_tensor*, int> producer_index;
|
|
||||||
producer_index.reserve(static_cast<size_t>(n_nodes));
|
|
||||||
for (int i = 0; i < n_nodes; ++i) {
|
|
||||||
producer_index[ggml_graph_node(gf, i)] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Segment> grouped_segments;
|
|
||||||
std::unordered_map<std::string, size_t> group_to_segment;
|
|
||||||
for (int i = 0; i < n_nodes; ++i) {
|
|
||||||
ggml_tensor* node = ggml_graph_node(gf, i);
|
|
||||||
if (!is_graph_cut_tensor(node)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
plan.has_cuts = true;
|
|
||||||
std::string full_name(node->name);
|
|
||||||
std::string payload = full_name.substr(std::strlen(GGML_RUNNER_CUT_PREFIX));
|
|
||||||
size_t sep = payload.find('|');
|
|
||||||
std::string group = sep == std::string::npos ? payload : payload.substr(0, sep);
|
|
||||||
|
|
||||||
auto it = group_to_segment.find(group);
|
|
||||||
if (it == group_to_segment.end()) {
|
|
||||||
Segment segment;
|
|
||||||
segment.group_name = group;
|
|
||||||
segment.output_node_indices.push_back(i);
|
|
||||||
group_to_segment[group] = grouped_segments.size();
|
|
||||||
grouped_segments.push_back(std::move(segment));
|
|
||||||
} else {
|
|
||||||
auto& segment = grouped_segments[it->second];
|
|
||||||
segment.output_node_indices.push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!plan.has_cuts) {
|
|
||||||
return plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<int> available_cut_output_node_indices;
|
|
||||||
available_cut_output_node_indices.reserve(static_cast<size_t>(n_nodes));
|
|
||||||
for (auto& segment : grouped_segments) {
|
|
||||||
build_segment(gf,
|
|
||||||
plan,
|
|
||||||
segment,
|
|
||||||
producer_index,
|
|
||||||
available_cut_output_node_indices,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
int final_output_index = graph_node_index_by_name(gf, "ggml_runner_final_result_tensor");
|
|
||||||
if (final_output_index < 0) {
|
|
||||||
final_output_index = n_nodes - 1;
|
|
||||||
}
|
|
||||||
ggml_tensor* final_output = final_output_index >= 0 ? ggml_graph_node(gf, final_output_index) : nullptr;
|
|
||||||
if (final_output != nullptr && available_cut_output_node_indices.find(final_output_index) == available_cut_output_node_indices.end()) {
|
|
||||||
Segment final_segment;
|
|
||||||
final_segment.group_name = "ggml_runner.final";
|
|
||||||
final_segment.output_node_indices.push_back(final_output_index);
|
|
||||||
build_segment(gf,
|
|
||||||
plan,
|
|
||||||
final_segment,
|
|
||||||
producer_index,
|
|
||||||
available_cut_output_node_indices,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan apply_max_vram_budget(ggml_cgraph* gf,
|
|
||||||
const Plan& base_plan,
|
|
||||||
size_t max_graph_vram_bytes,
|
|
||||||
ggml_backend_t backend,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc) {
|
|
||||||
GGML_ASSERT(backend != nullptr);
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
int64_t t_budget_begin = ggml_time_ms();
|
|
||||||
if (max_graph_vram_bytes == 0 || !base_plan.has_cuts || base_plan.segments.size() <= 1) {
|
|
||||||
return base_plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int n_nodes = ggml_graph_n_nodes(gf);
|
|
||||||
std::unordered_map<const ggml_tensor*, int> producer_index;
|
|
||||||
producer_index.reserve(static_cast<size_t>(n_nodes));
|
|
||||||
for (int i = 0; i < n_nodes; ++i) {
|
|
||||||
producer_index[ggml_graph_node(gf, i)] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan merged_plan;
|
|
||||||
merged_plan.available = true;
|
|
||||||
merged_plan.has_cuts = base_plan.has_cuts;
|
|
||||||
merged_plan.valid = base_plan.valid;
|
|
||||||
merged_plan.n_nodes = base_plan.n_nodes;
|
|
||||||
merged_plan.n_leafs = base_plan.n_leafs;
|
|
||||||
|
|
||||||
std::unordered_set<int> available_cut_output_node_indices;
|
|
||||||
available_cut_output_node_indices.reserve(static_cast<size_t>(n_nodes));
|
|
||||||
|
|
||||||
size_t start_segment_index = 0;
|
|
||||||
while (start_segment_index < base_plan.segments.size()) {
|
|
||||||
Plan single_plan;
|
|
||||||
auto single_available_cut_output_node_indices = available_cut_output_node_indices;
|
|
||||||
auto single_seed = make_segment_seed(base_plan,
|
|
||||||
start_segment_index,
|
|
||||||
start_segment_index);
|
|
||||||
build_segment(gf,
|
|
||||||
single_plan,
|
|
||||||
single_seed,
|
|
||||||
producer_index,
|
|
||||||
single_available_cut_output_node_indices,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
GGML_ASSERT(!single_plan.segments.empty());
|
|
||||||
|
|
||||||
size_t best_end_segment_index = start_segment_index;
|
|
||||||
bool can_merge_next_segment = graph_cut_segment_vram_bytes(single_plan.segments.back()) <= max_graph_vram_bytes;
|
|
||||||
|
|
||||||
while (can_merge_next_segment && best_end_segment_index + 1 < base_plan.segments.size()) {
|
|
||||||
const size_t next_end_segment_index = best_end_segment_index + 1;
|
|
||||||
Plan candidate_plan;
|
|
||||||
auto candidate_available_cut_output_node_indices = available_cut_output_node_indices;
|
|
||||||
auto candidate_seed = make_segment_seed(base_plan,
|
|
||||||
start_segment_index,
|
|
||||||
next_end_segment_index);
|
|
||||||
build_segment(gf,
|
|
||||||
candidate_plan,
|
|
||||||
candidate_seed,
|
|
||||||
producer_index,
|
|
||||||
candidate_available_cut_output_node_indices,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
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) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
best_end_segment_index = next_end_segment_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto best_seed = make_segment_seed(base_plan,
|
|
||||||
start_segment_index,
|
|
||||||
best_end_segment_index);
|
|
||||||
build_segment(gf,
|
|
||||||
merged_plan,
|
|
||||||
best_seed,
|
|
||||||
producer_index,
|
|
||||||
available_cut_output_node_indices,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
start_segment_index = best_end_segment_index + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log_desc != nullptr && merged_plan.segments.size() != base_plan.segments.size()) {
|
|
||||||
LOG_INFO("%s graph cut max_vram=%.2f MB merged %zu segments -> %zu segments",
|
|
||||||
log_desc,
|
|
||||||
max_graph_vram_bytes / 1024.0 / 1024.0,
|
|
||||||
base_plan.segments.size(),
|
|
||||||
merged_plan.segments.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (log_desc != nullptr) {
|
|
||||||
LOG_DEBUG("%s graph cut max_vram budget merge took %lld ms",
|
|
||||||
log_desc,
|
|
||||||
ggml_time_ms() - t_budget_begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged_plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan resolve_plan(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
PlanCache* cache,
|
|
||||||
size_t max_graph_vram_bytes,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc) {
|
|
||||||
GGML_ASSERT(backend != nullptr);
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
|
||||||
GGML_ASSERT(cache != nullptr);
|
|
||||||
|
|
||||||
int64_t t_prepare_begin = ggml_time_ms();
|
|
||||||
Plan base_plan;
|
|
||||||
int64_t t_plan_begin = ggml_time_ms();
|
|
||||||
if (cache->graph_cut_plan.available && plan_matches_graph(gf, cache->graph_cut_plan)) {
|
|
||||||
base_plan = cache->graph_cut_plan;
|
|
||||||
} else {
|
|
||||||
base_plan = build_plan(backend, gf, params_tensor_set, log_desc);
|
|
||||||
cache->graph_cut_plan = base_plan;
|
|
||||||
cache->graph_cut_plan.available = true;
|
|
||||||
cache->budgeted_graph_cut_plan.available = false;
|
|
||||||
if (log_desc != nullptr) {
|
|
||||||
LOG_INFO("%s build cached graph cut plan done (taking %lld ms)", log_desc, ggml_time_ms() - t_plan_begin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Plan resolved_plan = base_plan;
|
|
||||||
if (max_graph_vram_bytes > 0 && base_plan.has_cuts) {
|
|
||||||
if (cache->budgeted_graph_cut_plan.available &&
|
|
||||||
cache->budgeted_graph_cut_plan_max_vram_bytes == max_graph_vram_bytes &&
|
|
||||||
plan_matches_graph(gf, cache->budgeted_graph_cut_plan)) {
|
|
||||||
resolved_plan = cache->budgeted_graph_cut_plan;
|
|
||||||
} else {
|
|
||||||
resolved_plan = apply_max_vram_budget(gf,
|
|
||||||
base_plan,
|
|
||||||
max_graph_vram_bytes,
|
|
||||||
backend,
|
|
||||||
params_tensor_set,
|
|
||||||
log_desc);
|
|
||||||
cache->budgeted_graph_cut_plan = resolved_plan;
|
|
||||||
cache->budgeted_graph_cut_plan.available = true;
|
|
||||||
cache->budgeted_graph_cut_plan_max_vram_bytes = max_graph_vram_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resolved_plan;
|
|
||||||
}
|
|
||||||
|
|
||||||
void annotate_residency(Plan& plan, size_t max_graph_vram_bytes) {
|
|
||||||
// Cached plans may be reused with a smaller live budget.
|
|
||||||
for (auto& seg : plan.segments) {
|
|
||||||
seg.residency = SegmentResidency::STREAMED;
|
|
||||||
}
|
|
||||||
if (max_graph_vram_bytes == 0 || plan.segments.size() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool any_param_bearing = false;
|
|
||||||
for (const auto& seg : plan.segments) {
|
|
||||||
if (seg.input_param_bytes > 0) {
|
|
||||||
any_param_bearing = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!any_param_bearing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave room for the largest active streamed segment.
|
|
||||||
size_t worst_streamed_footprint = 0;
|
|
||||||
for (const auto& seg : plan.segments) {
|
|
||||||
const size_t seg_footprint = seg.input_param_bytes +
|
|
||||||
seg.compute_buffer_size +
|
|
||||||
seg.output_bytes +
|
|
||||||
seg.input_previous_cut_bytes +
|
|
||||||
seg.input_external_bytes;
|
|
||||||
if (seg_footprint > worst_streamed_footprint) {
|
|
||||||
worst_streamed_footprint = seg_footprint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
constexpr size_t safety = 512ull * 1024 * 1024;
|
|
||||||
const size_t reserved = safety + worst_streamed_footprint;
|
|
||||||
|
|
||||||
if (max_graph_vram_bytes <= reserved) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const size_t available = max_graph_vram_bytes - reserved;
|
|
||||||
|
|
||||||
size_t cumulative = 0;
|
|
||||||
for (auto& seg : plan.segments) {
|
|
||||||
if (cumulative + seg.input_param_bytes > available) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
seg.residency = SegmentResidency::RESIDENT;
|
|
||||||
cumulative += seg.input_param_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sd::ggml_graph_cut
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
#ifndef __SD_CORE_GGML_GRAPH_CUT_H__
|
|
||||||
#define __SD_CORE_GGML_GRAPH_CUT_H__
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "ggml-backend.h"
|
|
||||||
#include "ggml.h"
|
|
||||||
|
|
||||||
namespace sd::ggml_graph_cut {
|
|
||||||
|
|
||||||
// Streaming residency for a segment's params.
|
|
||||||
enum class SegmentResidency : uint8_t {
|
|
||||||
STREAMED = 0,
|
|
||||||
RESIDENT = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Segment {
|
|
||||||
enum InputType {
|
|
||||||
INPUT_EXTERNAL = 0,
|
|
||||||
INPUT_PREVIOUS_CUT,
|
|
||||||
INPUT_PARAM,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputRef {
|
|
||||||
InputType type = INPUT_EXTERNAL;
|
|
||||||
std::string display_name;
|
|
||||||
int leaf_index = -1;
|
|
||||||
int node_index = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t compute_buffer_size = 0;
|
|
||||||
size_t output_bytes = 0;
|
|
||||||
size_t input_external_bytes = 0;
|
|
||||||
size_t input_previous_cut_bytes = 0;
|
|
||||||
size_t input_param_bytes = 0;
|
|
||||||
std::string group_name;
|
|
||||||
std::vector<int> internal_node_indices;
|
|
||||||
std::vector<int> output_node_indices;
|
|
||||||
std::vector<InputRef> input_refs;
|
|
||||||
SegmentResidency residency = SegmentResidency::STREAMED;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Plan {
|
|
||||||
struct InputShape {
|
|
||||||
int leaf_index = -1;
|
|
||||||
ggml_type type = GGML_TYPE_COUNT;
|
|
||||||
std::array<int64_t, GGML_MAX_DIMS> ne = {0, 0, 0, 0};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool available = false;
|
|
||||||
bool has_cuts = false;
|
|
||||||
bool valid = true;
|
|
||||||
int n_nodes = 0;
|
|
||||||
int n_leafs = 0;
|
|
||||||
std::vector<InputShape> input_shapes;
|
|
||||||
std::vector<Segment> segments;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlanCache {
|
|
||||||
Plan graph_cut_plan;
|
|
||||||
Plan budgeted_graph_cut_plan;
|
|
||||||
size_t budgeted_graph_cut_plan_max_vram_bytes = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr const char* GGML_RUNNER_CUT_PREFIX = "ggml_runner_cut:";
|
|
||||||
|
|
||||||
bool is_graph_cut_tensor(const ggml_tensor* tensor);
|
|
||||||
std::string make_graph_cut_name(const std::string& group, const std::string& output);
|
|
||||||
void mark_graph_cut(ggml_tensor* tensor, const std::string& group, const std::string& output);
|
|
||||||
int leaf_count(ggml_cgraph* gf);
|
|
||||||
ggml_tensor* leaf_tensor(ggml_cgraph* gf, int leaf_index);
|
|
||||||
ggml_backend_buffer_t tensor_buffer(const ggml_tensor* tensor);
|
|
||||||
ggml_tensor* cache_source_tensor(ggml_tensor* tensor);
|
|
||||||
size_t cache_tensor_bytes(const ggml_tensor* tensor);
|
|
||||||
bool plan_matches_graph(ggml_cgraph* gf, const Plan& plan);
|
|
||||||
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);
|
|
||||||
ggml_cgraph* build_segment_graph(ggml_cgraph* gf,
|
|
||||||
const Segment& segment,
|
|
||||||
ggml_context** graph_ctx_out);
|
|
||||||
size_t measure_segment_compute_buffer(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
const Segment& segment,
|
|
||||||
const char* log_desc);
|
|
||||||
size_t max_vram_gib_to_bytes(float max_vram);
|
|
||||||
float resolve_max_vram_gib(float max_vram, ggml_backend_t backend);
|
|
||||||
Plan build_plan(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc);
|
|
||||||
Plan apply_max_vram_budget(ggml_cgraph* gf,
|
|
||||||
const Plan& base_plan,
|
|
||||||
size_t max_graph_vram_bytes,
|
|
||||||
ggml_backend_t backend,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc);
|
|
||||||
Plan resolve_plan(ggml_backend_t backend,
|
|
||||||
ggml_cgraph* gf,
|
|
||||||
PlanCache* cache,
|
|
||||||
size_t max_graph_vram_bytes,
|
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
|
||||||
const char* log_desc);
|
|
||||||
|
|
||||||
// Mark leading segments resident when they fit after streamed-segment headroom.
|
|
||||||
void annotate_residency(Plan& plan, size_t max_graph_vram_bytes);
|
|
||||||
} // namespace sd::ggml_graph_cut
|
|
||||||
|
|
||||||
#endif // __SD_CORE_GGML_GRAPH_CUT_H__
|
|
||||||
@ -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__
|
|
||||||
@ -1,17 +1,12 @@
|
|||||||
#ifndef __SD_RUNTIME_DENOISER_HPP__
|
#ifndef __DENOISER_HPP__
|
||||||
#define __SD_RUNTIME_DENOISER_HPP__
|
#define __DENOISER_HPP__
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "core/tensor.hpp"
|
#include "gits_noise.inl"
|
||||||
#include "runtime/gits_noise.h"
|
#include "tensor.hpp"
|
||||||
#include "runtime/guidance.h"
|
|
||||||
|
|
||||||
/*================================================= CompVisDenoiser ==================================================*/
|
/*================================================= CompVisDenoiser ==================================================*/
|
||||||
|
|
||||||
@ -484,81 +479,6 @@ struct KLOptimalScheduler : SigmaScheduler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LTX2Scheduler : SigmaScheduler {
|
|
||||||
int token_count = 4096;
|
|
||||||
float max_shift = 2.05f;
|
|
||||||
float base_shift = 0.95f;
|
|
||||||
bool stretch = true;
|
|
||||||
float terminal = 0.1f;
|
|
||||||
|
|
||||||
explicit LTX2Scheduler(int token_count, const char* extra_sample_args = nullptr)
|
|
||||||
: token_count(token_count > 0 ? token_count : 4096) {
|
|
||||||
parse_extra_sample_args(extra_sample_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_extra_sample_args(const char* extra_sample_args) {
|
|
||||||
for (const auto& [key, value] : parse_key_value_args(extra_sample_args, "ltx2 scheduler arg")) {
|
|
||||||
if (key == "max_shift") {
|
|
||||||
if (!parse_strict_float(value, max_shift)) {
|
|
||||||
LOG_WARN("ignoring invalid ltx2 scheduler arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
} else if (key == "base_shift") {
|
|
||||||
if (!parse_strict_float(value, base_shift)) {
|
|
||||||
LOG_WARN("ignoring invalid ltx2 scheduler arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
} else if (key == "terminal") {
|
|
||||||
if (!parse_strict_float(value, terminal)) {
|
|
||||||
LOG_WARN("ignoring invalid ltx2 scheduler arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
} else if (key == "stretch") {
|
|
||||||
if (!parse_strict_bool(value, stretch)) {
|
|
||||||
LOG_WARN("ignoring invalid ltx2 scheduler arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> get_sigmas(uint32_t n, float /*sigma_min*/, float /*sigma_max*/, t_to_sigma_t /*t_to_sigma*/) override {
|
|
||||||
std::vector<float> sigmas;
|
|
||||||
if (n == 0) {
|
|
||||||
sigmas.push_back(0.0f);
|
|
||||||
return sigmas;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr float base_shift_anchor = 1024.0f;
|
|
||||||
constexpr float max_shift_anchor = 4096.0f;
|
|
||||||
float m = (max_shift - base_shift) / (max_shift_anchor - base_shift_anchor);
|
|
||||||
float b = base_shift - m * base_shift_anchor;
|
|
||||||
float sigma_shift = static_cast<float>(token_count) * m + b;
|
|
||||||
float exp_shift = std::exp(sigma_shift);
|
|
||||||
float target_terminal = std::clamp(terminal, 0.0f, 0.99f);
|
|
||||||
|
|
||||||
LOG_DEBUG("LTX2 scheduler: tokens=%d, shift=%.4f, stretch=%d, terminal=%.4f", token_count, sigma_shift, stretch ? 1 : 0, target_terminal);
|
|
||||||
|
|
||||||
sigmas.reserve(n + 1);
|
|
||||||
for (uint32_t i = 0; i <= n; ++i) {
|
|
||||||
float sigma = 1.0f - static_cast<float>(i) / static_cast<float>(n);
|
|
||||||
if (sigma != 0.0f) {
|
|
||||||
sigma = exp_shift / (exp_shift + (1.0f / sigma - 1.0f));
|
|
||||||
}
|
|
||||||
sigmas.push_back(sigma);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stretch && sigmas.size() > 2) {
|
|
||||||
float one_minus_last = 1.0f - sigmas[n - 1];
|
|
||||||
float scale_factor = one_minus_last / (1.0f - target_terminal);
|
|
||||||
if (scale_factor > 1e-8f) {
|
|
||||||
for (uint32_t i = 0; i < n; ++i) {
|
|
||||||
sigmas[i] = 1.0f - (1.0f - sigmas[i]) / scale_factor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sigmas[n] = 0.0f;
|
|
||||||
return sigmas;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Denoiser {
|
struct Denoiser {
|
||||||
virtual float sigma_min() = 0;
|
virtual float sigma_min() = 0;
|
||||||
virtual float sigma_max() = 0;
|
virtual float sigma_max() = 0;
|
||||||
@ -571,7 +491,7 @@ struct Denoiser {
|
|||||||
virtual sd::Tensor<float> inverse_noise_scaling(float sigma,
|
virtual sd::Tensor<float> inverse_noise_scaling(float sigma,
|
||||||
const sd::Tensor<float>& latent) = 0;
|
const sd::Tensor<float>& latent) = 0;
|
||||||
|
|
||||||
virtual std::vector<float> get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version, const char* extra_sample_args = nullptr) {
|
virtual std::vector<float> get_sigmas(uint32_t n, int /*image_seq_len*/, scheduler_t scheduler_type, SDVersion version) {
|
||||||
auto bound_t_to_sigma = std::bind(&Denoiser::t_to_sigma, this, std::placeholders::_1);
|
auto bound_t_to_sigma = std::bind(&Denoiser::t_to_sigma, this, std::placeholders::_1);
|
||||||
std::shared_ptr<SigmaScheduler> scheduler;
|
std::shared_ptr<SigmaScheduler> scheduler;
|
||||||
switch (scheduler_type) {
|
switch (scheduler_type) {
|
||||||
@ -619,10 +539,6 @@ struct Denoiser {
|
|||||||
LOG_INFO("get_sigmas with LCM scheduler");
|
LOG_INFO("get_sigmas with LCM scheduler");
|
||||||
scheduler = std::make_shared<LCMScheduler>();
|
scheduler = std::make_shared<LCMScheduler>();
|
||||||
break;
|
break;
|
||||||
case LTX2_SCHEDULER:
|
|
||||||
LOG_INFO("get_sigmas with LTX2 scheduler");
|
|
||||||
scheduler = std::make_shared<LTX2Scheduler>(image_seq_len, extra_sample_args);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
LOG_INFO("get_sigmas with discrete scheduler (default)");
|
LOG_INFO("get_sigmas with discrete scheduler (default)");
|
||||||
scheduler = std::make_shared<DiscreteScheduler>();
|
scheduler = std::make_shared<DiscreteScheduler>();
|
||||||
@ -828,15 +744,15 @@ struct Flux2FlowDenoiser : public FluxFlowDenoiser {
|
|||||||
return mu;
|
return mu;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version, const char* extra_sample_args = nullptr) override {
|
std::vector<float> get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version) override {
|
||||||
float mu = compute_empirical_mu(n, image_seq_len);
|
float mu = compute_empirical_mu(n, image_seq_len);
|
||||||
LOG_DEBUG("Flux2FlowDenoiser: set shift to %.3f", mu);
|
LOG_DEBUG("Flux2FlowDenoiser: set shift to %.3f", mu);
|
||||||
set_shift(mu);
|
set_shift(mu);
|
||||||
return Denoiser::get_sigmas(n, image_seq_len, scheduler_type, version, extra_sample_args);
|
return Denoiser::get_sigmas(n, image_seq_len, scheduler_type, version);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<sd::guidance::GuiderOutput(const sd::Tensor<float>&, float, int)> denoise_cb_t;
|
typedef std::function<sd::Tensor<float>(const sd::Tensor<float>&, float, int)> denoise_cb_t;
|
||||||
|
|
||||||
static std::pair<float, float> get_ancestral_step(float sigma_from,
|
static std::pair<float, float> get_ancestral_step(float sigma_from,
|
||||||
float sigma_to,
|
float sigma_to,
|
||||||
@ -892,49 +808,49 @@ static std::tuple<float, float, float> get_ancestral_step_flow(float sigma_from,
|
|||||||
return {sigma_down, sigma_up, alpha_scale};
|
return {sigma_down, sigma_up, alpha_scale};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::tuple<float, float, float> get_ancestral_step(float sigma_from,
|
|
||||||
float sigma_to,
|
|
||||||
float eta,
|
|
||||||
bool is_flow_denoiser) {
|
|
||||||
if (is_flow_denoiser) {
|
|
||||||
return get_ancestral_step_flow(sigma_from, sigma_to, eta);
|
|
||||||
} else {
|
|
||||||
auto [sigma_down, sigma_up] = get_ancestral_step(sigma_from, sigma_to, eta);
|
|
||||||
return {sigma_down, sigma_up, 1.0f};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd::Tensor<float> sample_euler_ancestral(denoise_cb_t model,
|
static sd::Tensor<float> sample_euler_ancestral(denoise_cb_t model,
|
||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas,
|
const std::vector<float>& sigmas,
|
||||||
std::shared_ptr<RNG> rng = nullptr,
|
std::shared_ptr<RNG> rng,
|
||||||
bool is_flow_denoiser = false,
|
float eta) {
|
||||||
float eta = 0.f) {
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
float sigma = sigmas[i];
|
float sigma = sigmas[i];
|
||||||
float sigma_to = sigmas[i + 1];
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
auto denoised_opt = model(x, sigma, i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
if (sigma_to == 0.f) {
|
sd::Tensor<float> d = (x - denoised) / sigma;
|
||||||
x = denoised;
|
auto [sigma_down, sigma_up] = get_ancestral_step(sigmas[i], sigmas[i + 1], eta);
|
||||||
} else if (eta == 0.f) {
|
x += d * (sigma_down - sigmas[i]);
|
||||||
float sigma_ratio = sigma_to / sigma;
|
if (sigmas[i + 1] > 0) {
|
||||||
x = sigma_ratio * x + (1.0 - sigma_ratio) * denoised;
|
|
||||||
} else {
|
|
||||||
auto [sigma_down, sigma_up, alpha_scale] = get_ancestral_step(sigma, sigma_to, eta, is_flow_denoiser);
|
|
||||||
float sigma_ratio = sigma_down / sigma;
|
|
||||||
x = sigma_ratio * x + (1.0f - sigma_ratio) * denoised;
|
|
||||||
if (sigma_up > 0.f) {
|
|
||||||
if (is_flow_denoiser) {
|
|
||||||
x *= alpha_scale;
|
|
||||||
}
|
|
||||||
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static sd::Tensor<float> sample_euler_flow(denoise_cb_t model,
|
||||||
|
sd::Tensor<float> x,
|
||||||
|
const std::vector<float>& sigmas,
|
||||||
|
std::shared_ptr<RNG> rng,
|
||||||
|
float eta) {
|
||||||
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
|
for (int i = 0; i < steps; i++) {
|
||||||
|
float sigma = sigmas[i];
|
||||||
|
auto denoised_opt = model(x, sigma, i + 1);
|
||||||
|
if (denoised_opt.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
auto [sigma_down, sigma_up, alpha_scale] = get_ancestral_step_flow(sigma, sigmas[i + 1], eta);
|
||||||
|
float sigma_ratio = sigma_down / sigma;
|
||||||
|
x = sigma_ratio * x + (1.0f - sigma_ratio) * denoised;
|
||||||
|
|
||||||
|
if (sigma_up > 0.0f) {
|
||||||
|
x = alpha_scale * x + sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -946,10 +862,10 @@ static sd::Tensor<float> sample_euler(denoise_cb_t model,
|
|||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
float sigma = sigmas[i];
|
float sigma = sigmas[i];
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
auto denoised_opt = model(x, sigma, i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
sd::Tensor<float> d = (x - denoised) / sigma;
|
sd::Tensor<float> d = (x - denoised) / sigma;
|
||||||
x += d * (sigmas[i + 1] - sigma);
|
x += d * (sigmas[i + 1] - sigma);
|
||||||
}
|
}
|
||||||
@ -962,10 +878,10 @@ static sd::Tensor<float> sample_heun(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
sd::Tensor<float> d = (x - denoised) / sigmas[i];
|
sd::Tensor<float> d = (x - denoised) / sigmas[i];
|
||||||
float dt = sigmas[i + 1] - sigmas[i];
|
float dt = sigmas[i + 1] - sigmas[i];
|
||||||
if (sigmas[i + 1] == 0) {
|
if (sigmas[i + 1] == 0) {
|
||||||
@ -973,10 +889,10 @@ static sd::Tensor<float> sample_heun(denoise_cb_t model,
|
|||||||
} else {
|
} else {
|
||||||
sd::Tensor<float> x2 = x + d * dt;
|
sd::Tensor<float> x2 = x + d * dt;
|
||||||
auto denoised2_opt = model(x2, sigmas[i + 1], i + 1);
|
auto denoised2_opt = model(x2, sigmas[i + 1], i + 1);
|
||||||
if (denoised2_opt.pred.empty()) {
|
if (denoised2_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised2 = std::move(denoised2_opt.pred);
|
sd::Tensor<float> denoised2 = std::move(denoised2_opt);
|
||||||
d = (d + (x2 - denoised2) / sigmas[i + 1]) / 2.0f;
|
d = (d + (x2 - denoised2) / sigmas[i + 1]) / 2.0f;
|
||||||
x += d * dt;
|
x += d * dt;
|
||||||
}
|
}
|
||||||
@ -990,10 +906,10 @@ static sd::Tensor<float> sample_dpm2(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
sd::Tensor<float> d = (x - denoised) / sigmas[i];
|
sd::Tensor<float> d = (x - denoised) / sigmas[i];
|
||||||
if (sigmas[i + 1] == 0) {
|
if (sigmas[i + 1] == 0) {
|
||||||
x += d * (sigmas[i + 1] - sigmas[i]);
|
x += d * (sigmas[i + 1] - sigmas[i]);
|
||||||
@ -1003,10 +919,10 @@ static sd::Tensor<float> sample_dpm2(denoise_cb_t model,
|
|||||||
float dt_2 = sigmas[i + 1] - sigmas[i];
|
float dt_2 = sigmas[i + 1] - sigmas[i];
|
||||||
sd::Tensor<float> x2 = x + d * dt_1;
|
sd::Tensor<float> x2 = x + d * dt_1;
|
||||||
auto denoised2_opt = model(x2, sigma_mid, i + 1);
|
auto denoised2_opt = model(x2, sigma_mid, i + 1);
|
||||||
if (denoised2_opt.pred.empty()) {
|
if (denoised2_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised2 = std::move(denoised2_opt.pred);
|
sd::Tensor<float> denoised2 = std::move(denoised2_opt);
|
||||||
x += ((x2 - denoised2) / sigma_mid) * dt_2;
|
x += ((x2 - denoised2) / sigma_mid) * dt_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1024,10 +940,10 @@ static sd::Tensor<float> sample_dpmpp_2s_ancestral(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
auto denoised_opt = model(x, sigmas[i], -(i + 1));
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
auto [sigma_down, sigma_up] = get_ancestral_step(sigmas[i], sigmas[i + 1], eta);
|
auto [sigma_down, sigma_up] = get_ancestral_step(sigmas[i], sigmas[i + 1], eta);
|
||||||
|
|
||||||
if (sigma_down == 0) {
|
if (sigma_down == 0) {
|
||||||
@ -1040,10 +956,10 @@ static sd::Tensor<float> sample_dpmpp_2s_ancestral(denoise_cb_t model,
|
|||||||
float sigma_s = sigma_fn(s);
|
float sigma_s = sigma_fn(s);
|
||||||
sd::Tensor<float> x2 = (sigma_s / sigma_fn(t)) * x - (exp(-h * 0.5f) - 1) * denoised;
|
sd::Tensor<float> x2 = (sigma_s / sigma_fn(t)) * x - (exp(-h * 0.5f) - 1) * denoised;
|
||||||
auto denoised2_opt = model(x2, sigma_s, i + 1);
|
auto denoised2_opt = model(x2, sigma_s, i + 1);
|
||||||
if (denoised2_opt.pred.empty()) {
|
if (denoised2_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised2 = std::move(denoised2_opt.pred);
|
sd::Tensor<float> denoised2 = std::move(denoised2_opt);
|
||||||
x = (sigma_fn(t_next) / sigma_fn(t)) * x - (exp(-h) - 1) * denoised2;
|
x = (sigma_fn(t_next) / sigma_fn(t)) * x - (exp(-h) - 1) * denoised2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1067,10 +983,10 @@ static sd::Tensor<float> sample_dpmpp_2s_ancestral_flow(denoise_cb_t model,
|
|||||||
bool opt_first_step = (1.0 - sigma < 1e-6);
|
bool opt_first_step = (1.0 - sigma < 1e-6);
|
||||||
|
|
||||||
auto denoised_opt = model(x, sigma, (opt_first_step ? 1 : -1) * (i + 1));
|
auto denoised_opt = model(x, sigma, (opt_first_step ? 1 : -1) * (i + 1));
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
|
||||||
if (sigma_to == 0.0f) {
|
if (sigma_to == 0.0f) {
|
||||||
// Euler method (final step, no noise)
|
// Euler method (final step, no noise)
|
||||||
@ -1130,10 +1046,10 @@ static sd::Tensor<float> sample_dpmpp_2s_ancestral_flow(denoise_cb_t model,
|
|||||||
sd::Tensor<float> u = (x * sigma_s_i_ratio) + (denoised * (1.0f - sigma_s_i_ratio));
|
sd::Tensor<float> u = (x * sigma_s_i_ratio) + (denoised * (1.0f - sigma_s_i_ratio));
|
||||||
|
|
||||||
auto denoised2_opt = model(u, sigma_s, i + 1);
|
auto denoised2_opt = model(u, sigma_s, i + 1);
|
||||||
if (denoised2_opt.pred.empty()) {
|
if (denoised2_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
D_i = std::move(denoised2_opt.pred);
|
D_i = std::move(denoised2_opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
float sigma_down_i_ratio = sigma_down / sigma;
|
float sigma_down_i_ratio = sigma_down / sigma;
|
||||||
@ -1148,6 +1064,8 @@ static sd::Tensor<float> sample_dpmpp_2s_ancestral_flow(denoise_cb_t model,
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static sd::Tensor<float> sample_dpmpp_2m(denoise_cb_t model,
|
static sd::Tensor<float> sample_dpmpp_2m(denoise_cb_t model,
|
||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas) {
|
const std::vector<float>& sigmas) {
|
||||||
@ -1157,10 +1075,10 @@ static sd::Tensor<float> sample_dpmpp_2m(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], i + 1);
|
auto denoised_opt = model(x, sigmas[i], i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
float t = t_fn(sigmas[i]);
|
float t = t_fn(sigmas[i]);
|
||||||
float t_next = t_fn(sigmas[i + 1]);
|
float t_next = t_fn(sigmas[i + 1]);
|
||||||
float h = t_next - t;
|
float h = t_next - t;
|
||||||
@ -1189,10 +1107,10 @@ static sd::Tensor<float> sample_dpmpp_2m_v2(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], i + 1);
|
auto denoised_opt = model(x, sigmas[i], i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
float t = t_fn(sigmas[i]);
|
float t = t_fn(sigmas[i]);
|
||||||
float t_next = t_fn(sigmas[i + 1]);
|
float t_next = t_fn(sigmas[i + 1]);
|
||||||
float h = t_next - t;
|
float h = t_next - t;
|
||||||
@ -1216,85 +1134,19 @@ static sd::Tensor<float> sample_dpmpp_2m_v2(denoise_cb_t model,
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
using SamplerExtraArgs = KeyValueArgs;
|
|
||||||
|
|
||||||
static sd::Tensor<float> sample_lcm(denoise_cb_t model,
|
static sd::Tensor<float> sample_lcm(denoise_cb_t model,
|
||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas,
|
const std::vector<float>& sigmas,
|
||||||
std::shared_ptr<RNG> rng,
|
std::shared_ptr<RNG> rng) {
|
||||||
bool is_flow_denoiser,
|
|
||||||
const SamplerExtraArgs& extra_sample_args) {
|
|
||||||
struct LCMSampleArgs {
|
|
||||||
float noise_clip_std = 0.0f;
|
|
||||||
float noise_scale_start = 1.0f;
|
|
||||||
float noise_scale_end = 1.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
LCMSampleArgs args;
|
|
||||||
bool noise_scale_end_was_set = false;
|
|
||||||
bool noise_scale_start_was_set = false;
|
|
||||||
|
|
||||||
for (const auto& [key, value] : extra_sample_args) {
|
|
||||||
float parsed = 0.0f;
|
|
||||||
if (key == "noise_clip_std") {
|
|
||||||
if (!parse_strict_float(value, parsed)) {
|
|
||||||
LOG_WARN("ignoring invalid lcm extra sample arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
args.noise_clip_std = parsed;
|
|
||||||
} else if (key == "noise_scale_start") {
|
|
||||||
if (!parse_strict_float(value, parsed)) {
|
|
||||||
LOG_WARN("ignoring invalid lcm extra sample arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
args.noise_scale_start = parsed;
|
|
||||||
noise_scale_start_was_set = true;
|
|
||||||
} else if (key == "noise_scale_end") {
|
|
||||||
if (!parse_strict_float(value, parsed)) {
|
|
||||||
LOG_WARN("ignoring invalid lcm extra sample arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
args.noise_scale_end = parsed;
|
|
||||||
noise_scale_end_was_set = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (noise_scale_start_was_set && !noise_scale_end_was_set) {
|
|
||||||
args.noise_scale_end = args.noise_scale_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], i + 1);
|
auto denoised_opt = model(x, sigmas[i], i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
x = std::move(denoised_opt.pred);
|
x = std::move(denoised_opt);
|
||||||
if (sigmas[i + 1] > 0) {
|
if (sigmas[i + 1] > 0) {
|
||||||
if (is_flow_denoiser) {
|
x += sd::Tensor<float>::randn_like(x, rng) * sigmas[i + 1];
|
||||||
x *= (1 - sigmas[i + 1]);
|
|
||||||
}
|
|
||||||
auto noise = sd::Tensor<float>::randn_like(x, rng);
|
|
||||||
if (args.noise_clip_std > 0.0f && noise.numel() > 0) {
|
|
||||||
double mean = 0.0;
|
|
||||||
for (int64_t j = 0; j < noise.numel(); ++j) {
|
|
||||||
mean += static_cast<double>(noise[j]);
|
|
||||||
}
|
|
||||||
mean /= static_cast<double>(noise.numel());
|
|
||||||
|
|
||||||
double variance = 0.0;
|
|
||||||
for (int64_t j = 0; j < noise.numel(); ++j) {
|
|
||||||
double centered = static_cast<double>(noise[j]) - mean;
|
|
||||||
variance += centered * centered;
|
|
||||||
}
|
|
||||||
variance /= static_cast<double>(noise.numel());
|
|
||||||
|
|
||||||
float clip_val = args.noise_clip_std * static_cast<float>(std::sqrt(variance));
|
|
||||||
noise = sd::ops::clamp(noise, -clip_val, clip_val);
|
|
||||||
}
|
|
||||||
float t = steps > 1 ? static_cast<float>(i) / static_cast<float>(steps - 1) : 0.0f;
|
|
||||||
float noise_scale = args.noise_scale_start + (args.noise_scale_end - args.noise_scale_start) * t;
|
|
||||||
x += noise * (sigmas[i + 1] * noise_scale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
@ -1312,10 +1164,10 @@ static sd::Tensor<float> sample_ipndm(denoise_cb_t model,
|
|||||||
float sigma_next = sigmas[i + 1];
|
float sigma_next = sigmas[i + 1];
|
||||||
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
auto denoised_opt = model(x, sigma, i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
|
||||||
sd::Tensor<float> d_cur = (x - denoised) / sigma;
|
sd::Tensor<float> d_cur = (x - denoised) / sigma;
|
||||||
int order = std::min(max_order, i + 1);
|
int order = std::min(max_order, i + 1);
|
||||||
@ -1356,10 +1208,10 @@ static sd::Tensor<float> sample_ipndm_v(denoise_cb_t model,
|
|||||||
float t_next = sigmas[i + 1];
|
float t_next = sigmas[i + 1];
|
||||||
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
auto denoised_opt = model(x, sigma, i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
|
||||||
sd::Tensor<float> d_cur = (x - denoised) / sigma;
|
sd::Tensor<float> d_cur = (x - denoised) / sigma;
|
||||||
int order = std::min(max_order, i + 1);
|
int order = std::min(max_order, i + 1);
|
||||||
@ -1393,7 +1245,6 @@ static sd::Tensor<float> sample_res_multistep(denoise_cb_t model,
|
|||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas,
|
const std::vector<float>& sigmas,
|
||||||
std::shared_ptr<RNG> rng,
|
std::shared_ptr<RNG> rng,
|
||||||
bool is_flow_denoiser,
|
|
||||||
float eta) {
|
float eta) {
|
||||||
sd::Tensor<float> old_denoised = x;
|
sd::Tensor<float> old_denoised = x;
|
||||||
bool have_old_sigma = false;
|
bool have_old_sigma = false;
|
||||||
@ -1418,15 +1269,14 @@ static sd::Tensor<float> sample_res_multistep(denoise_cb_t model,
|
|||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], i + 1);
|
auto denoised_opt = model(x, sigmas[i], i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
|
||||||
float sigma_from = sigmas[i];
|
float sigma_from = sigmas[i];
|
||||||
float sigma_to = sigmas[i + 1];
|
float sigma_to = sigmas[i + 1];
|
||||||
|
auto [sigma_down, sigma_up] = get_ancestral_step(sigma_from, sigma_to, eta);
|
||||||
auto [sigma_down, sigma_up, alpha_scale] = get_ancestral_step(sigma_from, sigma_to, eta, is_flow_denoiser);
|
|
||||||
|
|
||||||
if (sigma_down == 0.0f || !have_old_sigma) {
|
if (sigma_down == 0.0f || !have_old_sigma) {
|
||||||
x += ((x - denoised) / sigma_from) * (sigma_down - sigma_from);
|
x += ((x - denoised) / sigma_from) * (sigma_down - sigma_from);
|
||||||
@ -1453,10 +1303,7 @@ static sd::Tensor<float> sample_res_multistep(denoise_cb_t model,
|
|||||||
x = sigma_fn(h) * x + h * (b1 * denoised + b2 * old_denoised);
|
x = sigma_fn(h) * x + h * (b1 * denoised + b2 * old_denoised);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sigma_to > 0.0f && sigma_up > 0.0f) {
|
if (sigmas[i + 1] > 0 && sigma_up > 0.0f) {
|
||||||
if (is_flow_denoiser) {
|
|
||||||
x *= alpha_scale;
|
|
||||||
}
|
|
||||||
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1471,7 +1318,6 @@ static sd::Tensor<float> sample_res_2s(denoise_cb_t model,
|
|||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas,
|
const std::vector<float>& sigmas,
|
||||||
std::shared_ptr<RNG> rng,
|
std::shared_ptr<RNG> rng,
|
||||||
bool is_flow_denoiser,
|
|
||||||
float eta) {
|
float eta) {
|
||||||
const float c2 = 0.5f;
|
const float c2 = 0.5f;
|
||||||
auto t_fn = [](float sigma) -> float { return -logf(sigma); };
|
auto t_fn = [](float sigma) -> float { return -logf(sigma); };
|
||||||
@ -1495,12 +1341,12 @@ static sd::Tensor<float> sample_res_2s(denoise_cb_t model,
|
|||||||
float sigma_to = sigmas[i + 1];
|
float sigma_to = sigmas[i + 1];
|
||||||
|
|
||||||
auto denoised_opt = model(x, sigma_from, -(i + 1));
|
auto denoised_opt = model(x, sigma_from, -(i + 1));
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> denoised = std::move(denoised_opt);
|
||||||
|
|
||||||
auto [sigma_down, sigma_up, alpha_scale] = get_ancestral_step(sigma_from, sigma_to, eta, is_flow_denoiser);
|
auto [sigma_down, sigma_up] = get_ancestral_step(sigma_from, sigma_to, eta);
|
||||||
|
|
||||||
sd::Tensor<float> x0 = x;
|
sd::Tensor<float> x0 = x;
|
||||||
if (sigma_down == 0.0f || sigma_from == 0.0f) {
|
if (sigma_down == 0.0f || sigma_from == 0.0f) {
|
||||||
@ -1521,18 +1367,15 @@ static sd::Tensor<float> sample_res_2s(denoise_cb_t model,
|
|||||||
sd::Tensor<float> x2 = x0 + eps1 * (h * a21);
|
sd::Tensor<float> x2 = x0 + eps1 * (h * a21);
|
||||||
|
|
||||||
auto denoised2_opt = model(x2, sigma_c2, i + 1);
|
auto denoised2_opt = model(x2, sigma_c2, i + 1);
|
||||||
if (denoised2_opt.pred.empty()) {
|
if (denoised2_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised2 = std::move(denoised2_opt.pred);
|
sd::Tensor<float> denoised2 = std::move(denoised2_opt);
|
||||||
sd::Tensor<float> eps2 = denoised2 - x0;
|
sd::Tensor<float> eps2 = denoised2 - x0;
|
||||||
x = x0 + h * (b1 * eps1 + b2 * eps2);
|
x = x0 + h * (b1 * eps1 + b2 * eps2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sigma_to > 0.0f && sigma_up > 0.0f) {
|
if (sigmas[i + 1] > 0 && sigma_up > 0.0f) {
|
||||||
if (is_flow_denoiser) {
|
|
||||||
x *= alpha_scale;
|
|
||||||
}
|
|
||||||
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1597,11 +1440,10 @@ static sd::Tensor<float> sample_er_sde(denoise_cb_t model,
|
|||||||
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
auto denoised_opt = model(x, sigmas[i], i + 1);
|
sd::Tensor<float> denoised = model(x, sigmas[i], i + 1);
|
||||||
if (denoised_opt.pred.empty()) {
|
if (denoised.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
|
||||||
|
|
||||||
int stage_used = std::min(max_stage, i + 1);
|
int stage_used = std::min(max_stage, i + 1);
|
||||||
|
|
||||||
@ -1674,6 +1516,68 @@ static sd::Tensor<float> sample_er_sde(denoise_cb_t model,
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static sd::Tensor<float> sample_ddim_trailing(denoise_cb_t model,
|
||||||
|
sd::Tensor<float> x,
|
||||||
|
const std::vector<float>& sigmas,
|
||||||
|
std::shared_ptr<RNG> rng,
|
||||||
|
float eta) {
|
||||||
|
float beta_start = 0.00085f;
|
||||||
|
float beta_end = 0.0120f;
|
||||||
|
std::vector<double> alphas_cumprod(TIMESTEPS);
|
||||||
|
std::vector<double> compvis_sigmas(TIMESTEPS);
|
||||||
|
for (int i = 0; i < TIMESTEPS; i++) {
|
||||||
|
alphas_cumprod[i] =
|
||||||
|
(i == 0 ? 1.0f : alphas_cumprod[i - 1]) *
|
||||||
|
(1.0f -
|
||||||
|
std::pow(sqrtf(beta_start) +
|
||||||
|
(sqrtf(beta_end) - sqrtf(beta_start)) *
|
||||||
|
((float)i / (TIMESTEPS - 1)),
|
||||||
|
2));
|
||||||
|
compvis_sigmas[i] =
|
||||||
|
std::sqrt((1 - alphas_cumprod[i]) / alphas_cumprod[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
|
for (int i = 0; i < steps; i++) {
|
||||||
|
int timestep = static_cast<int>(roundf(TIMESTEPS - i * ((float)TIMESTEPS / steps))) - 1;
|
||||||
|
int prev_timestep = timestep - TIMESTEPS / steps;
|
||||||
|
float sigma = static_cast<float>(compvis_sigmas[timestep]);
|
||||||
|
if (i == 0) {
|
||||||
|
x *= std::sqrt(sigma * sigma + 1) / sigma;
|
||||||
|
} else {
|
||||||
|
x *= std::sqrt(sigma * sigma + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto model_output_opt = model(x, sigma, i + 1);
|
||||||
|
if (model_output_opt.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
sd::Tensor<float> model_output = std::move(model_output_opt);
|
||||||
|
model_output = (x - model_output) * (1.0f / sigma);
|
||||||
|
|
||||||
|
float alpha_prod_t = static_cast<float>(alphas_cumprod[timestep]);
|
||||||
|
float alpha_prod_t_prev = static_cast<float>(prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0]);
|
||||||
|
float beta_prod_t = 1.0f - alpha_prod_t;
|
||||||
|
|
||||||
|
sd::Tensor<float> pred_original_sample = ((x / std::sqrt(sigma * sigma + 1)) -
|
||||||
|
std::sqrt(beta_prod_t) * model_output) *
|
||||||
|
(1.0f / std::sqrt(alpha_prod_t));
|
||||||
|
|
||||||
|
float beta_prod_t_prev = 1.0f - alpha_prod_t_prev;
|
||||||
|
float variance = (beta_prod_t_prev / beta_prod_t) *
|
||||||
|
(1.0f - alpha_prod_t / alpha_prod_t_prev);
|
||||||
|
float std_dev_t = eta * std::sqrt(variance);
|
||||||
|
|
||||||
|
x = std::sqrt(alpha_prod_t_prev) * pred_original_sample +
|
||||||
|
std::sqrt(1.0f - alpha_prod_t_prev - std::pow(std_dev_t, 2)) * model_output;
|
||||||
|
|
||||||
|
if (eta > 0) {
|
||||||
|
x += std_dev_t * sd::Tensor<float>::randn_like(x, rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
static sd::Tensor<float> sample_tcd(denoise_cb_t model,
|
static sd::Tensor<float> sample_tcd(denoise_cb_t model,
|
||||||
sd::Tensor<float> x,
|
sd::Tensor<float> x,
|
||||||
const std::vector<float>& sigmas,
|
const std::vector<float>& sigmas,
|
||||||
@ -1695,152 +1599,43 @@ static sd::Tensor<float> sample_tcd(denoise_cb_t model,
|
|||||||
std::sqrt((1 - alphas_cumprod[i]) / alphas_cumprod[i]);
|
std::sqrt((1 - alphas_cumprod[i]) / alphas_cumprod[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto get_timestep_from_sigma = [&](float s) -> int {
|
int original_steps = 50;
|
||||||
auto it = std::lower_bound(compvis_sigmas.begin(), compvis_sigmas.end(), s);
|
|
||||||
if (it == compvis_sigmas.begin())
|
|
||||||
return 0;
|
|
||||||
if (it == compvis_sigmas.end())
|
|
||||||
return TIMESTEPS - 1;
|
|
||||||
int idx_high = static_cast<int>(std::distance(compvis_sigmas.begin(), it));
|
|
||||||
int idx_low = idx_high - 1;
|
|
||||||
if (std::abs(compvis_sigmas[idx_high] - s) < std::abs(compvis_sigmas[idx_low] - s)) {
|
|
||||||
return idx_high;
|
|
||||||
}
|
|
||||||
return idx_low;
|
|
||||||
};
|
|
||||||
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
int steps = static_cast<int>(sigmas.size()) - 1;
|
||||||
for (int i = 0; i < steps; i++) {
|
for (int i = 0; i < steps; i++) {
|
||||||
float sigma_to = sigmas[i + 1];
|
int timestep = TIMESTEPS - 1 - (TIMESTEPS / original_steps) * (int)floor(i * ((float)original_steps / steps));
|
||||||
int prev_timestep = get_timestep_from_sigma(sigma_to);
|
int prev_timestep = i >= steps - 1 ? 0 : TIMESTEPS - 1 - (TIMESTEPS / original_steps) * (int)floor((i + 1) * ((float)original_steps / steps));
|
||||||
int timestep_s = (int)floor((1 - eta) * prev_timestep);
|
int timestep_s = (int)floor((1 - eta) * prev_timestep);
|
||||||
float sigma = sigmas[i];
|
float sigma = static_cast<float>(compvis_sigmas[timestep]);
|
||||||
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
if (i == 0) {
|
||||||
if (denoised_opt.pred.empty()) {
|
x *= std::sqrt(sigma * sigma + 1) / sigma;
|
||||||
|
} else {
|
||||||
|
x *= std::sqrt(sigma * sigma + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto model_output_opt = model(x, sigma, i + 1);
|
||||||
|
if (model_output_opt.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
sd::Tensor<float> model_output = std::move(model_output_opt);
|
||||||
sd::Tensor<float> d = (x - denoised) / sigma;
|
model_output = (x - model_output) * (1.0f / sigma);
|
||||||
|
|
||||||
float alpha_prod_t = 1.0f / (sigma * sigma + 1.0f);
|
float alpha_prod_t = static_cast<float>(alphas_cumprod[timestep]);
|
||||||
float beta_prod_t = 1.0f - alpha_prod_t;
|
float beta_prod_t = 1.0f - alpha_prod_t;
|
||||||
float alpha_prod_t_prev = 1.0f / (sigma_to * sigma_to + 1.0f);
|
float alpha_prod_t_prev = static_cast<float>(prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0]);
|
||||||
float alpha_prod_s = static_cast<float>(alphas_cumprod[timestep_s]);
|
float alpha_prod_s = static_cast<float>(alphas_cumprod[timestep_s]);
|
||||||
float beta_prod_s = 1.0f - alpha_prod_s;
|
float beta_prod_s = 1.0f - alpha_prod_s;
|
||||||
|
|
||||||
x = std::sqrt(alpha_prod_s / alpha_prod_t_prev) * denoised +
|
sd::Tensor<float> pred_original_sample = ((x / std::sqrt(sigma * sigma + 1)) -
|
||||||
std::sqrt(beta_prod_s / alpha_prod_t_prev) * d;
|
std::sqrt(beta_prod_t) * model_output) *
|
||||||
|
(1.0f / std::sqrt(alpha_prod_t));
|
||||||
|
|
||||||
if (eta > 0 && sigma_to > 0.0f) {
|
x = std::sqrt(alpha_prod_s) * pred_original_sample +
|
||||||
|
std::sqrt(beta_prod_s) * model_output;
|
||||||
|
|
||||||
|
if (eta > 0 && i != steps - 1) {
|
||||||
x = std::sqrt(alpha_prod_t_prev / alpha_prod_s) * x +
|
x = std::sqrt(alpha_prod_t_prev / alpha_prod_s) * x +
|
||||||
std::sqrt(1.0f / alpha_prod_t_prev - 1.0f / alpha_prod_s) * sd::Tensor<float>::randn_like(x, rng);
|
std::sqrt(1.0f - alpha_prod_t_prev / alpha_prod_s) * sd::Tensor<float>::randn_like(x, rng);
|
||||||
}
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd::Tensor<float> sample_euler_cfg_pp(denoise_cb_t model,
|
|
||||||
sd::Tensor<float> x,
|
|
||||||
const std::vector<float>& sigmas) {
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
|
||||||
for (int i = 0; i < steps; i++) {
|
|
||||||
float sigma = sigmas[i];
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
|
||||||
if (denoised_opt.pred.empty() || denoised_opt.pred_uncond.empty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
|
||||||
sd::Tensor<float> uncond_denoised = std::move(denoised_opt.pred_uncond);
|
|
||||||
sd::Tensor<float> d = (x - uncond_denoised) / sigma;
|
|
||||||
|
|
||||||
x = denoised + d * sigmas[i + 1];
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sd::Tensor<float> sample_euler_ancestral_cfg_pp(denoise_cb_t model,
|
|
||||||
sd::Tensor<float> x,
|
|
||||||
const std::vector<float>& sigmas,
|
|
||||||
std::shared_ptr<RNG> rng,
|
|
||||||
float eta) {
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
|
||||||
for (int i = 0; i < steps; i++) {
|
|
||||||
float sigma = sigmas[i];
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
|
||||||
if (denoised_opt.pred.empty() || denoised_opt.pred_uncond.empty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
|
||||||
sd::Tensor<float> uncond_denoised = std::move(denoised_opt.pred_uncond);
|
|
||||||
sd::Tensor<float> d = (x - uncond_denoised) / sigma;
|
|
||||||
|
|
||||||
auto [sigma_down, sigma_up] = get_ancestral_step(sigmas[i], sigmas[i + 1], eta);
|
|
||||||
|
|
||||||
x = denoised + d * sigma_down;
|
|
||||||
|
|
||||||
if (sigmas[i + 1] > 0) {
|
|
||||||
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/ToyotaResearchInstitute/gradient-estimation-sampler
|
|
||||||
static sd::Tensor<float> sample_gradient_estimation(denoise_cb_t model,
|
|
||||||
sd::Tensor<float> x,
|
|
||||||
const std::vector<float>& sigmas,
|
|
||||||
std::shared_ptr<RNG> rng,
|
|
||||||
bool is_flow_denoiser,
|
|
||||||
float eta,
|
|
||||||
const SamplerExtraArgs& extra_sample_args) {
|
|
||||||
float ge_gamma = 2.0f;
|
|
||||||
|
|
||||||
for (const auto& [key, value] : extra_sample_args) {
|
|
||||||
if (key == "gamma") {
|
|
||||||
float parsed = 0.0f;
|
|
||||||
if (!parse_strict_float(value, parsed)) {
|
|
||||||
LOG_WARN("ignoring invalid euler_ge extra sample arg '%s=%s'", key.c_str(), value.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("setting euler_ge gamma to %.2f", parsed);
|
|
||||||
ge_gamma = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int steps = static_cast<int>(sigmas.size()) - 1;
|
|
||||||
sd::Tensor<float> old_d;
|
|
||||||
bool has_old_d = false;
|
|
||||||
for (int i = 0; i < steps; i++) {
|
|
||||||
float sigma = sigmas[i];
|
|
||||||
float sigma_to = sigmas[i + 1];
|
|
||||||
auto denoised_opt = model(x, sigma, i + 1);
|
|
||||||
if (denoised_opt.pred.empty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
sd::Tensor<float> denoised = std::move(denoised_opt.pred);
|
|
||||||
if (sigma_to == 0.f) {
|
|
||||||
x = denoised;
|
|
||||||
} else {
|
|
||||||
auto [sigma_down, sigma_up, alpha_scale] = get_ancestral_step(sigma, sigma_to, eta, is_flow_denoiser);
|
|
||||||
sd::Tensor<float> d = (x - denoised) / sigma;
|
|
||||||
float dt = sigma_down - sigma;
|
|
||||||
if (has_old_d) {
|
|
||||||
sd::Tensor<float> d_bar = d * ge_gamma + old_d * (1.0f - ge_gamma);
|
|
||||||
x += d_bar * dt;
|
|
||||||
} else {
|
|
||||||
x += d * dt;
|
|
||||||
}
|
|
||||||
old_d = std::move(d);
|
|
||||||
has_old_d = true;
|
|
||||||
if (sigma_up > 0.f) {
|
|
||||||
if (is_flow_denoiser) {
|
|
||||||
x *= alpha_scale;
|
|
||||||
}
|
|
||||||
x += sd::Tensor<float>::randn_like(x, rng) * sigma_up;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
@ -1853,12 +1648,13 @@ static sd::Tensor<float> sample_k_diffusion(sample_method_t method,
|
|||||||
std::vector<float> sigmas,
|
std::vector<float> sigmas,
|
||||||
std::shared_ptr<RNG> rng,
|
std::shared_ptr<RNG> rng,
|
||||||
float eta,
|
float eta,
|
||||||
bool is_flow_denoiser,
|
bool is_flow_denoiser) {
|
||||||
const char* extra_sample_args) {
|
|
||||||
SamplerExtraArgs extra_args = parse_key_value_args(extra_sample_args, "extra sample arg");
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case EULER_A_SAMPLE_METHOD:
|
case EULER_A_SAMPLE_METHOD:
|
||||||
return sample_euler_ancestral(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
if (is_flow_denoiser)
|
||||||
|
return sample_euler_flow(model, std::move(x), sigmas, rng, eta);
|
||||||
|
else
|
||||||
|
return sample_euler_ancestral(model, std::move(x), sigmas, rng, eta);
|
||||||
case EULER_SAMPLE_METHOD:
|
case EULER_SAMPLE_METHOD:
|
||||||
return sample_euler(model, std::move(x), sigmas);
|
return sample_euler(model, std::move(x), sigmas);
|
||||||
case HEUN_SAMPLE_METHOD:
|
case HEUN_SAMPLE_METHOD:
|
||||||
@ -1875,31 +1671,24 @@ static sd::Tensor<float> sample_k_diffusion(sample_method_t method,
|
|||||||
case DPMPP2Mv2_SAMPLE_METHOD:
|
case DPMPP2Mv2_SAMPLE_METHOD:
|
||||||
return sample_dpmpp_2m_v2(model, std::move(x), sigmas);
|
return sample_dpmpp_2m_v2(model, std::move(x), sigmas);
|
||||||
case LCM_SAMPLE_METHOD:
|
case LCM_SAMPLE_METHOD:
|
||||||
return sample_lcm(model, std::move(x), sigmas, rng, is_flow_denoiser, extra_args);
|
return sample_lcm(model, std::move(x), sigmas, rng);
|
||||||
case IPNDM_SAMPLE_METHOD:
|
case IPNDM_SAMPLE_METHOD:
|
||||||
return sample_ipndm(model, std::move(x), sigmas);
|
return sample_ipndm(model, std::move(x), sigmas);
|
||||||
case IPNDM_V_SAMPLE_METHOD:
|
case IPNDM_V_SAMPLE_METHOD:
|
||||||
return sample_ipndm_v(model, std::move(x), sigmas);
|
return sample_ipndm_v(model, std::move(x), sigmas);
|
||||||
case RES_MULTISTEP_SAMPLE_METHOD:
|
case RES_MULTISTEP_SAMPLE_METHOD:
|
||||||
return sample_res_multistep(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
return sample_res_multistep(model, std::move(x), sigmas, rng, eta);
|
||||||
case RES_2S_SAMPLE_METHOD:
|
case RES_2S_SAMPLE_METHOD:
|
||||||
return sample_res_2s(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
return sample_res_2s(model, std::move(x), sigmas, rng, eta);
|
||||||
case ER_SDE_SAMPLE_METHOD:
|
case ER_SDE_SAMPLE_METHOD:
|
||||||
return sample_er_sde(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
return sample_er_sde(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
||||||
case DDIM_TRAILING_SAMPLE_METHOD:
|
case DDIM_TRAILING_SAMPLE_METHOD:
|
||||||
// DDIM is equivalent to Euler Ancestral with the Simple scheduler
|
return sample_ddim_trailing(model, std::move(x), sigmas, rng, eta);
|
||||||
return sample_euler_ancestral(model, std::move(x), sigmas, rng, is_flow_denoiser, eta);
|
|
||||||
case TCD_SAMPLE_METHOD:
|
case TCD_SAMPLE_METHOD:
|
||||||
return sample_tcd(model, std::move(x), sigmas, rng, eta);
|
return sample_tcd(model, std::move(x), sigmas, rng, eta);
|
||||||
case EULER_CFG_PP_SAMPLE_METHOD:
|
|
||||||
return sample_euler_cfg_pp(model, std::move(x), sigmas);
|
|
||||||
case EULER_A_CFG_PP_SAMPLE_METHOD:
|
|
||||||
return sample_euler_ancestral_cfg_pp(model, std::move(x), sigmas, rng, eta);
|
|
||||||
case EULER_GE_SAMPLE_METHOD:
|
|
||||||
return sample_gradient_estimation(model, std::move(x), sigmas, rng, is_flow_denoiser, eta, extra_args);
|
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __SD_RUNTIME_DENOISER_HPP__
|
#endif // __DENOISER_HPP__
|
||||||
582
src/diffusion_model.hpp
Normal file
582
src/diffusion_model.hpp
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
#ifndef __DIFFUSION_MODEL_H__
|
||||||
|
#define __DIFFUSION_MODEL_H__
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include "anima.hpp"
|
||||||
|
#include "ernie_image.hpp"
|
||||||
|
#include "flux.hpp"
|
||||||
|
#include "mmdit.hpp"
|
||||||
|
#include "qwen_image.hpp"
|
||||||
|
#include "tensor_ggml.hpp"
|
||||||
|
#include "unet.hpp"
|
||||||
|
#include "wan.hpp"
|
||||||
|
#include "z_image.hpp"
|
||||||
|
|
||||||
|
struct DiffusionParams {
|
||||||
|
const sd::Tensor<float>* x = nullptr;
|
||||||
|
const sd::Tensor<float>* timesteps = nullptr;
|
||||||
|
const sd::Tensor<float>* context = nullptr;
|
||||||
|
const sd::Tensor<float>* c_concat = nullptr;
|
||||||
|
const sd::Tensor<float>* y = nullptr;
|
||||||
|
const sd::Tensor<int32_t>* t5_ids = nullptr;
|
||||||
|
const sd::Tensor<float>* t5_weights = nullptr;
|
||||||
|
const sd::Tensor<float>* guidance = nullptr;
|
||||||
|
const std::vector<sd::Tensor<float>>* ref_latents = nullptr;
|
||||||
|
bool increase_ref_index = false;
|
||||||
|
int num_video_frames = -1;
|
||||||
|
const std::vector<sd::Tensor<float>>* controls = nullptr;
|
||||||
|
float control_strength = 0.f;
|
||||||
|
const sd::Tensor<float>* vace_context = nullptr;
|
||||||
|
float vace_strength = 1.f;
|
||||||
|
const std::vector<int>* skip_layers = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline const sd::Tensor<T>& tensor_or_empty(const sd::Tensor<T>* tensor) {
|
||||||
|
static const sd::Tensor<T> kEmpty;
|
||||||
|
return tensor != nullptr ? *tensor : kEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DiffusionModel {
|
||||||
|
virtual std::string get_desc() = 0;
|
||||||
|
virtual sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) = 0;
|
||||||
|
virtual void alloc_params_buffer() = 0;
|
||||||
|
virtual void free_params_buffer() = 0;
|
||||||
|
virtual void free_compute_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_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter){};
|
||||||
|
virtual int64_t get_adm_in_channels() = 0;
|
||||||
|
virtual void set_flash_attention_enabled(bool enabled) = 0;
|
||||||
|
virtual void set_circular_axes(bool circular_x, bool circular_y) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UNetModel : public DiffusionModel {
|
||||||
|
UNetModelRunner unet;
|
||||||
|
|
||||||
|
UNetModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
SDVersion version = VERSION_SD1)
|
||||||
|
: unet(backend, offload_params_to_cpu, tensor_storage_map, "model.diffusion_model", version) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return unet.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
unet.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
unet.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
unet.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
unet.get_param_tensors(tensors, "model.diffusion_model");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return unet.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
unet.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return unet.unet.adm_in_channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
unet.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
unet.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
static const std::vector<sd::Tensor<float>> empty_controls;
|
||||||
|
return unet.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
tensor_or_empty(diffusion_params.c_concat),
|
||||||
|
tensor_or_empty(diffusion_params.y),
|
||||||
|
diffusion_params.num_video_frames,
|
||||||
|
diffusion_params.controls ? *diffusion_params.controls : empty_controls,
|
||||||
|
diffusion_params.control_strength);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MMDiTModel : public DiffusionModel {
|
||||||
|
MMDiTRunner mmdit;
|
||||||
|
|
||||||
|
MMDiTModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {})
|
||||||
|
: mmdit(backend, offload_params_to_cpu, tensor_storage_map, "model.diffusion_model") {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return mmdit.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
mmdit.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
mmdit.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
mmdit.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
mmdit.get_param_tensors(tensors, "model.diffusion_model");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return mmdit.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
mmdit.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768 + 1280;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
mmdit.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
mmdit.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
static const std::vector<int> empty_skip_layers;
|
||||||
|
return mmdit.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
tensor_or_empty(diffusion_params.y),
|
||||||
|
diffusion_params.skip_layers ? *diffusion_params.skip_layers : empty_skip_layers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FluxModel : public DiffusionModel {
|
||||||
|
Flux::FluxRunner flux;
|
||||||
|
|
||||||
|
FluxModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
SDVersion version = VERSION_FLUX,
|
||||||
|
bool use_mask = false)
|
||||||
|
: flux(backend, offload_params_to_cpu, tensor_storage_map, "model.diffusion_model", version, use_mask) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return flux.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
flux.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
flux.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
flux.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
flux.get_param_tensors(tensors, "model.diffusion_model");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return flux.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
flux.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
flux.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
flux.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
static const std::vector<sd::Tensor<float>> empty_ref_latents;
|
||||||
|
static const std::vector<int> empty_skip_layers;
|
||||||
|
return flux.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
tensor_or_empty(diffusion_params.c_concat),
|
||||||
|
tensor_or_empty(diffusion_params.y),
|
||||||
|
tensor_or_empty(diffusion_params.guidance),
|
||||||
|
diffusion_params.ref_latents ? *diffusion_params.ref_latents : empty_ref_latents,
|
||||||
|
diffusion_params.increase_ref_index,
|
||||||
|
diffusion_params.skip_layers ? *diffusion_params.skip_layers : empty_skip_layers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimaModel : public DiffusionModel {
|
||||||
|
std::string prefix;
|
||||||
|
Anima::AnimaRunner anima;
|
||||||
|
|
||||||
|
AnimaModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
const std::string prefix = "model.diffusion_model")
|
||||||
|
: prefix(prefix), anima(backend, offload_params_to_cpu, tensor_storage_map, prefix) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return anima.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
anima.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
anima.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
anima.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
anima.get_param_tensors(tensors, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return anima.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
anima.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
anima.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
anima.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
return anima.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
tensor_or_empty(diffusion_params.t5_ids),
|
||||||
|
tensor_or_empty(diffusion_params.t5_weights));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WanModel : public DiffusionModel {
|
||||||
|
std::string prefix;
|
||||||
|
WAN::WanRunner wan;
|
||||||
|
|
||||||
|
WanModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
const std::string prefix = "model.diffusion_model",
|
||||||
|
SDVersion version = VERSION_WAN2)
|
||||||
|
: prefix(prefix), wan(backend, offload_params_to_cpu, tensor_storage_map, prefix, version) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return wan.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
wan.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
wan.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
wan.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
wan.get_param_tensors(tensors, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return wan.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
wan.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
wan.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
wan.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
return wan.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
tensor_or_empty(diffusion_params.y),
|
||||||
|
tensor_or_empty(diffusion_params.c_concat),
|
||||||
|
sd::Tensor<float>(),
|
||||||
|
tensor_or_empty(diffusion_params.vace_context),
|
||||||
|
diffusion_params.vace_strength);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QwenImageModel : public DiffusionModel {
|
||||||
|
std::string prefix;
|
||||||
|
Qwen::QwenImageRunner qwen_image;
|
||||||
|
|
||||||
|
QwenImageModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
const std::string prefix = "model.diffusion_model",
|
||||||
|
SDVersion version = VERSION_QWEN_IMAGE,
|
||||||
|
bool zero_cond_t = false)
|
||||||
|
: prefix(prefix), qwen_image(backend, offload_params_to_cpu, tensor_storage_map, prefix, version, zero_cond_t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return qwen_image.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
qwen_image.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
qwen_image.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
qwen_image.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
qwen_image.get_param_tensors(tensors, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return qwen_image.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
qwen_image.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
qwen_image.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
qwen_image.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
static const std::vector<sd::Tensor<float>> empty_ref_latents;
|
||||||
|
return qwen_image.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
diffusion_params.ref_latents ? *diffusion_params.ref_latents : empty_ref_latents,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZImageModel : public DiffusionModel {
|
||||||
|
std::string prefix;
|
||||||
|
ZImage::ZImageRunner z_image;
|
||||||
|
|
||||||
|
ZImageModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
const std::string prefix = "model.diffusion_model",
|
||||||
|
SDVersion version = VERSION_Z_IMAGE)
|
||||||
|
: prefix(prefix), z_image(backend, offload_params_to_cpu, tensor_storage_map, prefix, version) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return z_image.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
z_image.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
z_image.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
z_image.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
z_image.get_param_tensors(tensors, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return z_image.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
z_image.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
z_image.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
z_image.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
static const std::vector<sd::Tensor<float>> empty_ref_latents;
|
||||||
|
return z_image.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context),
|
||||||
|
diffusion_params.ref_latents ? *diffusion_params.ref_latents : empty_ref_latents,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ErnieImageModel : public DiffusionModel {
|
||||||
|
std::string prefix;
|
||||||
|
ErnieImage::ErnieImageRunner ernie_image;
|
||||||
|
|
||||||
|
ErnieImageModel(ggml_backend_t backend,
|
||||||
|
bool offload_params_to_cpu,
|
||||||
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
|
const std::string prefix = "model.diffusion_model")
|
||||||
|
: prefix(prefix), ernie_image(backend, offload_params_to_cpu, tensor_storage_map, prefix) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_desc() override {
|
||||||
|
return ernie_image.get_desc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_params_buffer() override {
|
||||||
|
ernie_image.alloc_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_params_buffer() override {
|
||||||
|
ernie_image.free_params_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_compute_buffer() override {
|
||||||
|
ernie_image.free_compute_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||||
|
ernie_image.get_param_tensors(tensors, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t get_params_buffer_size() override {
|
||||||
|
return ernie_image.get_params_buffer_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
||||||
|
ernie_image.set_weight_adapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_adm_in_channels() override {
|
||||||
|
return 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_flash_attention_enabled(bool enabled) {
|
||||||
|
ernie_image.set_flash_attention_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_circular_axes(bool circular_x, bool circular_y) override {
|
||||||
|
ernie_image.set_circular_axes(circular_x, circular_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd::Tensor<float> compute(int n_threads,
|
||||||
|
const DiffusionParams& diffusion_params) override {
|
||||||
|
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||||
|
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||||
|
return ernie_image.compute(n_threads,
|
||||||
|
*diffusion_params.x,
|
||||||
|
*diffusion_params.timesteps,
|
||||||
|
tensor_or_empty(diffusion_params.context));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,15 +1,15 @@
|
|||||||
#ifndef __SD_RUNTIME_EASYCACHE_HPP__
|
#ifndef __EASYCACHE_HPP__
|
||||||
#define __SD_RUNTIME_EASYCACHE_HPP__
|
#define __EASYCACHE_HPP__
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "condition_cache_utils.hpp"
|
||||||
#include "core/tensor.hpp"
|
#include "denoiser.hpp"
|
||||||
#include "runtime/condition_cache_utils.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "runtime/denoiser.hpp"
|
#include "tensor.hpp"
|
||||||
|
|
||||||
struct EasyCacheConfig {
|
struct EasyCacheConfig {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
@ -258,4 +258,4 @@ struct EasyCacheState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_RUNTIME_EASYCACHE_HPP__
|
#endif
|
||||||
@ -1,88 +1,17 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_ERNIE_IMAGE_HPP__
|
#ifndef __SD_ERNIE_IMAGE_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_ERNIE_IMAGE_HPP__
|
#define __SD_ERNIE_IMAGE_HPP__
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "model/common/rope.hpp"
|
#include "common_dit.hpp"
|
||||||
#include "model/diffusion/dit.hpp"
|
#include "flux.hpp"
|
||||||
#include "model/diffusion/flux.hpp"
|
#include "qwen_image.hpp"
|
||||||
#include "model/diffusion/model.hpp"
|
#include "rope.hpp"
|
||||||
#include "model/diffusion/qwen_image.hpp"
|
|
||||||
|
|
||||||
namespace ErnieImage {
|
namespace ErnieImage {
|
||||||
constexpr int ERNIE_IMAGE_GRAPH_SIZE = 40960;
|
constexpr int ERNIE_IMAGE_GRAPH_SIZE = 40960;
|
||||||
|
|
||||||
struct ErnieImageConfig {
|
|
||||||
int64_t hidden_size = 4096;
|
|
||||||
int64_t num_heads = 32;
|
|
||||||
int64_t num_layers = 36;
|
|
||||||
int64_t ffn_hidden_size = 12288;
|
|
||||||
int64_t in_channels = 128;
|
|
||||||
int64_t out_channels = 128;
|
|
||||||
int patch_size = 1;
|
|
||||||
int64_t text_in_dim = 3072;
|
|
||||||
int theta = 256;
|
|
||||||
std::vector<int> axes_dim = {32, 48, 48};
|
|
||||||
int axes_dim_sum = 128;
|
|
||||||
float eps = 1e-6f;
|
|
||||||
|
|
||||||
static ErnieImageConfig detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
|
||||||
ErnieImageConfig config;
|
|
||||||
config.num_layers = 0;
|
|
||||||
int64_t detected_head_dim = 0;
|
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
|
||||||
if (!starts_with(name, prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ends_with(name, "x_embedder.proj.weight") && tensor_storage.n_dims == 4) {
|
|
||||||
config.patch_size = static_cast<int>(tensor_storage.ne[0]);
|
|
||||||
config.in_channels = tensor_storage.ne[2];
|
|
||||||
config.hidden_size = tensor_storage.ne[3];
|
|
||||||
} else if (ends_with(name, "text_proj.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
config.text_in_dim = tensor_storage.ne[0];
|
|
||||||
} else if (ends_with(name, "layers.0.self_attention.norm_q.weight")) {
|
|
||||||
detected_head_dim = tensor_storage.ne[0];
|
|
||||||
} else if (ends_with(name, "layers.0.mlp.gate_proj.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
config.ffn_hidden_size = tensor_storage.ne[1];
|
|
||||||
} else if (ends_with(name, "final_linear.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
int64_t out_dim = tensor_storage.ne[1];
|
|
||||||
int64_t patch_area = config.patch_size * config.patch_size;
|
|
||||||
config.out_channels = out_dim / patch_area;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = name.find("layers.");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
auto items = split_string(name.substr(pos), '.');
|
|
||||||
if (items.size() > 1) {
|
|
||||||
int block_index = atoi(items[1].c_str());
|
|
||||||
if (block_index + 1 > config.num_layers) {
|
|
||||||
config.num_layers = block_index + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (config.num_layers == 0) {
|
|
||||||
config.num_layers = 36;
|
|
||||||
}
|
|
||||||
if (detected_head_dim > 0) {
|
|
||||||
config.num_heads = config.hidden_size / detected_head_dim;
|
|
||||||
}
|
|
||||||
config.axes_dim_sum = 0;
|
|
||||||
for (int axis_dim : config.axes_dim) {
|
|
||||||
config.axes_dim_sum += axis_dim;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("ernie_image: num_layers = %" PRId64 ", hidden_size = %" PRId64 ", num_heads = %" PRId64 ", ffn_hidden_size = %" PRId64 ", in_channels = %" PRId64 ", out_channels = %" PRId64,
|
|
||||||
config.num_layers,
|
|
||||||
config.hidden_size,
|
|
||||||
config.num_heads,
|
|
||||||
config.ffn_hidden_size,
|
|
||||||
config.in_channels,
|
|
||||||
config.out_channels);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* timestep_embedding_sin_cos(ggml_context* ctx,
|
__STATIC_INLINE__ ggml_tensor* timestep_embedding_sin_cos(ggml_context* ctx,
|
||||||
ggml_tensor* timesteps,
|
ggml_tensor* timesteps,
|
||||||
int dim,
|
int dim,
|
||||||
@ -278,36 +207,51 @@ namespace ErnieImage {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ErnieImageParams {
|
||||||
|
int64_t hidden_size = 4096;
|
||||||
|
int64_t num_heads = 32;
|
||||||
|
int64_t num_layers = 36;
|
||||||
|
int64_t ffn_hidden_size = 12288;
|
||||||
|
int64_t in_channels = 128;
|
||||||
|
int64_t out_channels = 128;
|
||||||
|
int patch_size = 1;
|
||||||
|
int64_t text_in_dim = 3072;
|
||||||
|
int theta = 256;
|
||||||
|
std::vector<int> axes_dim = {32, 48, 48};
|
||||||
|
int axes_dim_sum = 128;
|
||||||
|
float eps = 1e-6f;
|
||||||
|
};
|
||||||
|
|
||||||
class ErnieImageModel : public GGMLBlock {
|
class ErnieImageModel : public GGMLBlock {
|
||||||
public:
|
public:
|
||||||
ErnieImageConfig config;
|
ErnieImageParams params;
|
||||||
|
|
||||||
ErnieImageModel() = default;
|
ErnieImageModel() = default;
|
||||||
ErnieImageModel(ErnieImageConfig config)
|
ErnieImageModel(ErnieImageParams params)
|
||||||
: config(config) {
|
: params(params) {
|
||||||
blocks["x_embedder.proj"] = std::make_shared<Conv2d>(config.in_channels,
|
blocks["x_embedder.proj"] = std::make_shared<Conv2d>(params.in_channels,
|
||||||
config.hidden_size,
|
params.hidden_size,
|
||||||
std::pair<int, int>{config.patch_size, config.patch_size},
|
std::pair<int, int>{params.patch_size, params.patch_size},
|
||||||
std::pair<int, int>{config.patch_size, config.patch_size},
|
std::pair<int, int>{params.patch_size, params.patch_size},
|
||||||
std::pair<int, int>{0, 0},
|
std::pair<int, int>{0, 0},
|
||||||
std::pair<int, int>{1, 1},
|
std::pair<int, int>{1, 1},
|
||||||
true);
|
true);
|
||||||
if (config.text_in_dim != config.hidden_size) {
|
if (params.text_in_dim != params.hidden_size) {
|
||||||
blocks["text_proj"] = std::make_shared<Linear>(config.text_in_dim, config.hidden_size, false);
|
blocks["text_proj"] = std::make_shared<Linear>(params.text_in_dim, params.hidden_size, false);
|
||||||
}
|
}
|
||||||
blocks["time_embedding"] = std::make_shared<Qwen::TimestepEmbedding>(config.hidden_size, config.hidden_size);
|
blocks["time_embedding"] = std::make_shared<Qwen::TimestepEmbedding>(params.hidden_size, params.hidden_size);
|
||||||
blocks["adaLN_modulation.1"] = std::make_shared<Linear>(config.hidden_size, 6 * config.hidden_size, true);
|
blocks["adaLN_modulation.1"] = std::make_shared<Linear>(params.hidden_size, 6 * params.hidden_size, true);
|
||||||
|
|
||||||
for (int i = 0; i < config.num_layers; i++) {
|
for (int i = 0; i < params.num_layers; i++) {
|
||||||
blocks["layers." + std::to_string(i)] = std::make_shared<ErnieImageSharedAdaLNBlock>(config.hidden_size,
|
blocks["layers." + std::to_string(i)] = std::make_shared<ErnieImageSharedAdaLNBlock>(params.hidden_size,
|
||||||
config.num_heads,
|
params.num_heads,
|
||||||
config.ffn_hidden_size,
|
params.ffn_hidden_size,
|
||||||
config.eps);
|
params.eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks["final_norm"] = std::make_shared<ErnieImageAdaLNContinuous>(config.hidden_size, config.eps);
|
blocks["final_norm"] = std::make_shared<ErnieImageAdaLNContinuous>(params.hidden_size, params.eps);
|
||||||
blocks["final_linear"] = std::make_shared<Linear>(config.hidden_size,
|
blocks["final_linear"] = std::make_shared<Linear>(params.hidden_size,
|
||||||
config.patch_size * config.patch_size * config.out_channels,
|
params.patch_size * params.patch_size * params.out_channels,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,12 +264,12 @@ namespace ErnieImage {
|
|||||||
// context: [N, text_tokens, 3072]
|
// context: [N, text_tokens, 3072]
|
||||||
// pe: [image_tokens + text_tokens, head_dim/2, 2, 2]
|
// pe: [image_tokens + text_tokens, head_dim/2, 2, 2]
|
||||||
GGML_ASSERT(context != nullptr);
|
GGML_ASSERT(context != nullptr);
|
||||||
GGML_ASSERT(x->ne[1] % config.patch_size == 0 && x->ne[0] % config.patch_size == 0);
|
GGML_ASSERT(x->ne[1] % params.patch_size == 0 && x->ne[0] % params.patch_size == 0);
|
||||||
|
|
||||||
int64_t W = x->ne[0];
|
int64_t W = x->ne[0];
|
||||||
int64_t H = x->ne[1];
|
int64_t H = x->ne[1];
|
||||||
int64_t Hp = H / config.patch_size;
|
int64_t Hp = H / params.patch_size;
|
||||||
int64_t Wp = W / config.patch_size;
|
int64_t Wp = W / params.patch_size;
|
||||||
int64_t n_img = Hp * Wp;
|
int64_t n_img = Hp * Wp;
|
||||||
int64_t N = x->ne[3];
|
int64_t N = x->ne[3];
|
||||||
|
|
||||||
@ -347,12 +291,10 @@ namespace ErnieImage {
|
|||||||
|
|
||||||
auto hidden_states = ggml_concat(ctx->ggml_ctx, img, txt, 1); // [N, image_tokens + text_tokens, hidden_size]
|
auto hidden_states = ggml_concat(ctx->ggml_ctx, img, txt, 1); // [N, image_tokens + text_tokens, hidden_size]
|
||||||
|
|
||||||
auto sample = timestep_embedding_sin_cos(ctx->ggml_ctx, timestep, static_cast<int>(config.hidden_size));
|
auto sample = timestep_embedding_sin_cos(ctx->ggml_ctx, timestep, static_cast<int>(params.hidden_size));
|
||||||
auto c = time_embedding->forward(ctx, sample); // [N, hidden_size]
|
auto c = time_embedding->forward(ctx, sample); // [N, hidden_size]
|
||||||
|
|
||||||
auto mod_params = adaLN_mod->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, 6 * hidden_size]
|
auto mod_params = adaLN_mod->forward(ctx, ggml_silu(ctx->ggml_ctx, c)); // [N, 6 * hidden_size]
|
||||||
sd::ggml_graph_cut::mark_graph_cut(hidden_states, "ernie_image.prelude", "hidden_states");
|
|
||||||
// sd::ggml_graph_cut::mark_graph_cut(mod_params, "ernie_image.prelude", "mod_params");
|
|
||||||
auto chunks = ggml_ext_chunk(ctx->ggml_ctx, mod_params, 6, 0);
|
auto chunks = ggml_ext_chunk(ctx->ggml_ctx, mod_params, 6, 0);
|
||||||
std::vector<ggml_tensor*> temb;
|
std::vector<ggml_tensor*> temb;
|
||||||
temb.reserve(6);
|
temb.reserve(6);
|
||||||
@ -360,10 +302,9 @@ namespace ErnieImage {
|
|||||||
temb.push_back(ggml_reshape_3d(ctx->ggml_ctx, chunk, chunk->ne[0], 1, chunk->ne[1])); // [N, 1, hidden_size]
|
temb.push_back(ggml_reshape_3d(ctx->ggml_ctx, chunk, chunk->ne[0], 1, chunk->ne[1])); // [N, 1, hidden_size]
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < config.num_layers; i++) {
|
for (int i = 0; i < params.num_layers; i++) {
|
||||||
auto layer = std::dynamic_pointer_cast<ErnieImageSharedAdaLNBlock>(blocks["layers." + std::to_string(i)]);
|
auto layer = std::dynamic_pointer_cast<ErnieImageSharedAdaLNBlock>(blocks["layers." + std::to_string(i)]);
|
||||||
hidden_states = layer->forward(ctx, hidden_states, pe, temb);
|
hidden_states = layer->forward(ctx, hidden_states, pe, temb);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(hidden_states, "ernie_image.layers." + std::to_string(i), "hidden_states");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hidden_states = final_norm->forward(ctx, hidden_states, c);
|
hidden_states = final_norm->forward(ctx, hidden_states, c);
|
||||||
@ -374,25 +315,74 @@ namespace ErnieImage {
|
|||||||
patches,
|
patches,
|
||||||
Hp,
|
Hp,
|
||||||
Wp,
|
Wp,
|
||||||
config.patch_size,
|
params.patch_size,
|
||||||
config.patch_size,
|
params.patch_size,
|
||||||
false); // [N, out_channels, H, W]
|
false); // [N, out_channels, H, W]
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ErnieImageRunner : public DiffusionModelRunner {
|
struct ErnieImageRunner : public GGMLRunner {
|
||||||
ErnieImageConfig config;
|
ErnieImageParams ernie_params;
|
||||||
ErnieImageModel ernie_image;
|
ErnieImageModel ernie_image;
|
||||||
std::vector<float> pe_vec;
|
std::vector<float> pe_vec;
|
||||||
|
|
||||||
ErnieImageRunner(ggml_backend_t backend,
|
ErnieImageRunner(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
const std::string prefix = "")
|
const std::string prefix = "")
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||||
config(ErnieImageConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
ernie_params.num_layers = 0;
|
||||||
ernie_image = ErnieImageModel(config);
|
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||||
|
if (!starts_with(name, prefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ends_with(name, "x_embedder.proj.weight") && tensor_storage.n_dims == 4) {
|
||||||
|
ernie_params.patch_size = static_cast<int>(tensor_storage.ne[0]);
|
||||||
|
ernie_params.in_channels = tensor_storage.ne[2];
|
||||||
|
ernie_params.hidden_size = tensor_storage.ne[3];
|
||||||
|
} else if (ends_with(name, "text_proj.weight") && tensor_storage.n_dims == 2) {
|
||||||
|
ernie_params.text_in_dim = tensor_storage.ne[0];
|
||||||
|
} else if (ends_with(name, "layers.0.self_attention.norm_q.weight")) {
|
||||||
|
int64_t head_dim = tensor_storage.ne[0];
|
||||||
|
ernie_params.num_heads = ernie_params.hidden_size / head_dim;
|
||||||
|
} else if (ends_with(name, "layers.0.mlp.gate_proj.weight") && tensor_storage.n_dims == 2) {
|
||||||
|
ernie_params.ffn_hidden_size = tensor_storage.ne[1];
|
||||||
|
} else if (ends_with(name, "final_linear.weight") && tensor_storage.n_dims == 2) {
|
||||||
|
int64_t out_dim = tensor_storage.ne[1];
|
||||||
|
ernie_params.out_channels = out_dim / ernie_params.patch_size / ernie_params.patch_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = name.find("layers.");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
std::string layer_name = name.substr(pos);
|
||||||
|
auto items = split_string(layer_name, '.');
|
||||||
|
if (items.size() > 1) {
|
||||||
|
int block_index = atoi(items[1].c_str());
|
||||||
|
if (block_index + 1 > ernie_params.num_layers) {
|
||||||
|
ernie_params.num_layers = block_index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ernie_params.num_layers == 0) {
|
||||||
|
ernie_params.num_layers = 36;
|
||||||
|
}
|
||||||
|
ernie_params.axes_dim_sum = 0;
|
||||||
|
for (int axis_dim : ernie_params.axes_dim) {
|
||||||
|
ernie_params.axes_dim_sum += axis_dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("ernie_image: layers = %" PRId64 ", hidden_size = %" PRId64 ", heads = %" PRId64
|
||||||
|
", ffn_hidden_size = %" PRId64 ", in_channels = %" PRId64 ", out_channels = %" PRId64,
|
||||||
|
ernie_params.num_layers,
|
||||||
|
ernie_params.hidden_size,
|
||||||
|
ernie_params.num_heads,
|
||||||
|
ernie_params.ffn_hidden_size,
|
||||||
|
ernie_params.in_channels,
|
||||||
|
ernie_params.out_channels);
|
||||||
|
|
||||||
|
ernie_image = ErnieImageModel(ernie_params);
|
||||||
ernie_image.init(params_ctx, tensor_storage_map, prefix);
|
ernie_image.init(params_ctx, tensor_storage_map, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +390,7 @@ namespace ErnieImage {
|
|||||||
return "ernie_image";
|
return "ernie_image";
|
||||||
}
|
}
|
||||||
|
|
||||||
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, const std::string prefix) {
|
||||||
ernie_image.get_param_tensors(tensors, prefix);
|
ernie_image.get_param_tensors(tensors, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,15 +406,15 @@ namespace ErnieImage {
|
|||||||
|
|
||||||
pe_vec = Rope::gen_ernie_image_pe(static_cast<int>(x->ne[1]),
|
pe_vec = Rope::gen_ernie_image_pe(static_cast<int>(x->ne[1]),
|
||||||
static_cast<int>(x->ne[0]),
|
static_cast<int>(x->ne[0]),
|
||||||
config.patch_size,
|
ernie_params.patch_size,
|
||||||
static_cast<int>(x->ne[3]),
|
static_cast<int>(x->ne[3]),
|
||||||
static_cast<int>(context->ne[1]),
|
static_cast<int>(context->ne[1]),
|
||||||
config.theta,
|
ernie_params.theta,
|
||||||
circular_y_enabled,
|
circular_y_enabled,
|
||||||
circular_x_enabled,
|
circular_x_enabled,
|
||||||
config.axes_dim);
|
ernie_params.axes_dim);
|
||||||
int pos_len = static_cast<int>(pe_vec.size() / config.axes_dim_sum / 2);
|
int pos_len = static_cast<int>(pe_vec.size() / ernie_params.axes_dim_sum / 2);
|
||||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, config.axes_dim_sum, 1, pos_len, 2);
|
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, ernie_params.axes_dim_sum, 1, pos_len, 2);
|
||||||
set_backend_tensor_data(pe, pe_vec.data());
|
set_backend_tensor_data(pe, pe_vec.data());
|
||||||
|
|
||||||
auto runner_ctx = get_context();
|
auto runner_ctx = get_context();
|
||||||
@ -442,17 +432,7 @@ namespace ErnieImage {
|
|||||||
};
|
};
|
||||||
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), x.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace ErnieImage
|
} // namespace ErnieImage
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_ERNIE_IMAGE_HPP__
|
#endif // __SD_ERNIE_IMAGE_HPP__
|
||||||
@ -1,8 +1,8 @@
|
|||||||
#ifndef __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
#ifndef __ESRGAN_HPP__
|
||||||
#define __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
#define __ESRGAN_HPP__
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "model_loader.h"
|
#include "model.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=================================== ESRGAN ===================================
|
=================================== ESRGAN ===================================
|
||||||
@ -125,32 +125,26 @@ public:
|
|||||||
auto conv_last = std::dynamic_pointer_cast<Conv2d>(blocks["conv_last"]);
|
auto conv_last = std::dynamic_pointer_cast<Conv2d>(blocks["conv_last"]);
|
||||||
|
|
||||||
auto feat = conv_first->forward(ctx, x);
|
auto feat = conv_first->forward(ctx, x);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.prelude", "feat");
|
|
||||||
auto body_feat = feat;
|
auto body_feat = feat;
|
||||||
for (int i = 0; i < num_block; i++) {
|
for (int i = 0; i < num_block; i++) {
|
||||||
std::string name = "body." + std::to_string(i);
|
std::string name = "body." + std::to_string(i);
|
||||||
auto block = std::dynamic_pointer_cast<RRDB>(blocks[name]);
|
auto block = std::dynamic_pointer_cast<RRDB>(blocks[name]);
|
||||||
|
|
||||||
body_feat = block->forward(ctx, body_feat);
|
body_feat = block->forward(ctx, body_feat);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(body_feat, "esrgan.body." + std::to_string(i), "feat");
|
|
||||||
}
|
}
|
||||||
body_feat = conv_body->forward(ctx, body_feat);
|
body_feat = conv_body->forward(ctx, body_feat);
|
||||||
feat = ggml_add(ctx->ggml_ctx, feat, body_feat);
|
feat = ggml_add(ctx->ggml_ctx, feat, body_feat);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(feat, "esrgan.body.out", "feat");
|
|
||||||
// upsample
|
// upsample
|
||||||
if (scale >= 2) {
|
if (scale >= 2) {
|
||||||
auto conv_up1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up1"]);
|
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)));
|
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 (scale == 4) {
|
||||||
auto conv_up2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up2"]);
|
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)));
|
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for all scales
|
// for all scales
|
||||||
auto out = conv_last->forward(ctx, lrelu(ctx, conv_hr->forward(ctx, feat)));
|
auto out = conv_last->forward(ctx, lrelu(ctx, conv_hr->forward(ctx, feat)));
|
||||||
sd::ggml_graph_cut::mark_graph_cut(out, "esrgan.final", "out");
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -161,10 +155,10 @@ struct ESRGAN : public GGMLRunner {
|
|||||||
int tile_size = 128; // avoid cuda OOM for 4gb VRAM
|
int tile_size = 128; // avoid cuda OOM for 4gb VRAM
|
||||||
|
|
||||||
ESRGAN(ggml_backend_t backend,
|
ESRGAN(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
int tile_size = 128,
|
int tile_size = 128,
|
||||||
const String2TensorStorage& tensor_storage_map = {})
|
const String2TensorStorage& tensor_storage_map = {})
|
||||||
: GGMLRunner(backend, params_backend) {
|
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||||
this->tile_size = tile_size;
|
this->tile_size = tile_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,11 +264,7 @@ struct ESRGAN : public GGMLRunner {
|
|||||||
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 = 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, {}, "");
|
rrdb_net->init(params_ctx, {}, "");
|
||||||
|
|
||||||
if (!alloc_params_buffer()) {
|
alloc_params_buffer();
|
||||||
LOG_ERROR("esrgan model buffer allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ggml_tensor*> esrgan_tensors;
|
std::map<std::string, ggml_tensor*> esrgan_tensors;
|
||||||
rrdb_net->get_param_tensors(esrgan_tensors);
|
rrdb_net->get_param_tensors(esrgan_tensors);
|
||||||
|
|
||||||
@ -372,4 +362,4 @@ struct ESRGAN : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_UPSCALER_ESRGAN_HPP__
|
#endif // __ESRGAN_HPP__
|
||||||
@ -1,73 +0,0 @@
|
|||||||
#ifndef __SD_EXTENSIONS_GENERATION_EXTENSION_H__
|
|
||||||
#define __SD_EXTENSIONS_GENERATION_EXTENSION_H__
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "conditioning/conditioner.hpp"
|
|
||||||
#include "core/ggml_extend_backend.h"
|
|
||||||
#include "model_loader.h"
|
|
||||||
#include "stable-diffusion.h"
|
|
||||||
|
|
||||||
struct GenerationExtensionInitContext {
|
|
||||||
const sd_ctx_params_t* params;
|
|
||||||
SDVersion version;
|
|
||||||
const String2TensorStorage& tensor_storage_map;
|
|
||||||
ModelLoader& model_loader;
|
|
||||||
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 {
|
|
||||||
virtual ~GenerationExtension() = default;
|
|
||||||
|
|
||||||
virtual const char* name() const = 0;
|
|
||||||
virtual bool is_enabled() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
virtual bool init(const GenerationExtensionInitContext&) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual void collect_param_tensors(GenerationExtensionTensorContext&) {}
|
|
||||||
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 reset_runtime_condition() {}
|
|
||||||
virtual bool prepare_condition(GenerationExtensionConditionContext&) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
virtual const SDCondition& before_condition(int step,
|
|
||||||
const SDCondition& condition) const {
|
|
||||||
return condition;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<GenerationExtension> create_photomaker_extension();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,325 +0,0 @@
|
|||||||
#include "extensions/generation_extension.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <tuple>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#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>>
|
|
||||||
tokenize_photomaker_trigger(FrozenCLIPEmbedderWithCustomWords& clip_conditioner,
|
|
||||||
const std::string& text,
|
|
||||||
int trigger_token_count,
|
|
||||||
int32_t image_token) {
|
|
||||||
auto tokens_and_weights = clip_conditioner.tokenize(text);
|
|
||||||
std::vector<int> source_tokens = std::move(tokens_and_weights.first);
|
|
||||||
std::vector<float> source_weights = std::move(tokens_and_weights.second);
|
|
||||||
|
|
||||||
if (!source_tokens.empty() && source_tokens.front() == clip_conditioner.tokenizer.BOS_TOKEN_ID) {
|
|
||||||
source_tokens.erase(source_tokens.begin());
|
|
||||||
source_weights.erase(source_weights.begin());
|
|
||||||
}
|
|
||||||
if (!source_tokens.empty() && source_tokens.back() == clip_conditioner.tokenizer.EOS_TOKEN_ID) {
|
|
||||||
source_tokens.pop_back();
|
|
||||||
source_weights.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> tokens;
|
|
||||||
std::vector<float> weights;
|
|
||||||
int32_t class_idx = -1;
|
|
||||||
for (size_t i = 0; i < source_tokens.size(); i++) {
|
|
||||||
int token = source_tokens[i];
|
|
||||||
if (token == image_token) {
|
|
||||||
if (!tokens.empty()) {
|
|
||||||
class_idx = static_cast<int32_t>(tokens.size()) - 1;
|
|
||||||
int class_token = tokens.back();
|
|
||||||
float class_weight = weights.back();
|
|
||||||
for (int j = 1; j < trigger_token_count; j++) {
|
|
||||||
tokens.push_back(class_token);
|
|
||||||
weights.push_back(class_weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tokens.push_back(token);
|
|
||||||
weights.push_back(source_weights[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
clip_conditioner.tokenizer.pad_tokens(tokens,
|
|
||||||
&weights,
|
|
||||||
nullptr,
|
|
||||||
clip_conditioner.text_model->model.n_token,
|
|
||||||
clip_conditioner.text_model->model.n_token,
|
|
||||||
true);
|
|
||||||
std::vector<bool> class_token_mask;
|
|
||||||
for (int i = 0; i < tokens.size(); i++) {
|
|
||||||
class_token_mask.push_back(class_idx + 1 <= i && i < class_idx + 1 + trigger_token_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_tuple(tokens, weights, class_token_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::tuple<SDCondition, std::vector<bool>>
|
|
||||||
get_photomaker_condition_with_trigger(FrozenCLIPEmbedderWithCustomWords& clip_conditioner,
|
|
||||||
int n_threads,
|
|
||||||
const ConditionerParams& conditioner_params,
|
|
||||||
const std::string& trigger_word,
|
|
||||||
int trigger_token_count) {
|
|
||||||
auto image_tokens = clip_conditioner.convert_token_to_id(trigger_word);
|
|
||||||
GGML_ASSERT(image_tokens.size() == 1);
|
|
||||||
auto tokens_and_weights = tokenize_photomaker_trigger(clip_conditioner,
|
|
||||||
conditioner_params.text,
|
|
||||||
trigger_token_count,
|
|
||||||
image_tokens[0]);
|
|
||||||
std::vector<int>& tokens = std::get<0>(tokens_and_weights);
|
|
||||||
std::vector<float>& weights = std::get<1>(tokens_and_weights);
|
|
||||||
std::vector<bool>& trigger_mask = std::get<2>(tokens_and_weights);
|
|
||||||
auto cond = clip_conditioner.get_learned_condition_common(n_threads,
|
|
||||||
tokens,
|
|
||||||
weights,
|
|
||||||
conditioner_params.clip_skip,
|
|
||||||
conditioner_params.width,
|
|
||||||
conditioner_params.height,
|
|
||||||
conditioner_params.zero_out_masked);
|
|
||||||
return std::make_tuple(std::move(cond), trigger_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string remove_photomaker_trigger_from_prompt(FrozenCLIPEmbedderWithCustomWords& clip_conditioner,
|
|
||||||
const std::string& prompt,
|
|
||||||
const std::string& trigger_word) {
|
|
||||||
auto image_tokens = clip_conditioner.convert_token_to_id(trigger_word);
|
|
||||||
GGML_ASSERT(image_tokens.size() == 1);
|
|
||||||
auto tokens_and_weights = clip_conditioner.tokenize(prompt);
|
|
||||||
std::vector<int>& tokens = tokens_and_weights.first;
|
|
||||||
auto it = std::find(tokens.begin(), tokens.end(), image_tokens[0]);
|
|
||||||
GGML_ASSERT(it != tokens.end());
|
|
||||||
tokens.erase(it);
|
|
||||||
return clip_conditioner.decode(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
SDCondition id_condition;
|
|
||||||
int start_merge_step = -1;
|
|
||||||
|
|
||||||
const char* name() const override {
|
|
||||||
return "photomaker";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_enabled() const override {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init(const GenerationExtensionInitContext& ctx) override {
|
|
||||||
model_path = SAFE_STR(ctx.params->photo_maker_path);
|
|
||||||
if (model_path.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctx.ensure_backend_pair(SDBackendModule::PHOTOMAKER)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void collect_param_tensors(GenerationExtensionTensorContext& ctx) 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_ignore_tensors(std::set<std::string>& ignore_tensors) const override {
|
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ignore_tensors.insert("pmid.unet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool alloc_params_buffer() override {
|
|
||||||
if (!enabled || pmid_model == nullptr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
id_condition = {};
|
|
||||||
start_merge_step = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prepare_condition(GenerationExtensionConditionContext& ctx) override {
|
|
||||||
reset_runtime_condition();
|
|
||||||
if (!enabled || pmid_model == nullptr || pmid_lora == 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");
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto* clip_conditioner = dynamic_cast<FrozenCLIPEmbedderWithCustomWords*>(ctx.conditioner);
|
|
||||||
if (clip_conditioner == nullptr) {
|
|
||||||
LOG_WARN("PhotoMaker requires FrozenCLIPEmbedderWithCustomWords conditioner");
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int clip_image_size = 224;
|
|
||||||
pmid_model->style_strength = ctx.pm_params.style_strength;
|
|
||||||
sd::Tensor<float> id_image_tensor;
|
|
||||||
for (int i = 0; i < ctx.pm_params.id_images_count; i++) {
|
|
||||||
auto id_image = sd_image_to_tensor(ctx.pm_params.id_images[i]);
|
|
||||||
auto processed_id_image = clip_preprocess(id_image, clip_image_size, clip_image_size);
|
|
||||||
if (id_image_tensor.empty()) {
|
|
||||||
id_image_tensor = processed_id_image;
|
|
||||||
} else {
|
|
||||||
id_image_tensor = sd::ops::concat(id_image_tensor, processed_id_image, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t t0 = ggml_time_ms();
|
|
||||||
int trigger_token_count = pmv2 ? 2 * ctx.pm_params.id_images_count : ctx.pm_params.id_images_count;
|
|
||||||
auto cond_tup = get_photomaker_condition_with_trigger(*clip_conditioner,
|
|
||||||
ctx.n_threads,
|
|
||||||
ctx.condition_params,
|
|
||||||
trigger_word,
|
|
||||||
trigger_token_count);
|
|
||||||
SDCondition prepared_id_condition = std::get<0>(cond_tup);
|
|
||||||
auto class_tokens_mask = std::get<1>(cond_tup);
|
|
||||||
if (std::find(class_tokens_mask.begin(), class_tokens_mask.end(), true) == class_tokens_mask.end()) {
|
|
||||||
LOG_WARN("PhotoMaker trigger word '%s' was not found in prompt", trigger_word.c_str());
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> id_embeds;
|
|
||||||
if (pmv2 && ctx.pm_params.id_embed_path != nullptr) {
|
|
||||||
try {
|
|
||||||
id_embeds = sd::load_tensor_from_file_as_tensor<float>(ctx.pm_params.id_embed_path);
|
|
||||||
} catch (const std::exception&) {
|
|
||||||
id_embeds = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pmv2 && id_embeds.empty()) {
|
|
||||||
LOG_WARN("Provided PhotoMaker images, but NO valid ID embeds file for PM v2");
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (pmv2 && ctx.pm_params.id_images_count != id_embeds.shape()[1]) {
|
|
||||||
LOG_WARN("PhotoMaker image count (%d) does NOT match ID embeds (%d). You should run face_detect.py again.",
|
|
||||||
ctx.pm_params.id_images_count,
|
|
||||||
static_cast<int>(id_embeds.shape()[1]));
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = pmid_model->compute(ctx.n_threads,
|
|
||||||
id_image_tensor,
|
|
||||||
prepared_id_condition.c_crossattn,
|
|
||||||
id_embeds,
|
|
||||||
class_tokens_mask);
|
|
||||||
if (res.empty()) {
|
|
||||||
LOG_ERROR("Photomaker ID Stacking failed");
|
|
||||||
LOG_WARN("Turn off PhotoMaker for this request");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepared_id_condition.c_crossattn = std::move(res);
|
|
||||||
int64_t t1 = ggml_time_ms();
|
|
||||||
id_condition = std::move(prepared_id_condition);
|
|
||||||
start_merge_step = int(ctx.pm_params.style_strength / 100.f * ctx.total_steps);
|
|
||||||
ctx.condition_params.text = remove_photomaker_trigger_from_prompt(*clip_conditioner,
|
|
||||||
ctx.condition_params.text,
|
|
||||||
trigger_word);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SDCondition& before_condition(int step,
|
|
||||||
const SDCondition& condition) const override {
|
|
||||||
if (!id_condition.empty() && start_merge_step != -1 && step > start_merge_step) {
|
|
||||||
return id_condition;
|
|
||||||
}
|
|
||||||
return condition;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<GenerationExtension> create_photomaker_extension() {
|
|
||||||
return std::make_shared<PhotoMakerExtension>();
|
|
||||||
}
|
|
||||||
@ -1,167 +1,17 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_FLUX_HPP__
|
#ifndef __FLUX_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_FLUX_HPP__
|
#define __FLUX_HPP__
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "model/common/rope.hpp"
|
#include "common_dit.hpp"
|
||||||
#include "model/diffusion/dit.hpp"
|
#include "model.h"
|
||||||
#include "model/diffusion/model.hpp"
|
#include "rope.hpp"
|
||||||
#include "model_loader.h"
|
|
||||||
|
|
||||||
#define FLUX_GRAPH_SIZE 10240
|
#define FLUX_GRAPH_SIZE 10240
|
||||||
|
|
||||||
namespace Flux {
|
namespace Flux {
|
||||||
|
|
||||||
struct ChromaRadianceConfig {
|
|
||||||
int64_t nerf_hidden_size = 64;
|
|
||||||
int nerf_mlp_ratio = 4;
|
|
||||||
int nerf_depth = 4;
|
|
||||||
int nerf_max_freqs = 8;
|
|
||||||
bool use_x0 = false;
|
|
||||||
bool fake_patch_size_x2 = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FluxConfig {
|
|
||||||
SDVersion version = VERSION_FLUX;
|
|
||||||
bool is_chroma = false;
|
|
||||||
int patch_size = 2;
|
|
||||||
int64_t in_channels = 64;
|
|
||||||
int64_t out_channels = 64;
|
|
||||||
int64_t vec_in_dim = 768;
|
|
||||||
int64_t context_in_dim = 4096;
|
|
||||||
int64_t hidden_size = 3072;
|
|
||||||
float mlp_ratio = 4.0f;
|
|
||||||
int num_heads = 24;
|
|
||||||
int depth = 19;
|
|
||||||
int depth_single_blocks = 38;
|
|
||||||
std::vector<int> axes_dim = {16, 56, 56};
|
|
||||||
int axes_dim_sum = 128;
|
|
||||||
int theta = 10000;
|
|
||||||
bool qkv_bias = true;
|
|
||||||
bool guidance_embed = true;
|
|
||||||
int64_t in_dim = 64;
|
|
||||||
bool disable_bias = false;
|
|
||||||
bool share_modulation = false;
|
|
||||||
bool semantic_txt_norm = false;
|
|
||||||
bool use_yak_mlp = false;
|
|
||||||
bool use_mlp_silu_act = false;
|
|
||||||
float ref_index_scale = 1.f;
|
|
||||||
ChromaRadianceConfig chroma_radiance_params;
|
|
||||||
|
|
||||||
static FluxConfig detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
|
||||||
const std::string& prefix,
|
|
||||||
SDVersion version = VERSION_FLUX) {
|
|
||||||
FluxConfig config;
|
|
||||||
config.version = version;
|
|
||||||
config.guidance_embed = false;
|
|
||||||
config.depth = 0;
|
|
||||||
config.depth_single_blocks = 0;
|
|
||||||
if (version == VERSION_FLUX_FILL) {
|
|
||||||
config.in_channels = 384;
|
|
||||||
} else if (version == VERSION_FLUX_CONTROLS) {
|
|
||||||
config.in_channels = 128;
|
|
||||||
} else if (version == VERSION_FLEX_2) {
|
|
||||||
config.in_channels = 196;
|
|
||||||
} else if (version == VERSION_CHROMA_RADIANCE) {
|
|
||||||
config.in_channels = 3;
|
|
||||||
config.patch_size = 16;
|
|
||||||
} else if (version == VERSION_OVIS_IMAGE) {
|
|
||||||
config.semantic_txt_norm = true;
|
|
||||||
config.use_yak_mlp = true;
|
|
||||||
config.vec_in_dim = 0;
|
|
||||||
} else if (sd_version_is_flux2(version)) {
|
|
||||||
config.in_channels = 128;
|
|
||||||
config.patch_size = 1;
|
|
||||||
config.out_channels = 128;
|
|
||||||
config.mlp_ratio = 3.f;
|
|
||||||
config.theta = 2000;
|
|
||||||
config.axes_dim = {32, 32, 32, 32};
|
|
||||||
config.vec_in_dim = 0;
|
|
||||||
config.qkv_bias = false;
|
|
||||||
config.disable_bias = true;
|
|
||||||
config.share_modulation = true;
|
|
||||||
config.ref_index_scale = 10.f;
|
|
||||||
config.use_mlp_silu_act = true;
|
|
||||||
} else if (sd_version_is_longcat(version)) {
|
|
||||||
config.context_in_dim = 3584;
|
|
||||||
config.vec_in_dim = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t head_dim = 0;
|
|
||||||
int64_t actual_radiance_patch_size = -1;
|
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
|
||||||
if (!starts_with(name, prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (name.find("guidance_in.in_layer.weight") != std::string::npos) {
|
|
||||||
config.guidance_embed = true;
|
|
||||||
}
|
|
||||||
if (name.find("__x0__") != std::string::npos) {
|
|
||||||
LOG_DEBUG("using x0 prediction");
|
|
||||||
config.chroma_radiance_params.use_x0 = true;
|
|
||||||
}
|
|
||||||
if (name.find("__32x32__") != std::string::npos) {
|
|
||||||
LOG_DEBUG("using patch size 32");
|
|
||||||
config.patch_size = 32;
|
|
||||||
}
|
|
||||||
if (name.find("img_in_patch.weight") != std::string::npos) {
|
|
||||||
actual_radiance_patch_size = tensor_storage.ne[0];
|
|
||||||
LOG_DEBUG("actual radiance patch size: %" PRId64, actual_radiance_patch_size);
|
|
||||||
}
|
|
||||||
if (name.find("distilled_guidance_layer.in_proj.weight") != std::string::npos) {
|
|
||||||
config.is_chroma = true;
|
|
||||||
}
|
|
||||||
size_t db = name.find("double_blocks.");
|
|
||||||
if (db != std::string::npos) {
|
|
||||||
std::string block_name = name.substr(db);
|
|
||||||
int block_depth = atoi(block_name.substr(14, block_name.find(".", 14)).c_str());
|
|
||||||
if (block_depth + 1 > config.depth) {
|
|
||||||
config.depth = block_depth + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t sb = name.find("single_blocks.");
|
|
||||||
if (sb != std::string::npos) {
|
|
||||||
std::string block_name = name.substr(sb);
|
|
||||||
int block_depth = atoi(block_name.substr(14, block_name.find(".", 14)).c_str());
|
|
||||||
if (block_depth + 1 > config.depth_single_blocks) {
|
|
||||||
config.depth_single_blocks = block_depth + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ends_with(name, "txt_in.weight")) {
|
|
||||||
config.context_in_dim = tensor_storage.ne[0];
|
|
||||||
config.hidden_size = tensor_storage.ne[1];
|
|
||||||
}
|
|
||||||
if (ends_with(name, "single_blocks.0.norm.key_norm.scale")) {
|
|
||||||
head_dim = tensor_storage.ne[0];
|
|
||||||
}
|
|
||||||
if (ends_with(name, "double_blocks.0.txt_attn.norm.key_norm.scale")) {
|
|
||||||
head_dim = tensor_storage.ne[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (actual_radiance_patch_size > 0 && actual_radiance_patch_size != config.patch_size) {
|
|
||||||
GGML_ASSERT(config.patch_size == 2 * actual_radiance_patch_size);
|
|
||||||
LOG_DEBUG("using fake x2 patch size");
|
|
||||||
config.chroma_radiance_params.fake_patch_size_x2 = true;
|
|
||||||
}
|
|
||||||
if (head_dim > 0) {
|
|
||||||
config.num_heads = static_cast<int>(config.hidden_size / head_dim);
|
|
||||||
}
|
|
||||||
config.axes_dim_sum = 0;
|
|
||||||
for (int axis_dim : config.axes_dim) {
|
|
||||||
config.axes_dim_sum += axis_dim;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("flux: depth = %d, depth_single_blocks = %d, guidance_embed = %s, context_in_dim = %" PRId64 ", hidden_size = %" PRId64 ", num_heads = %d",
|
|
||||||
config.depth,
|
|
||||||
config.depth_single_blocks,
|
|
||||||
config.guidance_embed ? "true" : "false",
|
|
||||||
config.context_in_dim,
|
|
||||||
config.hidden_size,
|
|
||||||
config.num_heads);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MLPEmbedder : public UnaryBlock {
|
struct MLPEmbedder : public UnaryBlock {
|
||||||
public:
|
public:
|
||||||
MLPEmbedder(int64_t in_dim, int64_t hidden_dim, bool bias = true) {
|
MLPEmbedder(int64_t in_dim, int64_t hidden_dim, bool bias = true) {
|
||||||
@ -596,6 +446,7 @@ namespace Flux {
|
|||||||
if (use_yak_mlp || use_mlp_silu_act) {
|
if (use_yak_mlp || use_mlp_silu_act) {
|
||||||
mlp_mult_factor = 2;
|
mlp_mult_factor = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks["linear1"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, hidden_size * 3 + mlp_hidden_dim * mlp_mult_factor, mlp_proj_bias));
|
blocks["linear1"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, hidden_size * 3 + mlp_hidden_dim * mlp_mult_factor, mlp_proj_bias));
|
||||||
blocks["linear2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size + mlp_hidden_dim, hidden_size, mlp_proj_bias));
|
blocks["linear2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size + mlp_hidden_dim, hidden_size, mlp_proj_bias));
|
||||||
blocks["norm"] = std::shared_ptr<GGMLBlock>(new QKNorm(head_dim));
|
blocks["norm"] = std::shared_ptr<GGMLBlock>(new QKNorm(head_dim));
|
||||||
@ -872,90 +723,127 @@ namespace Flux {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ChromaRadianceParams {
|
||||||
|
int64_t nerf_hidden_size = 64;
|
||||||
|
int nerf_mlp_ratio = 4;
|
||||||
|
int nerf_depth = 4;
|
||||||
|
int nerf_max_freqs = 8;
|
||||||
|
bool use_x0 = false;
|
||||||
|
bool fake_patch_size_x2 = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FluxParams {
|
||||||
|
SDVersion version = VERSION_FLUX;
|
||||||
|
bool is_chroma = false;
|
||||||
|
int patch_size = 2;
|
||||||
|
int64_t in_channels = 64;
|
||||||
|
int64_t out_channels = 64;
|
||||||
|
int64_t vec_in_dim = 768;
|
||||||
|
int64_t context_in_dim = 4096;
|
||||||
|
int64_t hidden_size = 3072;
|
||||||
|
float mlp_ratio = 4.0f;
|
||||||
|
int num_heads = 24;
|
||||||
|
int depth = 19;
|
||||||
|
int depth_single_blocks = 38;
|
||||||
|
std::vector<int> axes_dim = {16, 56, 56};
|
||||||
|
int axes_dim_sum = 128;
|
||||||
|
int theta = 10000;
|
||||||
|
bool qkv_bias = true;
|
||||||
|
bool guidance_embed = true;
|
||||||
|
int64_t in_dim = 64;
|
||||||
|
bool disable_bias = false;
|
||||||
|
bool share_modulation = false;
|
||||||
|
bool semantic_txt_norm = false;
|
||||||
|
bool use_yak_mlp = false;
|
||||||
|
bool use_mlp_silu_act = false;
|
||||||
|
float ref_index_scale = 1.f;
|
||||||
|
ChromaRadianceParams chroma_radiance_params;
|
||||||
|
};
|
||||||
|
|
||||||
struct Flux : public GGMLBlock {
|
struct Flux : public GGMLBlock {
|
||||||
public:
|
public:
|
||||||
FluxConfig config;
|
FluxParams params;
|
||||||
Flux() {}
|
Flux() {}
|
||||||
Flux(FluxConfig config)
|
Flux(FluxParams params)
|
||||||
: config(config) {
|
: params(params) {
|
||||||
if (config.version == VERSION_CHROMA_RADIANCE) {
|
if (params.version == VERSION_CHROMA_RADIANCE) {
|
||||||
std::pair<int, int> kernel_size = {config.patch_size, config.patch_size};
|
std::pair<int, int> kernel_size = {params.patch_size, params.patch_size};
|
||||||
if (config.chroma_radiance_params.fake_patch_size_x2) {
|
if (params.chroma_radiance_params.fake_patch_size_x2) {
|
||||||
kernel_size = {config.patch_size / 2, config.patch_size / 2};
|
kernel_size = {params.patch_size / 2, params.patch_size / 2};
|
||||||
}
|
}
|
||||||
std::pair<int, int> stride = kernel_size;
|
std::pair<int, int> stride = kernel_size;
|
||||||
|
|
||||||
blocks["img_in_patch"] = std::make_shared<Conv2d>(config.in_channels,
|
blocks["img_in_patch"] = std::make_shared<Conv2d>(params.in_channels,
|
||||||
config.hidden_size,
|
params.hidden_size,
|
||||||
kernel_size,
|
kernel_size,
|
||||||
stride);
|
stride);
|
||||||
} else {
|
} else {
|
||||||
blocks["img_in"] = std::make_shared<Linear>(config.in_channels, config.hidden_size, !config.disable_bias);
|
blocks["img_in"] = std::make_shared<Linear>(params.in_channels, params.hidden_size, !params.disable_bias);
|
||||||
}
|
}
|
||||||
if (config.is_chroma) {
|
if (params.is_chroma) {
|
||||||
blocks["distilled_guidance_layer"] = std::make_shared<ChromaApproximator>(config.in_dim, config.hidden_size);
|
blocks["distilled_guidance_layer"] = std::make_shared<ChromaApproximator>(params.in_dim, params.hidden_size);
|
||||||
} else {
|
} else {
|
||||||
blocks["time_in"] = std::make_shared<MLPEmbedder>(256, config.hidden_size, !config.disable_bias);
|
blocks["time_in"] = std::make_shared<MLPEmbedder>(256, params.hidden_size, !params.disable_bias);
|
||||||
if (config.vec_in_dim > 0) {
|
if (params.vec_in_dim > 0) {
|
||||||
blocks["vector_in"] = std::make_shared<MLPEmbedder>(config.vec_in_dim, config.hidden_size, !config.disable_bias);
|
blocks["vector_in"] = std::make_shared<MLPEmbedder>(params.vec_in_dim, params.hidden_size, !params.disable_bias);
|
||||||
}
|
}
|
||||||
if (config.guidance_embed) {
|
if (params.guidance_embed) {
|
||||||
blocks["guidance_in"] = std::make_shared<MLPEmbedder>(256, config.hidden_size, !config.disable_bias);
|
blocks["guidance_in"] = std::make_shared<MLPEmbedder>(256, params.hidden_size, !params.disable_bias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config.semantic_txt_norm) {
|
if (params.semantic_txt_norm) {
|
||||||
blocks["txt_norm"] = std::make_shared<RMSNorm>(config.context_in_dim);
|
blocks["txt_norm"] = std::make_shared<RMSNorm>(params.context_in_dim);
|
||||||
}
|
}
|
||||||
blocks["txt_in"] = std::make_shared<Linear>(config.context_in_dim, config.hidden_size, !config.disable_bias);
|
blocks["txt_in"] = std::make_shared<Linear>(params.context_in_dim, params.hidden_size, !params.disable_bias);
|
||||||
|
|
||||||
for (int i = 0; i < config.depth; i++) {
|
for (int i = 0; i < params.depth; i++) {
|
||||||
blocks["double_blocks." + std::to_string(i)] = std::make_shared<DoubleStreamBlock>(config.hidden_size,
|
blocks["double_blocks." + std::to_string(i)] = std::make_shared<DoubleStreamBlock>(params.hidden_size,
|
||||||
config.num_heads,
|
params.num_heads,
|
||||||
config.mlp_ratio,
|
params.mlp_ratio,
|
||||||
i,
|
i,
|
||||||
config.qkv_bias,
|
params.qkv_bias,
|
||||||
config.is_chroma,
|
params.is_chroma,
|
||||||
config.share_modulation,
|
params.share_modulation,
|
||||||
!config.disable_bias,
|
!params.disable_bias,
|
||||||
config.use_yak_mlp,
|
params.use_yak_mlp,
|
||||||
config.use_mlp_silu_act);
|
params.use_mlp_silu_act);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < config.depth_single_blocks; i++) {
|
for (int i = 0; i < params.depth_single_blocks; i++) {
|
||||||
blocks["single_blocks." + std::to_string(i)] = std::make_shared<SingleStreamBlock>(config.hidden_size,
|
blocks["single_blocks." + std::to_string(i)] = std::make_shared<SingleStreamBlock>(params.hidden_size,
|
||||||
config.num_heads,
|
params.num_heads,
|
||||||
config.mlp_ratio,
|
params.mlp_ratio,
|
||||||
i,
|
i,
|
||||||
0.f,
|
0.f,
|
||||||
config.is_chroma,
|
params.is_chroma,
|
||||||
config.share_modulation,
|
params.share_modulation,
|
||||||
!config.disable_bias,
|
!params.disable_bias,
|
||||||
config.use_yak_mlp,
|
params.use_yak_mlp,
|
||||||
config.use_mlp_silu_act);
|
params.use_mlp_silu_act);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.version == VERSION_CHROMA_RADIANCE) {
|
if (params.version == VERSION_CHROMA_RADIANCE) {
|
||||||
blocks["nerf_image_embedder"] = std::make_shared<NerfEmbedder>(config.in_channels,
|
blocks["nerf_image_embedder"] = std::make_shared<NerfEmbedder>(params.in_channels,
|
||||||
config.chroma_radiance_params.nerf_hidden_size,
|
params.chroma_radiance_params.nerf_hidden_size,
|
||||||
config.chroma_radiance_params.nerf_max_freqs);
|
params.chroma_radiance_params.nerf_max_freqs);
|
||||||
|
|
||||||
for (int i = 0; i < config.chroma_radiance_params.nerf_depth; i++) {
|
for (int i = 0; i < params.chroma_radiance_params.nerf_depth; i++) {
|
||||||
blocks["nerf_blocks." + std::to_string(i)] = std::make_shared<NerfGLUBlock>(config.hidden_size,
|
blocks["nerf_blocks." + std::to_string(i)] = std::make_shared<NerfGLUBlock>(params.hidden_size,
|
||||||
config.chroma_radiance_params.nerf_hidden_size,
|
params.chroma_radiance_params.nerf_hidden_size,
|
||||||
config.chroma_radiance_params.nerf_mlp_ratio);
|
params.chroma_radiance_params.nerf_mlp_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks["nerf_final_layer_conv"] = std::make_shared<NerfFinalLayerConv>(config.chroma_radiance_params.nerf_hidden_size,
|
blocks["nerf_final_layer_conv"] = std::make_shared<NerfFinalLayerConv>(params.chroma_radiance_params.nerf_hidden_size,
|
||||||
config.in_channels);
|
params.in_channels);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
blocks["final_layer"] = std::make_shared<LastLayer>(config.hidden_size, 1, config.out_channels, config.is_chroma, !config.disable_bias);
|
blocks["final_layer"] = std::make_shared<LastLayer>(params.hidden_size, 1, params.out_channels, params.is_chroma, !params.disable_bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.share_modulation) {
|
if (params.share_modulation) {
|
||||||
blocks["double_stream_modulation_img"] = std::make_shared<Modulation>(config.hidden_size, true, !config.disable_bias);
|
blocks["double_stream_modulation_img"] = std::make_shared<Modulation>(params.hidden_size, true, !params.disable_bias);
|
||||||
blocks["double_stream_modulation_txt"] = std::make_shared<Modulation>(config.hidden_size, true, !config.disable_bias);
|
blocks["double_stream_modulation_txt"] = std::make_shared<Modulation>(params.hidden_size, true, !params.disable_bias);
|
||||||
blocks["single_stream_modulation"] = std::make_shared<Modulation>(config.hidden_size, false, !config.disable_bias);
|
blocks["single_stream_modulation"] = std::make_shared<Modulation>(params.hidden_size, false, !params.disable_bias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,7 +866,7 @@ namespace Flux {
|
|||||||
|
|
||||||
ggml_tensor* vec;
|
ggml_tensor* vec;
|
||||||
ggml_tensor* txt_img_mask = nullptr;
|
ggml_tensor* txt_img_mask = nullptr;
|
||||||
if (config.is_chroma) {
|
if (params.is_chroma) {
|
||||||
int64_t mod_index_length = 344;
|
int64_t mod_index_length = 344;
|
||||||
auto approx = std::dynamic_pointer_cast<ChromaApproximator>(blocks["distilled_guidance_layer"]);
|
auto approx = std::dynamic_pointer_cast<ChromaApproximator>(blocks["distilled_guidance_layer"]);
|
||||||
auto distill_timestep = ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, 16, 10000, 1000.f);
|
auto distill_timestep = ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, 16, 10000, 1000.f);
|
||||||
@ -1006,7 +894,7 @@ namespace Flux {
|
|||||||
} else {
|
} else {
|
||||||
auto time_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["time_in"]);
|
auto time_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["time_in"]);
|
||||||
vec = time_in->forward(ctx, ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, 256, 10000, 1000.f));
|
vec = time_in->forward(ctx, ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, 256, 10000, 1000.f));
|
||||||
if (config.guidance_embed) {
|
if (params.guidance_embed) {
|
||||||
GGML_ASSERT(guidance != nullptr);
|
GGML_ASSERT(guidance != nullptr);
|
||||||
auto guidance_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["guidance_in"]);
|
auto guidance_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["guidance_in"]);
|
||||||
// bf16 and fp16 result is different
|
// bf16 and fp16 result is different
|
||||||
@ -1014,7 +902,7 @@ namespace Flux {
|
|||||||
vec = ggml_add(ctx->ggml_ctx, vec, guidance_in->forward(ctx, g_in));
|
vec = ggml_add(ctx->ggml_ctx, vec, guidance_in->forward(ctx, g_in));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.vec_in_dim > 0) {
|
if (params.vec_in_dim > 0) {
|
||||||
auto vector_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["vector_in"]);
|
auto vector_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["vector_in"]);
|
||||||
vec = ggml_add(ctx->ggml_ctx, vec, vector_in->forward(ctx, y));
|
vec = ggml_add(ctx->ggml_ctx, vec, vector_in->forward(ctx, y));
|
||||||
}
|
}
|
||||||
@ -1023,7 +911,7 @@ namespace Flux {
|
|||||||
std::vector<ModulationOut> ds_img_mods;
|
std::vector<ModulationOut> ds_img_mods;
|
||||||
std::vector<ModulationOut> ds_txt_mods;
|
std::vector<ModulationOut> ds_txt_mods;
|
||||||
std::vector<ModulationOut> ss_mods;
|
std::vector<ModulationOut> ss_mods;
|
||||||
if (config.share_modulation) {
|
if (params.share_modulation) {
|
||||||
auto double_stream_modulation_img = std::dynamic_pointer_cast<Modulation>(blocks["double_stream_modulation_img"]);
|
auto double_stream_modulation_img = std::dynamic_pointer_cast<Modulation>(blocks["double_stream_modulation_img"]);
|
||||||
auto double_stream_modulation_txt = std::dynamic_pointer_cast<Modulation>(blocks["double_stream_modulation_txt"]);
|
auto double_stream_modulation_txt = std::dynamic_pointer_cast<Modulation>(blocks["double_stream_modulation_txt"]);
|
||||||
auto single_stream_modulation = std::dynamic_pointer_cast<Modulation>(blocks["single_stream_modulation"]);
|
auto single_stream_modulation = std::dynamic_pointer_cast<Modulation>(blocks["single_stream_modulation"]);
|
||||||
@ -1033,18 +921,15 @@ namespace Flux {
|
|||||||
ss_mods = single_stream_modulation->forward(ctx, vec);
|
ss_mods = single_stream_modulation->forward(ctx, vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.semantic_txt_norm) {
|
if (params.semantic_txt_norm) {
|
||||||
auto semantic_txt_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm"]);
|
auto semantic_txt_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm"]);
|
||||||
|
|
||||||
txt = semantic_txt_norm->forward(ctx, txt);
|
txt = semantic_txt_norm->forward(ctx, txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
txt = txt_in->forward(ctx, txt);
|
txt = txt_in->forward(ctx, txt);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(img, "flux.prelude", "img");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(txt, "flux.prelude", "txt");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(vec, "flux.prelude", "vec");
|
|
||||||
|
|
||||||
for (int i = 0; i < config.depth; i++) {
|
for (int i = 0; i < params.depth; i++) {
|
||||||
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i) != skip_layers.end()) {
|
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i) != skip_layers.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1054,19 +939,16 @@ namespace Flux {
|
|||||||
auto img_txt = block->forward(ctx, img, txt, vec, pe, txt_img_mask, ds_img_mods, ds_txt_mods);
|
auto img_txt = block->forward(ctx, img, txt, vec, pe, txt_img_mask, ds_img_mods, ds_txt_mods);
|
||||||
img = img_txt.first; // [N, n_img_token, hidden_size]
|
img = img_txt.first; // [N, n_img_token, hidden_size]
|
||||||
txt = img_txt.second; // [N, n_txt_token, hidden_size]
|
txt = img_txt.second; // [N, n_txt_token, hidden_size]
|
||||||
sd::ggml_graph_cut::mark_graph_cut(img, "flux.double_blocks." + std::to_string(i), "img");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(txt, "flux.double_blocks." + std::to_string(i), "txt");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto txt_img = ggml_concat(ctx->ggml_ctx, txt, img, 1); // [N, n_txt_token + n_img_token, hidden_size]
|
auto txt_img = ggml_concat(ctx->ggml_ctx, txt, img, 1); // [N, n_txt_token + n_img_token, hidden_size]
|
||||||
for (int i = 0; i < config.depth_single_blocks; i++) {
|
for (int i = 0; i < params.depth_single_blocks; i++) {
|
||||||
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i + config.depth) != skip_layers.end()) {
|
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i + params.depth) != skip_layers.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto block = std::dynamic_pointer_cast<SingleStreamBlock>(blocks["single_blocks." + std::to_string(i)]);
|
auto block = std::dynamic_pointer_cast<SingleStreamBlock>(blocks["single_blocks." + std::to_string(i)]);
|
||||||
|
|
||||||
txt_img = block->forward(ctx, txt_img, vec, pe, txt_img_mask, ss_mods);
|
txt_img = block->forward(ctx, txt_img, vec, pe, txt_img_mask, ss_mods);
|
||||||
sd::ggml_graph_cut::mark_graph_cut(txt_img, "flux.single_blocks." + std::to_string(i), "txt_img");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img = ggml_view_3d(ctx->ggml_ctx,
|
img = ggml_view_3d(ctx->ggml_ctx,
|
||||||
@ -1111,14 +993,14 @@ namespace Flux {
|
|||||||
int64_t W = x->ne[0];
|
int64_t W = x->ne[0];
|
||||||
int64_t H = x->ne[1];
|
int64_t H = x->ne[1];
|
||||||
int64_t C = x->ne[2];
|
int64_t C = x->ne[2];
|
||||||
int patch_size = config.patch_size;
|
int patch_size = params.patch_size;
|
||||||
int pad_h = (patch_size - H % patch_size) % patch_size;
|
int pad_h = (patch_size - H % patch_size) % patch_size;
|
||||||
int pad_w = (patch_size - W % patch_size) % patch_size;
|
int pad_w = (patch_size - W % patch_size) % patch_size;
|
||||||
|
|
||||||
auto img = DiT::pad_to_patch_size(ctx, x, config.patch_size, config.patch_size);
|
auto img = DiT::pad_to_patch_size(ctx, x, params.patch_size, params.patch_size);
|
||||||
auto orig_img = img;
|
auto orig_img = img;
|
||||||
|
|
||||||
if (config.chroma_radiance_params.fake_patch_size_x2) {
|
if (params.chroma_radiance_params.fake_patch_size_x2) {
|
||||||
// It's supposed to be using GGML_SCALE_MODE_NEAREST, but this seems more stable
|
// It's supposed to be using GGML_SCALE_MODE_NEAREST, but this seems more stable
|
||||||
// Maybe the implementation of nearest-neighbor interpolation in ggml behaves differently than the one in PyTorch?
|
// Maybe the implementation of nearest-neighbor interpolation in ggml behaves differently than the one in PyTorch?
|
||||||
// img = F.interpolate(img, size=(H//2, W//2), mode="nearest")
|
// img = F.interpolate(img, size=(H//2, W//2), mode="nearest")
|
||||||
@ -1149,7 +1031,7 @@ namespace Flux {
|
|||||||
auto nerf_hidden = ggml_reshape_2d(ctx->ggml_ctx, out, out->ne[0], out->ne[1] * out->ne[2]); // [N*num_patches, hidden_size]
|
auto nerf_hidden = ggml_reshape_2d(ctx->ggml_ctx, out, out->ne[0], out->ne[1] * out->ne[2]); // [N*num_patches, hidden_size]
|
||||||
auto img_dct = nerf_image_embedder->forward(ctx, nerf_pixels, dct); // [N*num_patches, patch_size*patch_size, nerf_hidden_size]
|
auto img_dct = nerf_image_embedder->forward(ctx, nerf_pixels, dct); // [N*num_patches, patch_size*patch_size, nerf_hidden_size]
|
||||||
|
|
||||||
for (int i = 0; i < config.chroma_radiance_params.nerf_depth; i++) {
|
for (int i = 0; i < params.chroma_radiance_params.nerf_depth; i++) {
|
||||||
auto block = std::dynamic_pointer_cast<NerfGLUBlock>(blocks["nerf_blocks." + std::to_string(i)]);
|
auto block = std::dynamic_pointer_cast<NerfGLUBlock>(blocks["nerf_blocks." + std::to_string(i)]);
|
||||||
|
|
||||||
img_dct = block->forward(ctx, img_dct, nerf_hidden);
|
img_dct = block->forward(ctx, img_dct, nerf_hidden);
|
||||||
@ -1161,7 +1043,7 @@ namespace Flux {
|
|||||||
|
|
||||||
out = nerf_final_layer_conv->forward(ctx, img_dct); // [N, C, H, W]
|
out = nerf_final_layer_conv->forward(ctx, img_dct); // [N, C, H, W]
|
||||||
|
|
||||||
if (config.chroma_radiance_params.use_x0) {
|
if (params.chroma_radiance_params.use_x0) {
|
||||||
out = _apply_x0_residual(ctx, out, orig_img, timestep);
|
out = _apply_x0_residual(ctx, out, orig_img, timestep);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1185,14 +1067,14 @@ namespace Flux {
|
|||||||
int64_t W = x->ne[0];
|
int64_t W = x->ne[0];
|
||||||
int64_t H = x->ne[1];
|
int64_t H = x->ne[1];
|
||||||
int64_t C = x->ne[2];
|
int64_t C = x->ne[2];
|
||||||
int patch_size = config.patch_size;
|
int patch_size = params.patch_size;
|
||||||
int pad_h = (patch_size - H % patch_size) % patch_size;
|
int pad_h = (patch_size - H % patch_size) % patch_size;
|
||||||
int pad_w = (patch_size - W % patch_size) % patch_size;
|
int pad_w = (patch_size - W % patch_size) % patch_size;
|
||||||
|
|
||||||
auto img = DiT::pad_and_patchify(ctx, x, patch_size, patch_size);
|
auto img = DiT::pad_and_patchify(ctx, x, patch_size, patch_size);
|
||||||
int64_t img_tokens = img->ne[1];
|
int64_t img_tokens = img->ne[1];
|
||||||
|
|
||||||
if (config.version == VERSION_FLUX_FILL) {
|
if (params.version == VERSION_FLUX_FILL) {
|
||||||
GGML_ASSERT(c_concat != nullptr);
|
GGML_ASSERT(c_concat != nullptr);
|
||||||
ggml_tensor* masked = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], C, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], 0);
|
ggml_tensor* masked = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], C, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], 0);
|
||||||
ggml_tensor* mask = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], 8 * 8, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], c_concat->nb[2] * C);
|
ggml_tensor* mask = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], 8 * 8, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], c_concat->nb[2] * C);
|
||||||
@ -1201,7 +1083,7 @@ namespace Flux {
|
|||||||
mask = DiT::pad_and_patchify(ctx, mask, patch_size, patch_size);
|
mask = DiT::pad_and_patchify(ctx, mask, patch_size, patch_size);
|
||||||
|
|
||||||
img = ggml_concat(ctx->ggml_ctx, img, ggml_concat(ctx->ggml_ctx, masked, mask, 0), 0);
|
img = ggml_concat(ctx->ggml_ctx, img, ggml_concat(ctx->ggml_ctx, masked, mask, 0), 0);
|
||||||
} else if (config.version == VERSION_FLEX_2) {
|
} else if (params.version == VERSION_FLEX_2) {
|
||||||
GGML_ASSERT(c_concat != nullptr);
|
GGML_ASSERT(c_concat != nullptr);
|
||||||
ggml_tensor* masked = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], C, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], 0);
|
ggml_tensor* masked = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], C, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], 0);
|
||||||
ggml_tensor* mask = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], 1, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], c_concat->nb[2] * C);
|
ggml_tensor* mask = ggml_view_4d(ctx->ggml_ctx, c_concat, c_concat->ne[0], c_concat->ne[1], 1, 1, c_concat->nb[1], c_concat->nb[2], c_concat->nb[3], c_concat->nb[2] * C);
|
||||||
@ -1212,7 +1094,7 @@ namespace Flux {
|
|||||||
control = DiT::pad_and_patchify(ctx, control, patch_size, patch_size);
|
control = DiT::pad_and_patchify(ctx, control, patch_size, patch_size);
|
||||||
|
|
||||||
img = ggml_concat(ctx->ggml_ctx, img, ggml_concat(ctx->ggml_ctx, ggml_concat(ctx->ggml_ctx, masked, mask, 0), control, 0), 0);
|
img = ggml_concat(ctx->ggml_ctx, img, ggml_concat(ctx->ggml_ctx, ggml_concat(ctx->ggml_ctx, masked, mask, 0), control, 0), 0);
|
||||||
} else if (config.version == VERSION_FLUX_CONTROLS) {
|
} else if (params.version == VERSION_FLUX_CONTROLS) {
|
||||||
GGML_ASSERT(c_concat != nullptr);
|
GGML_ASSERT(c_concat != nullptr);
|
||||||
|
|
||||||
auto control = DiT::pad_and_patchify(ctx, c_concat, patch_size, patch_size);
|
auto control = DiT::pad_and_patchify(ctx, c_concat, patch_size, patch_size);
|
||||||
@ -1259,7 +1141,7 @@ namespace Flux {
|
|||||||
// pe: (L, d_head/2, 2, 2)
|
// pe: (L, d_head/2, 2, 2)
|
||||||
// return: (N, C, H, W)
|
// return: (N, C, H, W)
|
||||||
|
|
||||||
if (config.version == VERSION_CHROMA_RADIANCE) {
|
if (params.version == VERSION_CHROMA_RADIANCE) {
|
||||||
return forward_chroma_radiance(ctx,
|
return forward_chroma_radiance(ctx,
|
||||||
x,
|
x,
|
||||||
timestep,
|
timestep,
|
||||||
@ -1289,9 +1171,9 @@ namespace Flux {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FluxRunner : public DiffusionModelRunner {
|
struct FluxRunner : public GGMLRunner {
|
||||||
public:
|
public:
|
||||||
FluxConfig config;
|
FluxParams flux_params;
|
||||||
Flux flux;
|
Flux flux;
|
||||||
std::vector<float> pe_vec;
|
std::vector<float> pe_vec;
|
||||||
std::vector<float> mod_index_arange_vec;
|
std::vector<float> mod_index_arange_vec;
|
||||||
@ -1301,20 +1183,116 @@ namespace Flux {
|
|||||||
bool use_mask = false;
|
bool use_mask = false;
|
||||||
|
|
||||||
FluxRunner(ggml_backend_t backend,
|
FluxRunner(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
const std::string prefix = "",
|
const std::string prefix = "",
|
||||||
SDVersion version = VERSION_FLUX,
|
SDVersion version = VERSION_FLUX,
|
||||||
bool use_mask = false)
|
bool use_mask = false)
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
: GGMLRunner(backend, offload_params_to_cpu), version(version), use_mask(use_mask) {
|
||||||
config(FluxConfig::detect_from_weights(tensor_storage_map, prefix, version)),
|
flux_params.version = version;
|
||||||
version(version),
|
flux_params.guidance_embed = false;
|
||||||
use_mask(use_mask) {
|
flux_params.depth = 0;
|
||||||
if (config.is_chroma) {
|
flux_params.depth_single_blocks = 0;
|
||||||
|
if (version == VERSION_FLUX_FILL) {
|
||||||
|
flux_params.in_channels = 384;
|
||||||
|
} else if (version == VERSION_FLUX_CONTROLS) {
|
||||||
|
flux_params.in_channels = 128;
|
||||||
|
} else if (version == VERSION_FLEX_2) {
|
||||||
|
flux_params.in_channels = 196;
|
||||||
|
} else if (version == VERSION_CHROMA_RADIANCE) {
|
||||||
|
flux_params.in_channels = 3;
|
||||||
|
flux_params.patch_size = 16;
|
||||||
|
} else if (version == VERSION_OVIS_IMAGE) {
|
||||||
|
flux_params.semantic_txt_norm = true;
|
||||||
|
flux_params.use_yak_mlp = true;
|
||||||
|
flux_params.vec_in_dim = 0;
|
||||||
|
} else if (sd_version_is_flux2(version)) {
|
||||||
|
flux_params.in_channels = 128;
|
||||||
|
flux_params.patch_size = 1;
|
||||||
|
flux_params.out_channels = 128;
|
||||||
|
flux_params.mlp_ratio = 3.f;
|
||||||
|
flux_params.theta = 2000;
|
||||||
|
flux_params.axes_dim = {32, 32, 32, 32};
|
||||||
|
flux_params.vec_in_dim = 0;
|
||||||
|
flux_params.qkv_bias = false;
|
||||||
|
flux_params.disable_bias = true;
|
||||||
|
flux_params.share_modulation = true;
|
||||||
|
flux_params.ref_index_scale = 10.f;
|
||||||
|
flux_params.use_mlp_silu_act = true;
|
||||||
|
}
|
||||||
|
int64_t head_dim = 0;
|
||||||
|
int64_t actual_radiance_patch_size = -1;
|
||||||
|
for (auto pair : tensor_storage_map) {
|
||||||
|
std::string tensor_name = pair.first;
|
||||||
|
if (!starts_with(tensor_name, prefix))
|
||||||
|
continue;
|
||||||
|
if (tensor_name.find("guidance_in.in_layer.weight") != std::string::npos) {
|
||||||
|
flux_params.guidance_embed = true;
|
||||||
|
}
|
||||||
|
if (tensor_name.find("__x0__") != std::string::npos) {
|
||||||
|
LOG_DEBUG("using x0 prediction");
|
||||||
|
flux_params.chroma_radiance_params.use_x0 = true;
|
||||||
|
}
|
||||||
|
if (tensor_name.find("__32x32__") != std::string::npos) {
|
||||||
|
LOG_DEBUG("using patch size 32");
|
||||||
|
flux_params.patch_size = 32;
|
||||||
|
}
|
||||||
|
if (tensor_name.find("img_in_patch.weight") != std::string::npos) {
|
||||||
|
actual_radiance_patch_size = pair.second.ne[0];
|
||||||
|
LOG_DEBUG("actual radiance patch size: %d", actual_radiance_patch_size);
|
||||||
|
}
|
||||||
|
if (tensor_name.find("distilled_guidance_layer.in_proj.weight") != std::string::npos) {
|
||||||
|
// Chroma
|
||||||
|
flux_params.is_chroma = true;
|
||||||
|
}
|
||||||
|
size_t db = tensor_name.find("double_blocks.");
|
||||||
|
if (db != std::string::npos) {
|
||||||
|
tensor_name = tensor_name.substr(db); // remove prefix
|
||||||
|
int block_depth = atoi(tensor_name.substr(14, tensor_name.find(".", 14)).c_str());
|
||||||
|
if (block_depth + 1 > flux_params.depth) {
|
||||||
|
flux_params.depth = block_depth + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t sb = tensor_name.find("single_blocks.");
|
||||||
|
if (sb != std::string::npos) {
|
||||||
|
tensor_name = tensor_name.substr(sb); // remove prefix
|
||||||
|
int block_depth = atoi(tensor_name.substr(14, tensor_name.find(".", 14)).c_str());
|
||||||
|
if (block_depth + 1 > flux_params.depth_single_blocks) {
|
||||||
|
flux_params.depth_single_blocks = block_depth + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ends_with(tensor_name, "txt_in.weight")) {
|
||||||
|
flux_params.context_in_dim = pair.second.ne[0];
|
||||||
|
flux_params.hidden_size = pair.second.ne[1];
|
||||||
|
}
|
||||||
|
if (ends_with(tensor_name, "single_blocks.0.norm.key_norm.scale")) {
|
||||||
|
head_dim = pair.second.ne[0];
|
||||||
|
}
|
||||||
|
if (ends_with(tensor_name, "double_blocks.0.txt_attn.norm.key_norm.scale")) {
|
||||||
|
head_dim = pair.second.ne[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (actual_radiance_patch_size > 0 && actual_radiance_patch_size != flux_params.patch_size) {
|
||||||
|
GGML_ASSERT(flux_params.patch_size == 2 * actual_radiance_patch_size);
|
||||||
|
LOG_DEBUG("using fake x2 patch size");
|
||||||
|
flux_params.chroma_radiance_params.fake_patch_size_x2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
flux_params.num_heads = static_cast<int>(flux_params.hidden_size / head_dim);
|
||||||
|
|
||||||
|
LOG_INFO("flux: depth = %d, depth_single_blocks = %d, guidance_embed = %s, context_in_dim = %" PRId64
|
||||||
|
", hidden_size = %" PRId64 ", num_heads = %d",
|
||||||
|
flux_params.depth,
|
||||||
|
flux_params.depth_single_blocks,
|
||||||
|
flux_params.guidance_embed ? "true" : "false",
|
||||||
|
flux_params.context_in_dim,
|
||||||
|
flux_params.hidden_size,
|
||||||
|
flux_params.num_heads);
|
||||||
|
if (flux_params.is_chroma) {
|
||||||
LOG_INFO("Using pruned modulation (Chroma)");
|
LOG_INFO("Using pruned modulation (Chroma)");
|
||||||
}
|
}
|
||||||
|
|
||||||
flux = Flux(config);
|
flux = Flux(flux_params);
|
||||||
flux.init(params_ctx, tensor_storage_map, prefix);
|
flux.init(params_ctx, tensor_storage_map, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1322,7 +1300,7 @@ namespace Flux {
|
|||||||
return "flux";
|
return "flux";
|
||||||
}
|
}
|
||||||
|
|
||||||
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, const std::string prefix) {
|
||||||
flux.get_param_tensors(tensors, prefix);
|
flux.get_param_tensors(tensors, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1390,10 +1368,10 @@ namespace Flux {
|
|||||||
ggml_tensor* context = make_optional_input(context_tensor);
|
ggml_tensor* context = make_optional_input(context_tensor);
|
||||||
ggml_tensor* c_concat = make_optional_input(c_concat_tensor);
|
ggml_tensor* c_concat = make_optional_input(c_concat_tensor);
|
||||||
ggml_tensor* y = make_optional_input(y_tensor);
|
ggml_tensor* y = make_optional_input(y_tensor);
|
||||||
if (config.guidance_embed || config.is_chroma) {
|
if (flux_params.guidance_embed || flux_params.is_chroma) {
|
||||||
if (!guidance_tensor.empty()) {
|
if (!guidance_tensor.empty()) {
|
||||||
this->guidance_tensor = guidance_tensor;
|
this->guidance_tensor = guidance_tensor;
|
||||||
if (config.is_chroma) {
|
if (flux_params.is_chroma) {
|
||||||
this->guidance_tensor.fill_(0.f);
|
this->guidance_tensor.fill_(0.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1411,7 +1389,7 @@ namespace Flux {
|
|||||||
ggml_tensor* mod_index_arange = nullptr;
|
ggml_tensor* mod_index_arange = nullptr;
|
||||||
ggml_tensor* dct = nullptr; // for chroma radiance
|
ggml_tensor* dct = nullptr; // for chroma radiance
|
||||||
|
|
||||||
if (config.is_chroma) {
|
if (flux_params.is_chroma) {
|
||||||
if (!use_mask) {
|
if (!use_mask) {
|
||||||
y = nullptr;
|
y = nullptr;
|
||||||
}
|
}
|
||||||
@ -1428,31 +1406,31 @@ namespace Flux {
|
|||||||
} else if (version == VERSION_OVIS_IMAGE) {
|
} else if (version == VERSION_OVIS_IMAGE) {
|
||||||
txt_arange_dims = {1, 2};
|
txt_arange_dims = {1, 2};
|
||||||
}
|
}
|
||||||
|
|
||||||
pe_vec = Rope::gen_flux_pe(static_cast<int>(x->ne[1]),
|
pe_vec = Rope::gen_flux_pe(static_cast<int>(x->ne[1]),
|
||||||
static_cast<int>(x->ne[0]),
|
static_cast<int>(x->ne[0]),
|
||||||
config.patch_size,
|
flux_params.patch_size,
|
||||||
static_cast<int>(x->ne[3]),
|
static_cast<int>(x->ne[3]),
|
||||||
static_cast<int>(context->ne[1]),
|
static_cast<int>(context->ne[1]),
|
||||||
txt_arange_dims,
|
txt_arange_dims,
|
||||||
ref_latents,
|
ref_latents,
|
||||||
increase_ref_index,
|
increase_ref_index,
|
||||||
config.ref_index_scale,
|
flux_params.ref_index_scale,
|
||||||
config.theta,
|
flux_params.theta,
|
||||||
circular_y_enabled,
|
circular_y_enabled,
|
||||||
circular_x_enabled,
|
circular_x_enabled,
|
||||||
config.axes_dim,
|
flux_params.axes_dim);
|
||||||
sd_version_is_longcat(version));
|
int pos_len = static_cast<int>(pe_vec.size() / flux_params.axes_dim_sum / 2);
|
||||||
int pos_len = static_cast<int>(pe_vec.size() / config.axes_dim_sum / 2);
|
|
||||||
// LOG_DEBUG("pos_len %d", pos_len);
|
// LOG_DEBUG("pos_len %d", pos_len);
|
||||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, config.axes_dim_sum / 2, pos_len);
|
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, flux_params.axes_dim_sum / 2, pos_len);
|
||||||
// pe->data = pe_vec.data();
|
// pe->data = pe_vec.data();
|
||||||
// print_ggml_tensor(pe);
|
// print_ggml_tensor(pe);
|
||||||
// pe->data = nullptr;
|
// pe->data = nullptr;
|
||||||
set_backend_tensor_data(pe, pe_vec.data());
|
set_backend_tensor_data(pe, pe_vec.data());
|
||||||
|
|
||||||
if (version == VERSION_CHROMA_RADIANCE) {
|
if (version == VERSION_CHROMA_RADIANCE) {
|
||||||
int patch_size = config.patch_size;
|
int patch_size = flux_params.patch_size;
|
||||||
int nerf_max_freqs = config.chroma_radiance_params.nerf_max_freqs;
|
int nerf_max_freqs = flux_params.chroma_radiance_params.nerf_max_freqs;
|
||||||
dct_vec = fetch_dct_pos(patch_size, nerf_max_freqs);
|
dct_vec = fetch_dct_pos(patch_size, nerf_max_freqs);
|
||||||
dct = ggml_new_tensor_2d(compute_ctx, GGML_TYPE_F32, nerf_max_freqs * nerf_max_freqs, patch_size * patch_size);
|
dct = ggml_new_tensor_2d(compute_ctx, GGML_TYPE_F32, nerf_max_freqs * nerf_max_freqs, patch_size * patch_size);
|
||||||
// dct->data = dct_vec.data();
|
// dct->data = dct_vec.data();
|
||||||
@ -1504,25 +1482,6 @@ namespace Flux {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
const auto* extra = diffusion_extra_as<FluxDiffusionExtra>(diffusion_params);
|
|
||||||
static const std::vector<sd::Tensor<float>> empty_ref_latents;
|
|
||||||
static const std::vector<int> empty_skip_layers;
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context),
|
|
||||||
tensor_or_empty(diffusion_params.c_concat),
|
|
||||||
tensor_or_empty(diffusion_params.y),
|
|
||||||
tensor_or_empty(extra->guidance),
|
|
||||||
diffusion_params.ref_latents ? *diffusion_params.ref_latents : empty_ref_latents,
|
|
||||||
diffusion_params.increase_ref_index,
|
|
||||||
extra->skip_layers ? *extra->skip_layers : empty_skip_layers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test() {
|
void test() {
|
||||||
ggml_init_params params;
|
ggml_init_params params;
|
||||||
params.mem_size = static_cast<size_t>(1024 * 1024) * 1024; // 1GB
|
params.mem_size = static_cast<size_t>(1024 * 1024) * 1024; // 1GB
|
||||||
@ -1580,7 +1539,7 @@ namespace Flux {
|
|||||||
|
|
||||||
static void load_from_file_and_test(const std::string& file_path) {
|
static void load_from_file_and_test(const std::string& file_path) {
|
||||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||||
ggml_backend_t backend = sd_backend_cpu_init();
|
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||||
ggml_type model_data_type = GGML_TYPE_COUNT;
|
ggml_type model_data_type = GGML_TYPE_COUNT;
|
||||||
|
|
||||||
ModelLoader model_loader;
|
ModelLoader model_loader;
|
||||||
@ -1599,17 +1558,13 @@ namespace Flux {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<FluxRunner> flux = std::make_shared<FluxRunner>(backend,
|
std::shared_ptr<FluxRunner> flux = std::make_shared<FluxRunner>(backend,
|
||||||
backend,
|
false,
|
||||||
tensor_storage_map,
|
tensor_storage_map,
|
||||||
"model.diffusion_model",
|
"model.diffusion_model",
|
||||||
VERSION_FLUX2,
|
VERSION_FLUX2,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
if (!flux->alloc_params_buffer()) {
|
flux->alloc_params_buffer();
|
||||||
LOG_ERROR("flux model allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ggml_tensor*> tensors;
|
std::map<std::string, ggml_tensor*> tensors;
|
||||||
flux->get_param_tensors(tensors, "model.diffusion_model");
|
flux->get_param_tensors(tensors, "model.diffusion_model");
|
||||||
|
|
||||||
@ -1627,4 +1582,4 @@ namespace Flux {
|
|||||||
|
|
||||||
} // namespace Flux
|
} // namespace Flux
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_FLUX_HPP__
|
#endif // __FLUX_HPP__
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
|||||||
#ifndef __SD_MODEL_IO_GGUF_READER_EXT_H__
|
#ifndef __GGUF_READER_HPP__
|
||||||
#define __SD_MODEL_IO_GGUF_READER_EXT_H__
|
#define __GGUF_READER_HPP__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/util.h"
|
|
||||||
#include "ggml.h"
|
#include "ggml.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
struct GGUFTensorInfo {
|
struct GGUFTensorInfo {
|
||||||
std::string name;
|
std::string name;
|
||||||
@ -231,4 +231,4 @@ public:
|
|||||||
size_t data_offset() const { return data_offset_; }
|
size_t data_offset() const { return data_offset_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_IO_GGUF_READER_EXT_H__
|
#endif // __GGUF_READER_HPP__
|
||||||
349
src/gits_noise.inl
Normal file
349
src/gits_noise.inl
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
#ifndef GITS_NOISE_INL
|
||||||
|
#define GITS_NOISE_INL
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_0_80 = {
|
||||||
|
{ 14.61464119f, 7.49001646f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 6.77309084f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 3.07277966f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 2.05039096f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 2.05039096f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 8.75849152f, 7.49001646f, 5.85520077f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 8.75849152f, 7.49001646f, 5.85520077f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 8.75849152f, 7.49001646f, 5.85520077f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 5.85520077f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.07277966f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.75859547f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.19567990f, 1.98035145f, 0.86115354f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.75859547f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.19567990f, 1.98035145f, 0.86115354f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.75859547f, 9.24142551f, 8.75849152f, 8.30717278f, 7.88507891f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.07277966f, 1.84880662f, 0.83188516f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_0_85 = {
|
||||||
|
{ 14.61464119f, 7.49001646f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 1.84880662f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 6.77309084f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.11996698f, 3.07277966f, 1.24153244f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.09240818f, 2.84484982f, 0.95350921f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.09240818f, 2.84484982f, 0.95350921f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.58536053f, 3.19567990f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 8.75849152f, 7.49001646f, 5.58536053f, 3.19567990f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 8.75849152f, 7.49001646f, 6.14220476f, 4.65472794f, 3.07277966f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 8.75849152f, 7.49001646f, 6.14220476f, 4.65472794f, 3.07277966f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.65472794f, 3.07277966f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.65472794f, 3.07277966f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.65472794f, 3.07277966f, 1.84880662f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.60512662f, 2.63833880f, 1.56271636f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.46139455f, 2.45070267f, 1.56271636f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.46139455f, 2.45070267f, 1.56271636f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.46139455f, 2.45070267f, 1.56271636f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.75859547f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.46139455f, 2.45070267f, 1.56271636f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.90732002f, 10.31284904f, 9.75859547f, 9.24142551f, 8.75849152f, 8.30717278f, 7.88507891f, 7.49001646f, 6.77309084f, 5.85520077f, 4.65472794f, 3.46139455f, 2.45070267f, 1.56271636f, 0.72133851f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_0_90 = {
|
||||||
|
{ 14.61464119f, 6.77309084f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 3.07277966f, 0.95350921f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.54230714f, 0.89115214f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 4.86714602f, 2.54230714f, 0.89115214f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.09240818f, 3.07277966f, 1.61558151f, 0.69515091f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.11996698f, 4.86714602f, 3.07277966f, 1.61558151f, 0.69515091f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 2.95596409f, 1.61558151f, 0.69515091f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.19988537f, 1.24153244f, 0.57119018f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 10.90732002f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.19988537f, 1.24153244f, 0.57119018f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 9.24142551f, 8.30717278f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.19988537f, 1.24153244f, 0.57119018f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.75677586f, 2.84484982f, 1.84880662f, 1.08895338f, 0.52423614f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 4.86714602f, 3.75677586f, 2.84484982f, 1.84880662f, 1.08895338f, 0.52423614f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.44769001f, 5.58536053f, 4.45427561f, 3.32507086f, 2.45070267f, 1.61558151f, 0.95350921f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.44769001f, 5.58536053f, 4.45427561f, 3.32507086f, 2.45070267f, 1.61558151f, 0.95350921f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.86714602f, 3.91689563f, 3.07277966f, 2.27973175f, 1.56271636f, 0.95350921f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.86714602f, 3.91689563f, 3.07277966f, 2.27973175f, 1.56271636f, 0.95350921f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 4.86714602f, 3.91689563f, 3.07277966f, 2.27973175f, 1.56271636f, 0.95350921f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.96784878f, 12.23089790f, 11.54541874f, 10.31284904f, 9.24142551f, 8.75849152f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 5.09240818f, 4.45427561f, 3.60512662f, 2.95596409f, 2.19988537f, 1.51179266f, 0.89115214f, 0.43325692f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_0_95 = {
|
||||||
|
{ 14.61464119f, 6.77309084f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 2.84484982f, 0.89115214f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.36326075f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.95596409f, 1.56271636f, 0.64427125f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 4.86714602f, 2.95596409f, 1.56271636f, 0.64427125f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 4.86714602f, 3.07277966f, 1.91321158f, 1.08895338f, 0.50118381f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.07277966f, 1.91321158f, 1.08895338f, 0.50118381f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 3.07277966f, 1.91321158f, 1.08895338f, 0.50118381f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.19988537f, 1.41535246f, 0.803307f, 0.38853383f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 8.75849152f, 7.49001646f, 5.85520077f, 4.65472794f, 3.46139455f, 2.63833880f, 1.84880662f, 1.24153244f, 0.72133851f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 10.90732002f, 8.75849152f, 7.49001646f, 5.85520077f, 4.65472794f, 3.46139455f, 2.63833880f, 1.84880662f, 1.24153244f, 0.72133851f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 10.90732002f, 8.75849152f, 7.49001646f, 6.14220476f, 4.86714602f, 3.75677586f, 2.95596409f, 2.19988537f, 1.56271636f, 1.05362725f, 0.64427125f, 0.32104823f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 10.90732002f, 8.75849152f, 7.49001646f, 6.44769001f, 5.58536053f, 4.65472794f, 3.60512662f, 2.95596409f, 2.19988537f, 1.56271636f, 1.05362725f, 0.64427125f, 0.32104823f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 9.24142551f, 8.30717278f, 7.49001646f, 6.44769001f, 5.58536053f, 4.65472794f, 3.60512662f, 2.95596409f, 2.19988537f, 1.56271636f, 1.05362725f, 0.64427125f, 0.32104823f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 9.24142551f, 8.30717278f, 7.49001646f, 6.44769001f, 5.58536053f, 4.65472794f, 3.75677586f, 3.07277966f, 2.45070267f, 1.78698075f, 1.24153244f, 0.83188516f, 0.50118381f, 0.22545385f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.96784878f, 11.54541874f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 5.09240818f, 4.45427561f, 3.60512662f, 2.95596409f, 2.36326075f, 1.72759056f, 1.24153244f, 0.83188516f, 0.50118381f, 0.22545385f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 5.09240818f, 4.45427561f, 3.60512662f, 2.95596409f, 2.36326075f, 1.72759056f, 1.24153244f, 0.83188516f, 0.50118381f, 0.22545385f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 13.76078796f, 12.23089790f, 10.90732002f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 5.09240818f, 4.45427561f, 3.75677586f, 3.07277966f, 2.45070267f, 1.91321158f, 1.46270394f, 1.05362725f, 0.72133851f, 0.43325692f, 0.19894916f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_00 = {
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 0.95350921f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 2.36326075f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.11996698f, 3.07277966f, 1.56271636f, 0.59516323f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.84484982f, 1.41535246f, 0.57119018f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.84484982f, 1.61558151f, 0.86115354f, 0.38853383f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 4.86714602f, 2.84484982f, 1.61558151f, 0.86115354f, 0.38853383f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 4.86714602f, 3.07277966f, 1.98035145f, 1.24153244f, 0.72133851f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.07277966f, 1.98035145f, 1.24153244f, 0.72133851f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.27973175f, 1.51179266f, 0.95350921f, 0.54755926f, 0.25053367f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.36326075f, 1.61558151f, 1.08895338f, 0.72133851f, 0.41087446f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.36326075f, 1.61558151f, 1.08895338f, 0.72133851f, 0.41087446f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.84484982f, 2.12350607f, 1.56271636f, 1.08895338f, 0.72133851f, 0.41087446f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.84484982f, 2.19988537f, 1.61558151f, 1.162866f, 0.803307f, 0.50118381f, 0.27464288f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 5.85520077f, 4.65472794f, 3.75677586f, 3.07277966f, 2.45070267f, 1.84880662f, 1.36964464f, 1.01931262f, 0.72133851f, 0.45573691f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 6.14220476f, 5.09240818f, 4.26497746f, 3.46139455f, 2.84484982f, 2.19988537f, 1.67050016f, 1.24153244f, 0.92192322f, 0.64427125f, 0.43325692f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.75849152f, 7.49001646f, 6.14220476f, 5.09240818f, 4.26497746f, 3.60512662f, 2.95596409f, 2.45070267f, 1.91321158f, 1.51179266f, 1.12534678f, 0.83188516f, 0.59516323f, 0.38853383f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 9.24142551f, 8.30717278f, 7.49001646f, 6.14220476f, 5.09240818f, 4.26497746f, 3.60512662f, 2.95596409f, 2.45070267f, 1.91321158f, 1.51179266f, 1.12534678f, 0.83188516f, 0.59516323f, 0.38853383f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 12.23089790f, 9.24142551f, 8.30717278f, 7.49001646f, 6.77309084f, 5.85520077f, 5.09240818f, 4.26497746f, 3.60512662f, 2.95596409f, 2.45070267f, 1.91321158f, 1.51179266f, 1.12534678f, 0.83188516f, 0.59516323f, 0.38853383f, 0.22545385f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_05 = {
|
||||||
|
{ 14.61464119f, 0.95350921f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 0.89115214f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 2.05039096f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 2.84484982f, 1.28281462f, 0.52423614f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.07277966f, 1.61558151f, 0.803307f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.84484982f, 1.56271636f, 0.803307f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.84484982f, 1.61558151f, 0.95350921f, 0.52423614f, 0.22545385f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 1.98035145f, 1.24153244f, 0.74807048f, 0.41087446f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.27973175f, 1.51179266f, 0.95350921f, 0.59516323f, 0.34370604f, 0.13792117f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.09240818f, 3.46139455f, 2.45070267f, 1.61558151f, 1.08895338f, 0.72133851f, 0.45573691f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.09240818f, 3.46139455f, 2.45070267f, 1.61558151f, 1.08895338f, 0.72133851f, 0.45573691f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.36326075f, 1.61558151f, 1.08895338f, 0.72133851f, 0.45573691f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.45070267f, 1.72759056f, 1.24153244f, 0.86115354f, 0.59516323f, 0.38853383f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.84484982f, 2.19988537f, 1.61558151f, 1.162866f, 0.83188516f, 0.59516323f, 0.38853383f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.84484982f, 2.19988537f, 1.67050016f, 1.28281462f, 0.95350921f, 0.72133851f, 0.52423614f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.95596409f, 2.36326075f, 1.84880662f, 1.41535246f, 1.08895338f, 0.83188516f, 0.61951244f, 0.45573691f, 0.32104823f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.65472794f, 3.60512662f, 2.95596409f, 2.45070267f, 1.91321158f, 1.51179266f, 1.20157266f, 0.95350921f, 0.74807048f, 0.57119018f, 0.43325692f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.30717278f, 7.11996698f, 5.85520077f, 4.65472794f, 3.60512662f, 2.95596409f, 2.45070267f, 1.91321158f, 1.51179266f, 1.20157266f, 0.95350921f, 0.74807048f, 0.57119018f, 0.43325692f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 8.30717278f, 7.11996698f, 5.85520077f, 4.65472794f, 3.60512662f, 2.95596409f, 2.45070267f, 1.98035145f, 1.61558151f, 1.32549286f, 1.08895338f, 0.86115354f, 0.69515091f, 0.54755926f, 0.41087446f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_10 = {
|
||||||
|
{ 14.61464119f, 0.89115214f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 1.61558151f, 0.57119018f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 2.45070267f, 1.08895338f, 0.45573691f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 2.95596409f, 1.56271636f, 0.803307f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.07277966f, 1.61558151f, 0.89115214f, 0.4783645f, 0.19894916f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.07277966f, 1.84880662f, 1.08895338f, 0.64427125f, 0.34370604f, 0.13792117f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.84484982f, 1.61558151f, 0.95350921f, 0.54755926f, 0.27464288f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.95596409f, 1.91321158f, 1.24153244f, 0.803307f, 0.4783645f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 2.05039096f, 1.41535246f, 0.95350921f, 0.64427125f, 0.41087446f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.27973175f, 1.61558151f, 1.12534678f, 0.803307f, 0.54755926f, 0.36617002f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.32507086f, 2.45070267f, 1.72759056f, 1.24153244f, 0.89115214f, 0.64427125f, 0.45573691f, 0.32104823f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.09240818f, 3.60512662f, 2.84484982f, 2.05039096f, 1.51179266f, 1.08895338f, 0.803307f, 0.59516323f, 0.43325692f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.09240818f, 3.60512662f, 2.84484982f, 2.12350607f, 1.61558151f, 1.24153244f, 0.95350921f, 0.72133851f, 0.54755926f, 0.41087446f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.45070267f, 1.84880662f, 1.41535246f, 1.08895338f, 0.83188516f, 0.64427125f, 0.50118381f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.85520077f, 4.45427561f, 3.19567990f, 2.45070267f, 1.91321158f, 1.51179266f, 1.20157266f, 0.95350921f, 0.74807048f, 0.59516323f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 5.85520077f, 4.45427561f, 3.46139455f, 2.84484982f, 2.19988537f, 1.72759056f, 1.36964464f, 1.08895338f, 0.86115354f, 0.69515091f, 0.54755926f, 0.43325692f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.46139455f, 2.84484982f, 2.19988537f, 1.72759056f, 1.36964464f, 1.08895338f, 0.86115354f, 0.69515091f, 0.54755926f, 0.43325692f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 11.54541874f, 7.49001646f, 5.85520077f, 4.45427561f, 3.46139455f, 2.84484982f, 2.19988537f, 1.72759056f, 1.36964464f, 1.08895338f, 0.89115214f, 0.72133851f, 0.59516323f, 0.4783645f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_15 = {
|
||||||
|
{ 14.61464119f, 0.83188516f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.59516323f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 1.56271636f, 0.52423614f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 1.91321158f, 0.83188516f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.24153244f, 0.59516323f, 0.25053367f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.51179266f, 0.803307f, 0.41087446f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.56271636f, 0.89115214f, 0.50118381f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.07277966f, 1.84880662f, 1.12534678f, 0.72133851f, 0.43325692f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.07277966f, 1.91321158f, 1.24153244f, 0.803307f, 0.52423614f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 2.95596409f, 1.91321158f, 1.24153244f, 0.803307f, 0.52423614f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 2.05039096f, 1.36964464f, 0.95350921f, 0.69515091f, 0.4783645f, 0.32104823f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 2.12350607f, 1.51179266f, 1.08895338f, 0.803307f, 0.59516323f, 0.43325692f, 0.29807833f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 2.12350607f, 1.51179266f, 1.08895338f, 0.803307f, 0.59516323f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.07277966f, 2.19988537f, 1.61558151f, 1.24153244f, 0.95350921f, 0.74807048f, 0.59516323f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.45070267f, 1.78698075f, 1.32549286f, 1.01931262f, 0.803307f, 0.64427125f, 0.50118381f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.45070267f, 1.78698075f, 1.32549286f, 1.01931262f, 0.803307f, 0.64427125f, 0.52423614f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.45070267f, 1.84880662f, 1.41535246f, 1.12534678f, 0.89115214f, 0.72133851f, 0.59516323f, 0.4783645f, 0.38853383f, 0.32104823f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.86714602f, 3.19567990f, 2.45070267f, 1.84880662f, 1.41535246f, 1.12534678f, 0.89115214f, 0.72133851f, 0.59516323f, 0.50118381f, 0.41087446f, 0.34370604f, 0.29807833f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_20 = {
|
||||||
|
{ 14.61464119f, 0.803307f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.52423614f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 0.92192322f, 0.36617002f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.24153244f, 0.59516323f, 0.25053367f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.05039096f, 0.95350921f, 0.45573691f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.24153244f, 0.64427125f, 0.29807833f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.36964464f, 0.803307f, 0.45573691f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 0.95350921f, 0.59516323f, 0.36617002f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.67050016f, 1.08895338f, 0.74807048f, 0.50118381f, 0.32104823f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.84880662f, 1.24153244f, 0.83188516f, 0.59516323f, 0.41087446f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 3.07277966f, 1.98035145f, 1.36964464f, 0.95350921f, 0.69515091f, 0.50118381f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.46139455f, 2.36326075f, 1.56271636f, 1.08895338f, 0.803307f, 0.59516323f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 6.77309084f, 3.46139455f, 2.45070267f, 1.61558151f, 1.162866f, 0.86115354f, 0.64427125f, 0.50118381f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.12350607f, 1.51179266f, 1.08895338f, 0.83188516f, 0.64427125f, 0.50118381f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.12350607f, 1.51179266f, 1.08895338f, 0.83188516f, 0.64427125f, 0.50118381f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.12350607f, 1.51179266f, 1.08895338f, 0.83188516f, 0.64427125f, 0.50118381f, 0.41087446f, 0.34370604f, 0.27464288f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.19988537f, 1.61558151f, 1.20157266f, 0.92192322f, 0.72133851f, 0.57119018f, 0.45573691f, 0.36617002f, 0.29807833f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.19988537f, 1.61558151f, 1.24153244f, 0.95350921f, 0.74807048f, 0.59516323f, 0.4783645f, 0.38853383f, 0.32104823f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 7.49001646f, 4.65472794f, 3.07277966f, 2.19988537f, 1.61558151f, 1.24153244f, 0.95350921f, 0.74807048f, 0.59516323f, 0.50118381f, 0.41087446f, 0.34370604f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_25 = {
|
||||||
|
{ 14.61464119f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.50118381f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.05039096f, 0.803307f, 0.32104823f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 0.95350921f, 0.43325692f, 0.17026083f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.24153244f, 0.59516323f, 0.27464288f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.51179266f, 0.803307f, 0.43325692f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.36326075f, 1.24153244f, 0.72133851f, 0.41087446f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.36964464f, 0.83188516f, 0.52423614f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 0.98595673f, 0.64427125f, 0.43325692f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.67050016f, 1.08895338f, 0.74807048f, 0.52423614f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.72759056f, 1.162866f, 0.803307f, 0.59516323f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.84880662f, 1.24153244f, 0.86115354f, 0.64427125f, 0.4783645f, 0.36617002f, 0.27464288f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.84880662f, 1.28281462f, 0.92192322f, 0.69515091f, 0.52423614f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.91321158f, 1.32549286f, 0.95350921f, 0.72133851f, 0.54755926f, 0.43325692f, 0.34370604f, 0.27464288f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.91321158f, 1.32549286f, 0.95350921f, 0.72133851f, 0.57119018f, 0.45573691f, 0.36617002f, 0.29807833f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.95596409f, 1.91321158f, 1.32549286f, 0.95350921f, 0.74807048f, 0.59516323f, 0.4783645f, 0.38853383f, 0.32104823f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 3.07277966f, 2.05039096f, 1.41535246f, 1.05362725f, 0.803307f, 0.61951244f, 0.50118381f, 0.41087446f, 0.34370604f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 3.07277966f, 2.05039096f, 1.41535246f, 1.05362725f, 0.803307f, 0.64427125f, 0.52423614f, 0.43325692f, 0.36617002f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 3.07277966f, 2.05039096f, 1.46270394f, 1.08895338f, 0.83188516f, 0.66947293f, 0.54755926f, 0.45573691f, 0.38853383f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_30 = {
|
||||||
|
{ 14.61464119f, 0.72133851f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.24153244f, 0.43325692f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.59516323f, 0.22545385f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.803307f, 0.36617002f, 0.13792117f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 1.01931262f, 0.52423614f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.36964464f, 0.74807048f, 0.41087446f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.56271636f, 0.89115214f, 0.54755926f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.61558151f, 0.95350921f, 0.61951244f, 0.41087446f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.36964464f, 0.83188516f, 0.54755926f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.41535246f, 0.92192322f, 0.64427125f, 0.45573691f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.6383388f, 1.56271636f, 1.01931262f, 0.72133851f, 0.50118381f, 0.36617002f, 0.27464288f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 1.05362725f, 0.74807048f, 0.54755926f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 1.08895338f, 0.77538133f, 0.57119018f, 0.43325692f, 0.34370604f, 0.27464288f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 1.08895338f, 0.803307f, 0.59516323f, 0.45573691f, 0.36617002f, 0.29807833f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.61558151f, 1.08895338f, 0.803307f, 0.59516323f, 0.4783645f, 0.38853383f, 0.32104823f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.72759056f, 1.162866f, 0.83188516f, 0.64427125f, 0.50118381f, 0.41087446f, 0.34370604f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.72759056f, 1.162866f, 0.83188516f, 0.64427125f, 0.52423614f, 0.43325692f, 0.36617002f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.78698075f, 1.24153244f, 0.92192322f, 0.72133851f, 0.57119018f, 0.45573691f, 0.38853383f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.84484982f, 1.78698075f, 1.24153244f, 0.92192322f, 0.72133851f, 0.57119018f, 0.4783645f, 0.41087446f, 0.36617002f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_35 = {
|
||||||
|
{ 14.61464119f, 0.69515091f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.95350921f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.57119018f, 0.19894916f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.61558151f, 0.69515091f, 0.29807833f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.83188516f, 0.43325692f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.162866f, 0.64427125f, 0.36617002f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.36964464f, 0.803307f, 0.50118381f, 0.32104823f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.41535246f, 0.83188516f, 0.54755926f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 0.95350921f, 0.64427125f, 0.45573691f, 0.32104823f, 0.22545385f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 0.95350921f, 0.64427125f, 0.45573691f, 0.34370604f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.61558151f, 1.01931262f, 0.72133851f, 0.52423614f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.61558151f, 1.01931262f, 0.72133851f, 0.52423614f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.61558151f, 1.05362725f, 0.74807048f, 0.54755926f, 0.43325692f, 0.34370604f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.72759056f, 1.12534678f, 0.803307f, 0.59516323f, 0.45573691f, 0.36617002f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 3.07277966f, 1.72759056f, 1.12534678f, 0.803307f, 0.59516323f, 0.4783645f, 0.38853383f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.45070267f, 1.51179266f, 1.01931262f, 0.74807048f, 0.57119018f, 0.45573691f, 0.36617002f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.6383388f, 1.61558151f, 1.08895338f, 0.803307f, 0.61951244f, 0.50118381f, 0.41087446f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.6383388f, 1.61558151f, 1.08895338f, 0.803307f, 0.64427125f, 0.52423614f, 0.43325692f, 0.36617002f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 5.85520077f, 2.6383388f, 1.61558151f, 1.08895338f, 0.803307f, 0.64427125f, 0.52423614f, 0.45573691f, 0.38853383f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_40 = {
|
||||||
|
{ 14.61464119f, 0.59516323f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.95350921f, 0.34370604f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.08895338f, 0.43325692f, 0.13792117f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.64427125f, 0.27464288f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.61558151f, 0.803307f, 0.43325692f, 0.22545385f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.05039096f, 0.95350921f, 0.54755926f, 0.34370604f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.24153244f, 0.72133851f, 0.43325692f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.24153244f, 0.74807048f, 0.50118381f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.803307f, 0.52423614f, 0.36617002f, 0.27464288f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.803307f, 0.54755926f, 0.38853383f, 0.29807833f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.41535246f, 0.86115354f, 0.59516323f, 0.43325692f, 0.32104823f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.51179266f, 0.95350921f, 0.64427125f, 0.45573691f, 0.34370604f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.51179266f, 0.95350921f, 0.64427125f, 0.4783645f, 0.36617002f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 0.98595673f, 0.69515091f, 0.52423614f, 0.41087446f, 0.34370604f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 1.01931262f, 0.72133851f, 0.54755926f, 0.43325692f, 0.36617002f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.61558151f, 1.05362725f, 0.74807048f, 0.57119018f, 0.45573691f, 0.38853383f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.61558151f, 1.08895338f, 0.803307f, 0.61951244f, 0.50118381f, 0.41087446f, 0.36617002f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.61558151f, 1.08895338f, 0.803307f, 0.61951244f, 0.50118381f, 0.43325692f, 0.38853383f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.61558151f, 1.08895338f, 0.803307f, 0.64427125f, 0.52423614f, 0.45573691f, 0.41087446f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_45 = {
|
||||||
|
{ 14.61464119f, 0.59516323f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.803307f, 0.25053367f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.95350921f, 0.34370604f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.24153244f, 0.54755926f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.72133851f, 0.36617002f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.61558151f, 0.803307f, 0.45573691f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.91321158f, 0.95350921f, 0.57119018f, 0.36617002f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.19988537f, 1.08895338f, 0.64427125f, 0.41087446f, 0.27464288f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.24153244f, 0.74807048f, 0.50118381f, 0.34370604f, 0.25053367f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.24153244f, 0.74807048f, 0.50118381f, 0.36617002f, 0.27464288f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.803307f, 0.54755926f, 0.41087446f, 0.32104823f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.803307f, 0.57119018f, 0.43325692f, 0.34370604f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.83188516f, 0.59516323f, 0.45573691f, 0.36617002f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.28281462f, 0.83188516f, 0.59516323f, 0.45573691f, 0.36617002f, 0.32104823f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.51179266f, 0.95350921f, 0.69515091f, 0.52423614f, 0.41087446f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.51179266f, 0.95350921f, 0.69515091f, 0.52423614f, 0.43325692f, 0.36617002f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 0.98595673f, 0.72133851f, 0.54755926f, 0.45573691f, 0.38853383f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 1.01931262f, 0.74807048f, 0.57119018f, 0.4783645f, 0.41087446f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.84484982f, 1.56271636f, 1.01931262f, 0.74807048f, 0.59516323f, 0.50118381f, 0.43325692f, 0.38853383f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::vector<float>> GITS_NOISE_1_50 = {
|
||||||
|
{ 14.61464119f, 0.54755926f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.803307f, 0.25053367f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 0.86115354f, 0.32104823f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.24153244f, 0.54755926f, 0.25053367f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.56271636f, 0.72133851f, 0.36617002f, 0.19894916f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.61558151f, 0.803307f, 0.45573691f, 0.27464288f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.61558151f, 0.83188516f, 0.52423614f, 0.34370604f, 0.25053367f, 0.17026083f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.95350921f, 0.59516323f, 0.38853383f, 0.27464288f, 0.19894916f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.95350921f, 0.59516323f, 0.41087446f, 0.29807833f, 0.22545385f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 1.84880662f, 0.95350921f, 0.61951244f, 0.43325692f, 0.32104823f, 0.25053367f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.19988537f, 1.12534678f, 0.72133851f, 0.50118381f, 0.36617002f, 0.27464288f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.19988537f, 1.12534678f, 0.72133851f, 0.50118381f, 0.36617002f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 1.24153244f, 0.803307f, 0.57119018f, 0.43325692f, 0.34370604f, 0.29807833f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 1.24153244f, 0.803307f, 0.57119018f, 0.43325692f, 0.34370604f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 1.24153244f, 0.803307f, 0.59516323f, 0.45573691f, 0.36617002f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.36326075f, 1.24153244f, 0.803307f, 0.59516323f, 0.45573691f, 0.38853383f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.32549286f, 0.86115354f, 0.64427125f, 0.50118381f, 0.41087446f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.36964464f, 0.92192322f, 0.69515091f, 0.54755926f, 0.45573691f, 0.41087446f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f },
|
||||||
|
{ 14.61464119f, 2.45070267f, 1.41535246f, 0.95350921f, 0.72133851f, 0.57119018f, 0.4783645f, 0.43325692f, 0.38853383f, 0.36617002f, 0.34370604f, 0.32104823f, 0.29807833f, 0.27464288f, 0.25053367f, 0.22545385f, 0.19894916f, 0.17026083f, 0.13792117f, 0.09824532f, 0.02916753f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<const std::vector<std::vector<float>>*> GITS_NOISE = {
|
||||||
|
&GITS_NOISE_0_80,
|
||||||
|
&GITS_NOISE_0_85,
|
||||||
|
&GITS_NOISE_0_90,
|
||||||
|
&GITS_NOISE_0_95,
|
||||||
|
&GITS_NOISE_1_00,
|
||||||
|
&GITS_NOISE_1_05,
|
||||||
|
&GITS_NOISE_1_10,
|
||||||
|
&GITS_NOISE_1_15,
|
||||||
|
&GITS_NOISE_1_20,
|
||||||
|
&GITS_NOISE_1_25,
|
||||||
|
&GITS_NOISE_1_30,
|
||||||
|
&GITS_NOISE_1_35,
|
||||||
|
&GITS_NOISE_1_40,
|
||||||
|
&GITS_NOISE_1_45,
|
||||||
|
&GITS_NOISE_1_50
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GITS_NOISE_INL
|
||||||
@ -1,140 +1,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "core/tensor.hpp"
|
|
||||||
#include "ggml.h"
|
#include "ggml.h"
|
||||||
|
#include "tensor.hpp"
|
||||||
const float ltxav_latent_rgb_proj[128][3] = {
|
|
||||||
{-0.0293802f, -0.0362516f, -0.0291386f},
|
|
||||||
{0.0117735f, 0.0223435f, 0.018856f},
|
|
||||||
{0.00922335f, 0.0145666f, 0.0038772f},
|
|
||||||
{0.0227299f, 0.0109122f, 0.0131384f},
|
|
||||||
{0.00192413f, 0.0024648f, 0.00689245f},
|
|
||||||
{-0.0105576f, -0.0135933f, -0.00873841f},
|
|
||||||
{-0.0310222f, -0.0396358f, -0.0408445f},
|
|
||||||
{0.0149737f, 0.0316323f, 0.03415f},
|
|
||||||
{0.0027752f, 0.00814889f, 0.0108575f},
|
|
||||||
{-0.000678017f, -0.00180589f, -0.0161684f},
|
|
||||||
{0.0153964f, 0.0159774f, 0.0186479f},
|
|
||||||
{-0.0222799f, -0.0202068f, -0.0181082f},
|
|
||||||
{0.0128696f, 0.00754416f, -0.00673279f},
|
|
||||||
{0.0142729f, 0.00448099f, -0.00193934f},
|
|
||||||
{-0.014066f, -0.0193755f, -0.0160104f},
|
|
||||||
{-0.0176785f, -0.015903f, -0.0152621f},
|
|
||||||
{0.0307381f, 0.0292082f, 0.0328668f},
|
|
||||||
{0.0332928f, 0.0368629f, 0.0440893f},
|
|
||||||
{0.0186304f, 0.0124069f, 0.0160734f},
|
|
||||||
{0.00477787f, -0.00315658f, -0.000145702f},
|
|
||||||
{0.0183099f, 0.0122593f, 0.00599732f},
|
|
||||||
{-0.0194551f, -0.0183924f, -0.0147465f},
|
|
||||||
{0.0025732f, 0.00442582f, 0.0173176f},
|
|
||||||
{-0.0169423f, -0.0293863f, -0.0225908f},
|
|
||||||
{-0.021228f, -0.0265094f, -0.0253049f},
|
|
||||||
{0.0327111f, 0.0187133f, 0.0266184f},
|
|
||||||
{-0.0226425f, -0.0313781f, -0.0414356f},
|
|
||||||
{-0.0163142f, -0.0146144f, -0.0171793f},
|
|
||||||
{0.0192183f, 0.0108411f, 0.00829186f},
|
|
||||||
{-0.032246f, -0.0274846f, -0.0287434f},
|
|
||||||
{0.00345399f, 0.0115567f, 0.015288f},
|
|
||||||
{0.000972292f, 0.00331303f, 0.0110501f},
|
|
||||||
{0.000939494f, -0.00705084f, -0.00979449f},
|
|
||||||
{0.0405155f, 0.0339534f, 0.0419513f},
|
|
||||||
{0.0198596f, 0.0186626f, 0.0213766f},
|
|
||||||
{-0.00982375f, -0.00880439f, -0.00470429f},
|
|
||||||
{-0.0313707f, -0.0258098f, -0.0211663f},
|
|
||||||
{0.0144159f, 0.0117896f, 0.0141573f},
|
|
||||||
{0.0164571f, 0.0149178f, 0.00921599f},
|
|
||||||
{0.0436184f, 0.0346583f, 0.0360647f},
|
|
||||||
{-0.00289744f, -0.000752502f, 0.000675415f},
|
|
||||||
{-0.00621715f, -0.000558851f, 0.0135814f},
|
|
||||||
{-0.00817579f, -0.0113584f, -0.00556793f},
|
|
||||||
{0.00965067f, 0.0178221f, 0.015821f},
|
|
||||||
{0.0211832f, 0.0180827f, 0.0154707f},
|
|
||||||
{-0.00412858f, -0.00374182f, 0.0029568f},
|
|
||||||
{-0.0175603f, -0.0226242f, -0.0279012f},
|
|
||||||
{-0.00437471f, -0.00668329f, 0.000164887f},
|
|
||||||
{-0.0355983f, -0.0419093f, -0.0383065f},
|
|
||||||
{0.0144314f, 0.0192514f, 0.0175639f},
|
|
||||||
{-0.0130693f, -0.00569884f, -0.00341647f},
|
|
||||||
{-0.00184689f, 0.00189034f, -0.00190561f},
|
|
||||||
{0.019457f, 0.00842282f, 0.0123738f},
|
|
||||||
{-0.00477146f, -0.00206932f, 0.00283336f},
|
|
||||||
{-0.0364544f, -0.0256141f, -0.0322336f},
|
|
||||||
{-0.0295634f, -0.0295048f, -0.021057f},
|
|
||||||
{0.0144484f, 0.0191862f, 0.0112445f},
|
|
||||||
{0.0536406f, 0.0582376f, 0.0570966f},
|
|
||||||
{0.0085178f, 0.00748455f, 0.00995162f},
|
|
||||||
{-0.0136637f, -0.0172914f, -0.0195978f},
|
|
||||||
{-0.0339128f, -0.0392692f, -0.0355216f},
|
|
||||||
{0.00612855f, 0.00568303f, -0.00212333f},
|
|
||||||
{-0.0029225f, 0.00668819f, 0.0122131f},
|
|
||||||
{0.00841843f, 0.000181587f, -0.00650644f},
|
|
||||||
{-0.00514432f, 0.0127043f, 0.0168049f},
|
|
||||||
{-0.00997384f, -0.00602262f, -0.0164031f},
|
|
||||||
{0.0233226f, 0.033254f, 0.0307266f},
|
|
||||||
{-0.0110201f, -0.0164169f, -0.0161829f},
|
|
||||||
{-0.0195952f, -0.0177943f, -0.0115377f},
|
|
||||||
{-0.00523918f, -0.00452043f, 0.00267397f},
|
|
||||||
{0.0313464f, 0.0288241f, 0.0262496f},
|
|
||||||
{0.0324018f, 0.0339792f, 0.0312209f},
|
|
||||||
{-0.0163247f, -0.0230503f, -0.0263239f},
|
|
||||||
{0.000420577f, -0.00535659f, -0.00663426f},
|
|
||||||
{-0.012897f, -0.00203767f, -0.000622678f},
|
|
||||||
{-0.0632956f, -0.0651325f, -0.0584479f},
|
|
||||||
{-0.00426634f, -0.0150098f, -0.00719348f},
|
|
||||||
{0.00476109f, 0.00674315f, 0.00895472f},
|
|
||||||
{0.0129384f, 0.0158352f, 0.00963773f},
|
|
||||||
{-0.0333379f, -0.0410522f, -0.0317462f},
|
|
||||||
{0.00344054f, 0.00275915f, 0.00355732f},
|
|
||||||
{0.0209062f, 0.0273453f, 0.0222967f},
|
|
||||||
{0.00827287f, 0.00223045f, 0.00325844f},
|
|
||||||
{-0.0149132f, -0.0183973f, -0.0199781f},
|
|
||||||
{-0.0100786f, -0.0103681f, -0.00218224f},
|
|
||||||
{-0.00791409f, -0.00405153f, -0.00599893f},
|
|
||||||
{0.0176126f, 0.00618342f, -6.6569e-05f},
|
|
||||||
{0.00942486f, -0.00206494f, -0.00580324f},
|
|
||||||
{0.00678093f, -0.00291742f, -0.000921195f},
|
|
||||||
{-0.0221992f, -0.00483162f, -0.000848514f},
|
|
||||||
{-0.0151587f, -0.0157166f, -0.0107302f},
|
|
||||||
{0.00909646f, 0.0171985f, 0.0169785f},
|
|
||||||
{0.0127224f, 0.0170612f, 0.0303428f},
|
|
||||||
{0.0196562f, 0.00212451f, 0.0127744f},
|
|
||||||
{0.0233013f, 0.0228994f, 0.0108387f},
|
|
||||||
{0.00520761f, 0.00992992f, 0.0066267f},
|
|
||||||
{-3.77736e-05f, 0.00460229f, -0.00475132f},
|
|
||||||
{-0.0311763f, -0.0453566f, -0.0486901f},
|
|
||||||
{0.0195798f, 0.0281246f, 0.0180102f},
|
|
||||||
{-0.0174149f, -0.0240867f, -0.0188785f},
|
|
||||||
{0.000104658f, 0.00659008f, 0.0144594f},
|
|
||||||
{-0.00311086f, -0.0241426f, -0.0244164f},
|
|
||||||
{0.0336462f, 0.0305173f, 0.0331101f},
|
|
||||||
{0.0613625f, 0.066561f, 0.0610198f},
|
|
||||||
{-0.0286757f, -0.0325401f, -0.0338036f},
|
|
||||||
{0.0141534f, 0.0188266f, 0.0253059f},
|
|
||||||
{-0.00548197f, -0.00170198f, 0.00561745f},
|
|
||||||
{-0.0117872f, -0.00763218f, -0.0145037f},
|
|
||||||
{-0.0253304f, -0.0245217f, -0.0144905f},
|
|
||||||
{-0.00393624f, 0.00350048f, 0.00765561f},
|
|
||||||
{0.0113625f, 0.00561576f, -0.0113672f},
|
|
||||||
{-0.0301278f, -0.0261472f, -0.0301903f},
|
|
||||||
{0.016863f, 0.0173781f, 0.0170916f},
|
|
||||||
{-0.00495108f, 0.00686749f, 0.00282767f},
|
|
||||||
{0.00125409f, -0.00378072f, -0.00264117f},
|
|
||||||
{-0.00264001f, -0.00529772f, -0.0113109f},
|
|
||||||
{-0.054888f, -0.0575461f, -0.0509146f},
|
|
||||||
{-0.019442f, -0.0232916f, -0.0258637f},
|
|
||||||
{0.0133362f, 0.0161808f, 0.00917951f},
|
|
||||||
{-0.0349002f, -0.0372642f, -0.0466206f},
|
|
||||||
{-0.00216926f, 0.00208738f, 0.00766492f},
|
|
||||||
{0.0268528f, 0.0301179f, 0.0228579f},
|
|
||||||
{0.0226176f, 0.021536f, 0.023152f},
|
|
||||||
{-0.0110646f, -0.00511349f, -0.0137346f},
|
|
||||||
{-0.0098424f, -0.00218176f, 0.00414545f},
|
|
||||||
{0.00200216f, 0.00441732f, -0.0136515f},
|
|
||||||
{0.00695946f, 0.00313109f, -0.00379435f},
|
|
||||||
{0.0188377f, 0.0144059f, 0.0229724f},
|
|
||||||
};
|
|
||||||
float ltxav_latent_rgb_bias[3] = {0.043849f, 0.0201085f, 0.0150286f};
|
|
||||||
|
|
||||||
const float wan_21_latent_rgb_proj[16][3] = {
|
const float wan_21_latent_rgb_proj[16][3] = {
|
||||||
{0.015123f, -0.148418f, 0.479828f},
|
{0.015123f, -0.148418f, 0.479828f},
|
||||||
1261
src/llm.hpp
Normal file
1261
src/llm.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,8 @@
|
|||||||
#ifndef __SD_MODEL_ADAPTER_LORA_HPP__
|
#ifndef __LORA_HPP__
|
||||||
#define __SD_MODEL_ADAPTER_LORA_HPP__
|
#define __LORA_HPP__
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "model_loader.h"
|
|
||||||
|
|
||||||
#define LORA_GRAPH_BASE_SIZE 10240
|
#define LORA_GRAPH_BASE_SIZE 10240
|
||||||
|
|
||||||
@ -23,11 +22,10 @@ struct LoraModel : public GGMLRunner {
|
|||||||
|
|
||||||
LoraModel(const std::string& lora_id,
|
LoraModel(const std::string& lora_id,
|
||||||
ggml_backend_t backend,
|
ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
|
||||||
const std::string& file_path = "",
|
const std::string& file_path = "",
|
||||||
std::string prefix = "",
|
std::string prefix = "",
|
||||||
SDVersion version = VERSION_COUNT)
|
SDVersion version = VERSION_COUNT)
|
||||||
: lora_id(lora_id), file_path(file_path), GGMLRunner(backend, params_backend) {
|
: lora_id(lora_id), file_path(file_path), GGMLRunner(backend, false) {
|
||||||
prefix = "lora." + prefix;
|
prefix = "lora." + prefix;
|
||||||
if (!model_loader.init_from_file_and_convert_name(file_path, prefix, version)) {
|
if (!model_loader.init_from_file_and_convert_name(file_path, prefix, version)) {
|
||||||
load_failed = true;
|
load_failed = true;
|
||||||
@ -87,10 +85,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
lora_tensors[name] = real;
|
lora_tensors[name] = real;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alloc_params_buffer()) {
|
alloc_params_buffer();
|
||||||
LOG_ERROR("lora model buffer allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dry_run = false;
|
dry_run = false;
|
||||||
model_loader.load_tensors(on_new_tensor_cb, n_threads);
|
model_loader.load_tensors(on_new_tensor_cb, n_threads);
|
||||||
@ -134,7 +129,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_lora_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_backend_t backend) {
|
ggml_tensor* get_lora_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||||
ggml_tensor* updown = nullptr;
|
ggml_tensor* updown = nullptr;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -157,17 +152,17 @@ struct LoraModel : public GGMLRunner {
|
|||||||
|
|
||||||
auto iter = lora_tensors.find(lora_up_name);
|
auto iter = lora_tensors.find(lora_up_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lora_up = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lora_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(lora_mid_name);
|
iter = lora_tensors.find(lora_mid_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lora_mid = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lora_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(lora_down_name);
|
iter = lora_tensors.find(lora_down_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lora_down = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lora_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lora_up == nullptr || lora_down == nullptr) {
|
if (lora_up == nullptr || lora_down == nullptr) {
|
||||||
@ -213,7 +208,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
return updown;
|
return updown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_raw_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_backend_t backend) {
|
ggml_tensor* get_raw_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||||
ggml_tensor* updown = nullptr;
|
ggml_tensor* updown = nullptr;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -230,7 +225,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
|
|
||||||
auto iter = lora_tensors.find(diff_name);
|
auto iter = lora_tensors.find(diff_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
curr_updown = ggml_ext_cast_f32(ctx, backend, iter->second);
|
curr_updown = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -253,7 +248,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
return updown;
|
return updown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_loha_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_backend_t backend) {
|
ggml_tensor* get_loha_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||||
ggml_tensor* updown = nullptr;
|
ggml_tensor* updown = nullptr;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -281,33 +276,33 @@ struct LoraModel : public GGMLRunner {
|
|||||||
|
|
||||||
auto iter = lora_tensors.find(hada_1_down_name);
|
auto iter = lora_tensors.find(hada_1_down_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_1_down = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_1_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(hada_1_up_name);
|
iter = lora_tensors.find(hada_1_up_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_1_up = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_1_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(hada_1_mid_name);
|
iter = lora_tensors.find(hada_1_mid_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_1_mid = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_1_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
hada_1_up = ggml_cont(ctx, ggml_transpose(ctx, hada_1_up));
|
hada_1_up = ggml_cont(ctx, ggml_transpose(ctx, hada_1_up));
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(hada_2_down_name);
|
iter = lora_tensors.find(hada_2_down_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_2_down = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_2_down = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(hada_2_up_name);
|
iter = lora_tensors.find(hada_2_up_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_2_up = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_2_up = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(hada_2_mid_name);
|
iter = lora_tensors.find(hada_2_mid_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
hada_2_mid = ggml_ext_cast_f32(ctx, backend, iter->second);
|
hada_2_mid = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
hada_2_up = ggml_cont(ctx, ggml_transpose(ctx, hada_2_up));
|
hada_2_up = ggml_cont(ctx, ggml_transpose(ctx, hada_2_up));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +351,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
return updown;
|
return updown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_lokr_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_backend_t backend) {
|
ggml_tensor* get_lokr_weight_diff(const std::string& model_tensor_name, ggml_context* ctx) {
|
||||||
ggml_tensor* updown = nullptr;
|
ggml_tensor* updown = nullptr;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -383,24 +378,24 @@ struct LoraModel : public GGMLRunner {
|
|||||||
|
|
||||||
auto iter = lora_tensors.find(lokr_w1_name);
|
auto iter = lora_tensors.find(lokr_w1_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w1 = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w1 = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(lokr_w2_name);
|
iter = lora_tensors.find(lokr_w2_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w2 = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w2 = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t rank = 1;
|
int64_t rank = 1;
|
||||||
if (lokr_w1 == nullptr) {
|
if (lokr_w1 == nullptr) {
|
||||||
iter = lora_tensors.find(lokr_w1_a_name);
|
iter = lora_tensors.find(lokr_w1_a_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w1_a = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w1_a = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(lokr_w1_b_name);
|
iter = lora_tensors.find(lokr_w1_b_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w1_b = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w1_b = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lokr_w1_a == nullptr || lokr_w1_b == nullptr) {
|
if (lokr_w1_a == nullptr || lokr_w1_b == nullptr) {
|
||||||
@ -415,12 +410,12 @@ struct LoraModel : public GGMLRunner {
|
|||||||
if (lokr_w2 == nullptr) {
|
if (lokr_w2 == nullptr) {
|
||||||
iter = lora_tensors.find(lokr_w2_a_name);
|
iter = lora_tensors.find(lokr_w2_a_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w2_a = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w2_a = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = lora_tensors.find(lokr_w2_b_name);
|
iter = lora_tensors.find(lokr_w2_b_name);
|
||||||
if (iter != lora_tensors.end()) {
|
if (iter != lora_tensors.end()) {
|
||||||
lokr_w2_b = ggml_ext_cast_f32(ctx, backend, iter->second);
|
lokr_w2_b = ggml_ext_cast_f32(ctx, iter->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lokr_w2_a == nullptr || lokr_w2_b == nullptr) {
|
if (lokr_w2_a == nullptr || lokr_w2_b == nullptr) {
|
||||||
@ -473,23 +468,23 @@ struct LoraModel : public GGMLRunner {
|
|||||||
return updown;
|
return updown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_weight_diff(const std::string& model_tensor_name, ggml_backend_t backend, ggml_context* ctx, ggml_tensor* model_tensor, bool with_lora_and_lokr = true) {
|
ggml_tensor* get_weight_diff(const std::string& model_tensor_name, ggml_context* ctx, ggml_tensor* model_tensor, bool with_lora_and_lokr = true) {
|
||||||
// lora
|
// lora
|
||||||
ggml_tensor* diff = nullptr;
|
ggml_tensor* diff = nullptr;
|
||||||
if (with_lora_and_lokr) {
|
if (with_lora_and_lokr) {
|
||||||
diff = get_lora_weight_diff(model_tensor_name, ctx, backend);
|
diff = get_lora_weight_diff(model_tensor_name, ctx);
|
||||||
}
|
}
|
||||||
// diff
|
// diff
|
||||||
if (diff == nullptr) {
|
if (diff == nullptr) {
|
||||||
diff = get_raw_weight_diff(model_tensor_name, ctx, backend);
|
diff = get_raw_weight_diff(model_tensor_name, ctx);
|
||||||
}
|
}
|
||||||
// loha
|
// loha
|
||||||
if (diff == nullptr) {
|
if (diff == nullptr) {
|
||||||
diff = get_loha_weight_diff(model_tensor_name, ctx, backend);
|
diff = get_loha_weight_diff(model_tensor_name, ctx);
|
||||||
}
|
}
|
||||||
// lokr
|
// lokr
|
||||||
if (diff == nullptr && with_lora_and_lokr) {
|
if (diff == nullptr && with_lora_and_lokr) {
|
||||||
diff = get_lokr_weight_diff(model_tensor_name, ctx, backend);
|
diff = get_lokr_weight_diff(model_tensor_name, ctx);
|
||||||
}
|
}
|
||||||
if (diff != nullptr) {
|
if (diff != nullptr) {
|
||||||
if (ggml_nelements(diff) < ggml_nelements(model_tensor)) {
|
if (ggml_nelements(diff) < ggml_nelements(model_tensor)) {
|
||||||
@ -507,7 +502,6 @@ struct LoraModel : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* get_out_diff(ggml_context* ctx,
|
ggml_tensor* get_out_diff(ggml_context* ctx,
|
||||||
ggml_backend_t backend,
|
|
||||||
ggml_tensor* x,
|
ggml_tensor* x,
|
||||||
WeightAdapter::ForwardParams forward_params,
|
WeightAdapter::ForwardParams forward_params,
|
||||||
const std::string& model_tensor_name) {
|
const std::string& model_tensor_name) {
|
||||||
@ -596,7 +590,7 @@ struct LoraModel : public GGMLRunner {
|
|||||||
}
|
}
|
||||||
scale_value *= multiplier;
|
scale_value *= multiplier;
|
||||||
|
|
||||||
auto curr_out_diff = ggml_ext_lokr_forward(ctx, backend, x, lokr_w1, lokr_w1_a, lokr_w1_b, lokr_w2, lokr_w2_a, lokr_w2_b, is_conv2d, forward_params.conv2d, scale_value);
|
auto curr_out_diff = ggml_ext_lokr_forward(ctx, x, lokr_w1, lokr_w1_a, lokr_w1_b, lokr_w2, lokr_w2_a, lokr_w2_b, is_conv2d, forward_params.conv2d, scale_value);
|
||||||
if (out_diff == nullptr) {
|
if (out_diff == nullptr) {
|
||||||
out_diff = curr_out_diff;
|
out_diff = curr_out_diff;
|
||||||
} else {
|
} else {
|
||||||
@ -767,27 +761,27 @@ struct LoraModel : public GGMLRunner {
|
|||||||
ggml_tensor* model_tensor = it.second;
|
ggml_tensor* model_tensor = it.second;
|
||||||
|
|
||||||
// lora
|
// lora
|
||||||
ggml_tensor* diff = get_weight_diff(model_tensor_name, runtime_backend, compute_ctx, model_tensor);
|
ggml_tensor* diff = get_weight_diff(model_tensor_name, compute_ctx, model_tensor);
|
||||||
if (diff == nullptr) {
|
if (diff == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* original_tensor = model_tensor;
|
ggml_tensor* original_tensor = model_tensor;
|
||||||
if (!sd_backend_is_cpu(runtime_backend) && ggml_backend_buffer_is_host(original_tensor->buffer)) {
|
if (!ggml_backend_is_cpu(runtime_backend) && ggml_backend_buffer_is_host(original_tensor->buffer)) {
|
||||||
model_tensor = ggml_dup_tensor(compute_ctx, model_tensor);
|
model_tensor = ggml_dup_tensor(compute_ctx, model_tensor);
|
||||||
set_backend_tensor_data(model_tensor, original_tensor->data);
|
set_backend_tensor_data(model_tensor, original_tensor->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* final_tensor;
|
ggml_tensor* final_tensor;
|
||||||
if (model_tensor->type != GGML_TYPE_F32 && model_tensor->type != GGML_TYPE_F16) {
|
if (model_tensor->type != GGML_TYPE_F32 && model_tensor->type != GGML_TYPE_F16) {
|
||||||
final_tensor = ggml_ext_cast_f32(compute_ctx, runtime_backend, model_tensor);
|
final_tensor = ggml_ext_cast_f32(compute_ctx, model_tensor);
|
||||||
final_tensor = ggml_add_inplace(compute_ctx, final_tensor, diff);
|
final_tensor = ggml_add_inplace(compute_ctx, final_tensor, diff);
|
||||||
final_tensor = ggml_cpy(compute_ctx, final_tensor, model_tensor);
|
final_tensor = ggml_cpy(compute_ctx, final_tensor, model_tensor);
|
||||||
} else {
|
} else {
|
||||||
final_tensor = ggml_add_inplace(compute_ctx, model_tensor, diff);
|
final_tensor = ggml_add_inplace(compute_ctx, model_tensor, diff);
|
||||||
}
|
}
|
||||||
ggml_build_forward_expand(gf, final_tensor);
|
ggml_build_forward_expand(gf, final_tensor);
|
||||||
if (!sd_backend_is_cpu(runtime_backend) && ggml_backend_buffer_is_host(original_tensor->buffer)) {
|
if (!ggml_backend_is_cpu(runtime_backend) && ggml_backend_buffer_is_host(original_tensor->buffer)) {
|
||||||
original_tensor_to_final_tensor[original_tensor] = final_tensor;
|
original_tensor_to_final_tensor[original_tensor] = final_tensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -847,35 +841,34 @@ public:
|
|||||||
: lora_models(lora_models) {
|
: lora_models(lora_models) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* patch_weight(ggml_context* ctx, ggml_backend_t backend, ggml_tensor* weight, const std::string& weight_name, bool with_lora_and_lokr) {
|
ggml_tensor* patch_weight(ggml_context* ctx, ggml_tensor* weight, const std::string& weight_name, bool with_lora_and_lokr) {
|
||||||
for (auto& lora_model : lora_models) {
|
for (auto& lora_model : lora_models) {
|
||||||
ggml_tensor* diff = lora_model->get_weight_diff(weight_name, backend, ctx, weight, with_lora_and_lokr);
|
ggml_tensor* diff = lora_model->get_weight_diff(weight_name, ctx, weight, with_lora_and_lokr);
|
||||||
if (diff == nullptr) {
|
if (diff == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weight->type != GGML_TYPE_F32 && weight->type != GGML_TYPE_F16) {
|
if (weight->type != GGML_TYPE_F32 && weight->type != GGML_TYPE_F16) {
|
||||||
weight = ggml_ext_cast_f32(ctx, backend, weight);
|
weight = ggml_ext_cast_f32(ctx, weight);
|
||||||
}
|
}
|
||||||
weight = ggml_add(ctx, weight, diff);
|
weight = ggml_add(ctx, weight, diff);
|
||||||
}
|
}
|
||||||
return weight;
|
return weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* patch_weight(ggml_context* ctx, ggml_backend_t backend, ggml_tensor* weight, const std::string& weight_name) override {
|
ggml_tensor* patch_weight(ggml_context* ctx, ggml_tensor* weight, const std::string& weight_name) override {
|
||||||
return patch_weight(ctx, backend, weight, weight_name, true);
|
return patch_weight(ctx, weight, weight_name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* forward_with_lora(ggml_context* ctx,
|
ggml_tensor* forward_with_lora(ggml_context* ctx,
|
||||||
ggml_backend_t backend,
|
|
||||||
ggml_tensor* x,
|
ggml_tensor* x,
|
||||||
ggml_tensor* w,
|
ggml_tensor* w,
|
||||||
ggml_tensor* b,
|
ggml_tensor* b,
|
||||||
const std::string& prefix,
|
const std::string& prefix,
|
||||||
WeightAdapter::ForwardParams forward_params) override {
|
WeightAdapter::ForwardParams forward_params) override {
|
||||||
w = patch_weight(ctx, backend, w, prefix + "weight", false);
|
w = patch_weight(ctx, w, prefix + "weight", false);
|
||||||
if (b) {
|
if (b) {
|
||||||
b = patch_weight(ctx, backend, b, prefix + "bias", false);
|
b = patch_weight(ctx, b, prefix + "bias", false);
|
||||||
}
|
}
|
||||||
ggml_tensor* out;
|
ggml_tensor* out;
|
||||||
if (forward_params.op_type == ForwardParams::op_type_t::OP_LINEAR) {
|
if (forward_params.op_type == ForwardParams::op_type_t::OP_LINEAR) {
|
||||||
@ -897,7 +890,7 @@ public:
|
|||||||
forward_params.conv2d.scale);
|
forward_params.conv2d.scale);
|
||||||
}
|
}
|
||||||
for (auto& lora_model : lora_models) {
|
for (auto& lora_model : lora_models) {
|
||||||
ggml_tensor* out_diff = lora_model->get_out_diff(ctx, backend, x, forward_params, prefix + "weight");
|
ggml_tensor* out_diff = lora_model->get_out_diff(ctx, x, forward_params, prefix + "weight");
|
||||||
if (out_diff == nullptr) {
|
if (out_diff == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -915,4 +908,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_ADAPTER_LORA_HPP__
|
#endif // __LORA_HPP__
|
||||||
73
src/ltxv.hpp
Normal file
73
src/ltxv.hpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#ifndef __LTXV_HPP__
|
||||||
|
#define __LTXV_HPP__
|
||||||
|
|
||||||
|
#include "common_block.hpp"
|
||||||
|
|
||||||
|
namespace LTXV {
|
||||||
|
|
||||||
|
class CausalConv3d : public GGMLBlock {
|
||||||
|
protected:
|
||||||
|
int time_kernel_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CausalConv3d(int64_t in_channels,
|
||||||
|
int64_t out_channels,
|
||||||
|
int kernel_size = 3,
|
||||||
|
std::tuple<int, int, int> stride = {1, 1, 1},
|
||||||
|
int dilation = 1,
|
||||||
|
bool bias = true) {
|
||||||
|
time_kernel_size = kernel_size / 2;
|
||||||
|
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(in_channels,
|
||||||
|
out_channels,
|
||||||
|
{kernel_size, kernel_size, kernel_size},
|
||||||
|
stride,
|
||||||
|
{0, kernel_size / 2, kernel_size / 2},
|
||||||
|
{dilation, 1, 1},
|
||||||
|
bias));
|
||||||
|
}
|
||||||
|
|
||||||
|
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||||
|
ggml_tensor* x,
|
||||||
|
bool causal = true) {
|
||||||
|
// x: [N*IC, ID, IH, IW]
|
||||||
|
// result: [N*OC, OD, OH, OW]
|
||||||
|
auto conv = std::dynamic_pointer_cast<Conv3d>(blocks["conv"]);
|
||||||
|
if (causal) {
|
||||||
|
auto h = ggml_cont(ctx, ggml_permute(ctx, x, 0, 1, 3, 2)); // [ID, N*IC, IH, IW]
|
||||||
|
auto first_frame = ggml_view_3d(ctx, h, h->ne[0], h->ne[1], h->ne[2], h->nb[1], h->nb[2], 0); // [N*IC, IH, IW]
|
||||||
|
first_frame = ggml_reshape_4d(ctx, first_frame, first_frame->ne[0], first_frame->ne[1], 1, first_frame->ne[2]); // [N*IC, 1, IH, IW]
|
||||||
|
auto first_frame_pad = first_frame;
|
||||||
|
for (int i = 1; i < time_kernel_size - 1; i++) {
|
||||||
|
first_frame_pad = ggml_concat(ctx, first_frame_pad, first_frame, 2);
|
||||||
|
}
|
||||||
|
x = ggml_concat(ctx, first_frame_pad, x, 2);
|
||||||
|
} else {
|
||||||
|
auto h = ggml_cont(ctx, ggml_permute(ctx, x, 0, 1, 3, 2)); // [ID, N*IC, IH, IW]
|
||||||
|
int64_t offset = h->nb[2] * h->ne[2];
|
||||||
|
|
||||||
|
auto first_frame = ggml_view_3d(ctx, h, h->ne[0], h->ne[1], h->ne[2], h->nb[1], h->nb[2], 0); // [N*IC, IH, IW]
|
||||||
|
first_frame = ggml_reshape_4d(ctx, first_frame, first_frame->ne[0], first_frame->ne[1], 1, first_frame->ne[2]); // [N*IC, 1, IH, IW]
|
||||||
|
auto first_frame_pad = first_frame;
|
||||||
|
for (int i = 1; i < (time_kernel_size - 1) / 2; i++) {
|
||||||
|
first_frame_pad = ggml_concat(ctx, first_frame_pad, first_frame, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto last_frame = ggml_view_3d(ctx, h, h->ne[0], h->ne[1], h->ne[2], h->nb[1], h->nb[2], offset * (h->ne[3] - 1)); // [N*IC, IH, IW]
|
||||||
|
last_frame = ggml_reshape_4d(ctx, last_frame, last_frame->ne[0], last_frame->ne[1], 1, last_frame->ne[2]); // [N*IC, 1, IH, IW]
|
||||||
|
auto last_frame_pad = last_frame;
|
||||||
|
for (int i = 1; i < (time_kernel_size - 1) / 2; i++) {
|
||||||
|
last_frame_pad = ggml_concat(ctx, last_frame_pad, last_frame, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = ggml_concat(ctx, first_frame_pad, x, 2);
|
||||||
|
x = ggml_concat(ctx, x, last_frame_pad, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = conv->forward(ctx, x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,137 +1,41 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_MMDIT_HPP__
|
#ifndef __MMDIT_HPP__
|
||||||
#define __SD_MODEL_DIFFUSION_MMDIT_HPP__
|
#define __MMDIT_HPP__
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
#include "ggml_extend.hpp"
|
||||||
#include "model/common/block.hpp"
|
#include "model.h"
|
||||||
#include "model/diffusion/model.hpp"
|
|
||||||
#include "model_loader.h"
|
|
||||||
|
|
||||||
#define MMDIT_GRAPH_SIZE 10240
|
#define MMDIT_GRAPH_SIZE 10240
|
||||||
|
|
||||||
struct MMDiTConfig {
|
struct Mlp : public GGMLBlock {
|
||||||
int64_t input_size = -1;
|
public:
|
||||||
int patch_size = 2;
|
Mlp(int64_t in_features,
|
||||||
int64_t in_channels = 16;
|
int64_t hidden_features = -1,
|
||||||
int64_t d_self = -1; // >=0 for MMdiT-X
|
int64_t out_features = -1,
|
||||||
int64_t depth = 24;
|
bool bias = true) {
|
||||||
float mlp_ratio = 4.0f;
|
// act_layer is always lambda: nn.GELU(approximate="tanh")
|
||||||
int64_t adm_in_channels = 2048;
|
// norm_layer is always None
|
||||||
int64_t out_channels = 16;
|
// use_conv is always False
|
||||||
int64_t pos_embed_max_size = 192;
|
if (hidden_features == -1) {
|
||||||
int64_t num_patches = 36864; // 192 * 192
|
hidden_features = in_features;
|
||||||
int64_t context_size = 4096;
|
}
|
||||||
int64_t context_embedder_out_dim = 1536;
|
if (out_features == -1) {
|
||||||
int64_t hidden_size = 1536;
|
out_features = in_features;
|
||||||
std::string qk_norm;
|
}
|
||||||
|
blocks["fc1"] = std::shared_ptr<GGMLBlock>(new Linear(in_features, hidden_features, bias));
|
||||||
static MMDiTConfig detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
blocks["fc2"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_features, out_features, bias));
|
||||||
MMDiTConfig config;
|
|
||||||
bool has_weight_config = false;
|
|
||||||
bool has_pos_embed = false;
|
|
||||||
bool has_hidden_size = false;
|
|
||||||
bool has_context_embed = false;
|
|
||||||
|
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
|
||||||
if (!starts_with(name, prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.find("x_embedder.proj.weight") != std::string::npos && tensor_storage.n_dims == 4) {
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||||
has_weight_config = true;
|
// x: [N, n_token, in_features]
|
||||||
has_hidden_size = true;
|
auto fc1 = std::dynamic_pointer_cast<Linear>(blocks["fc1"]);
|
||||||
config.patch_size = static_cast<int>(tensor_storage.ne[0]);
|
auto fc2 = std::dynamic_pointer_cast<Linear>(blocks["fc2"]);
|
||||||
config.in_channels = tensor_storage.ne[2];
|
|
||||||
config.hidden_size = tensor_storage.ne[3];
|
|
||||||
} else if (name.find("t_embedder.mlp.0.weight") != std::string::npos && tensor_storage.n_dims == 2) {
|
|
||||||
has_weight_config = true;
|
|
||||||
has_hidden_size = true;
|
|
||||||
config.hidden_size = tensor_storage.ne[1];
|
|
||||||
} else if (name.find("y_embedder.mlp.0.weight") != std::string::npos && tensor_storage.n_dims == 2) {
|
|
||||||
has_weight_config = true;
|
|
||||||
has_hidden_size = true;
|
|
||||||
config.adm_in_channels = tensor_storage.ne[0];
|
|
||||||
config.hidden_size = tensor_storage.ne[1];
|
|
||||||
} else if (name.find("context_embedder.weight") != std::string::npos && tensor_storage.n_dims == 2) {
|
|
||||||
has_weight_config = true;
|
|
||||||
has_context_embed = true;
|
|
||||||
config.context_size = tensor_storage.ne[0];
|
|
||||||
config.context_embedder_out_dim = tensor_storage.ne[1];
|
|
||||||
} else if (name.find("final_layer.linear.weight") != std::string::npos && tensor_storage.n_dims == 2) {
|
|
||||||
has_weight_config = true;
|
|
||||||
has_hidden_size = true;
|
|
||||||
config.hidden_size = tensor_storage.ne[0];
|
|
||||||
int64_t patch_area = static_cast<int64_t>(config.patch_size) * config.patch_size;
|
|
||||||
if (patch_area > 0) {
|
|
||||||
config.out_channels = tensor_storage.ne[1] / patch_area;
|
|
||||||
}
|
|
||||||
} else if (name.find("pos_embed") != std::string::npos && tensor_storage.n_dims == 3) {
|
|
||||||
has_weight_config = true;
|
|
||||||
has_pos_embed = true;
|
|
||||||
has_hidden_size = true;
|
|
||||||
config.hidden_size = tensor_storage.ne[0];
|
|
||||||
config.num_patches = tensor_storage.ne[1];
|
|
||||||
for (int64_t size = 1; size * size <= config.num_patches; size++) {
|
|
||||||
if (size * size == config.num_patches) {
|
|
||||||
config.pos_embed_max_size = size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t jb = name.find("joint_blocks.");
|
x = fc1->forward(ctx, x);
|
||||||
if (jb == std::string::npos) {
|
x = ggml_ext_gelu(ctx->ggml_ctx, x, true);
|
||||||
continue;
|
x = fc2->forward(ctx, x);
|
||||||
}
|
return x;
|
||||||
|
|
||||||
has_weight_config = true;
|
|
||||||
std::string block_name = name.substr(jb);
|
|
||||||
int64_t block_depth = atoi(block_name.substr(13, block_name.find(".", 13)).c_str());
|
|
||||||
if (block_depth + 1 > config.depth) {
|
|
||||||
config.depth = block_depth + 1;
|
|
||||||
}
|
|
||||||
if (block_name.find("attn.ln") != std::string::npos) {
|
|
||||||
if (block_name.find(".bias") != std::string::npos) {
|
|
||||||
config.qk_norm = "ln";
|
|
||||||
} else {
|
|
||||||
config.qk_norm = "rms";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (block_name.find("attn2") != std::string::npos) {
|
|
||||||
if (block_depth > config.d_self) {
|
|
||||||
config.d_self = block_depth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_pos_embed && config.d_self >= 0) {
|
|
||||||
config.pos_embed_max_size *= 2;
|
|
||||||
config.num_patches *= 4;
|
|
||||||
}
|
|
||||||
if (!has_hidden_size || config.hidden_size <= 0) {
|
|
||||||
config.hidden_size = 64 * config.depth;
|
|
||||||
}
|
|
||||||
if (!has_context_embed || config.context_embedder_out_dim <= 0) {
|
|
||||||
config.context_embedder_out_dim = config.hidden_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_weight_config) {
|
|
||||||
LOG_DEBUG("mmdit: num_layers = %" PRId64 ", num_mmdit_x_layers = %" PRId64 ", hidden_size = %" PRId64 ", patch_size = %d, in_channels = %" PRId64 ", out_channels = %" PRId64 ", context_size = %" PRId64 ", adm_in_channels = %" PRId64 ", qk_norm = %s",
|
|
||||||
config.depth,
|
|
||||||
config.d_self + 1,
|
|
||||||
config.hidden_size,
|
|
||||||
config.patch_size,
|
|
||||||
config.in_channels,
|
|
||||||
config.out_channels,
|
|
||||||
config.context_size,
|
|
||||||
config.adm_in_channels,
|
|
||||||
config.qk_norm.empty() ? "none" : config.qk_norm.c_str());
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -707,16 +611,28 @@ public:
|
|||||||
struct MMDiT : public GGMLBlock {
|
struct MMDiT : public GGMLBlock {
|
||||||
// Diffusion model with a Transformer backbone.
|
// Diffusion model with a Transformer backbone.
|
||||||
protected:
|
protected:
|
||||||
|
int64_t input_size = -1;
|
||||||
|
int patch_size = 2;
|
||||||
|
int64_t in_channels = 16;
|
||||||
|
int64_t d_self = -1; // >=0 for MMdiT-X
|
||||||
|
int64_t depth = 24;
|
||||||
|
float mlp_ratio = 4.0f;
|
||||||
|
int64_t adm_in_channels = 2048;
|
||||||
|
int64_t out_channels = 16;
|
||||||
|
int64_t pos_embed_max_size = 192;
|
||||||
|
int64_t num_patchs = 36864; // 192 * 192
|
||||||
|
int64_t context_size = 4096;
|
||||||
|
int64_t context_embedder_out_dim = 1536;
|
||||||
|
int64_t hidden_size;
|
||||||
|
std::string qk_norm;
|
||||||
|
|
||||||
void init_params(ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, std::string prefix = "") override {
|
void init_params(ggml_context* ctx, const String2TensorStorage& tensor_storage_map = {}, std::string prefix = "") override {
|
||||||
enum ggml_type wtype = GGML_TYPE_F32;
|
enum ggml_type wtype = GGML_TYPE_F32;
|
||||||
params["pos_embed"] = ggml_new_tensor_3d(ctx, wtype, config.hidden_size, config.num_patches, 1);
|
params["pos_embed"] = ggml_new_tensor_3d(ctx, wtype, hidden_size, num_patchs, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MMDiTConfig config;
|
MMDiT(const String2TensorStorage& tensor_storage_map = {}) {
|
||||||
|
|
||||||
explicit MMDiT(MMDiTConfig config = {})
|
|
||||||
: config(config) {
|
|
||||||
// input_size is always None
|
// input_size is always None
|
||||||
// learn_sigma is always False
|
// learn_sigma is always False
|
||||||
// register_length is alwalys 0
|
// register_length is alwalys 0
|
||||||
@ -729,30 +645,64 @@ public:
|
|||||||
// pos_embed_offset is not used
|
// pos_embed_offset is not used
|
||||||
// context_embedder_config is always {'target': 'torch.nn.Linear', 'params': {'in_features': 4096, 'out_features': 1536}}
|
// context_embedder_config is always {'target': 'torch.nn.Linear', 'params': {'in_features': 4096, 'out_features': 1536}}
|
||||||
|
|
||||||
blocks["x_embedder"] = std::shared_ptr<GGMLBlock>(new PatchEmbed(config.input_size,
|
for (auto pair : tensor_storage_map) {
|
||||||
config.patch_size,
|
std::string tensor_name = pair.first;
|
||||||
config.in_channels,
|
if (tensor_name.find("model.diffusion_model.") == std::string::npos)
|
||||||
config.hidden_size,
|
continue;
|
||||||
true));
|
size_t jb = tensor_name.find("joint_blocks.");
|
||||||
blocks["t_embedder"] = std::shared_ptr<GGMLBlock>(new TimestepEmbedder(config.hidden_size));
|
if (jb != std::string::npos) {
|
||||||
|
tensor_name = tensor_name.substr(jb); // remove prefix
|
||||||
if (config.adm_in_channels != -1) {
|
int block_depth = atoi(tensor_name.substr(13, tensor_name.find(".", 13)).c_str());
|
||||||
blocks["y_embedder"] = std::shared_ptr<GGMLBlock>(new VectorEmbedder(config.adm_in_channels, config.hidden_size));
|
if (block_depth + 1 > depth) {
|
||||||
|
depth = block_depth + 1;
|
||||||
|
}
|
||||||
|
if (tensor_name.find("attn.ln") != std::string::npos) {
|
||||||
|
if (tensor_name.find(".bias") != std::string::npos) {
|
||||||
|
qk_norm = "ln";
|
||||||
|
} else {
|
||||||
|
qk_norm = "rms";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tensor_name.find("attn2") != std::string::npos) {
|
||||||
|
if (block_depth > d_self) {
|
||||||
|
d_self = block_depth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks["context_embedder"] = std::shared_ptr<GGMLBlock>(new Linear(config.context_size, config.context_embedder_out_dim, true, true));
|
if (d_self >= 0) {
|
||||||
|
pos_embed_max_size *= 2;
|
||||||
|
num_patchs *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < config.depth; i++) {
|
LOG_INFO("MMDiT layers: %d (including %d MMDiT-x layers)", depth, d_self + 1);
|
||||||
blocks["joint_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new JointBlock(config.hidden_size,
|
|
||||||
config.depth,
|
int64_t default_out_channels = in_channels;
|
||||||
config.mlp_ratio,
|
hidden_size = 64 * depth;
|
||||||
config.qk_norm,
|
context_embedder_out_dim = 64 * depth;
|
||||||
|
int64_t num_heads = depth;
|
||||||
|
|
||||||
|
blocks["x_embedder"] = std::shared_ptr<GGMLBlock>(new PatchEmbed(input_size, patch_size, in_channels, hidden_size, true));
|
||||||
|
blocks["t_embedder"] = std::shared_ptr<GGMLBlock>(new TimestepEmbedder(hidden_size));
|
||||||
|
|
||||||
|
if (adm_in_channels != -1) {
|
||||||
|
blocks["y_embedder"] = std::shared_ptr<GGMLBlock>(new VectorEmbedder(adm_in_channels, hidden_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks["context_embedder"] = std::shared_ptr<GGMLBlock>(new Linear(4096, context_embedder_out_dim, true, true));
|
||||||
|
|
||||||
|
for (int i = 0; i < depth; i++) {
|
||||||
|
blocks["joint_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new JointBlock(hidden_size,
|
||||||
|
num_heads,
|
||||||
|
mlp_ratio,
|
||||||
|
qk_norm,
|
||||||
true,
|
true,
|
||||||
i == config.depth - 1,
|
i == depth - 1,
|
||||||
i <= config.d_self));
|
i <= d_self));
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks["final_layer"] = std::shared_ptr<GGMLBlock>(new FinalLayer(config.hidden_size, config.patch_size, config.out_channels));
|
blocks["final_layer"] = std::shared_ptr<GGMLBlock>(new FinalLayer(hidden_size, patch_size, out_channels));
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor*
|
ggml_tensor*
|
||||||
@ -761,22 +711,22 @@ public:
|
|||||||
int64_t w) {
|
int64_t w) {
|
||||||
auto pos_embed = params["pos_embed"];
|
auto pos_embed = params["pos_embed"];
|
||||||
|
|
||||||
h = (h + 1) / config.patch_size;
|
h = (h + 1) / patch_size;
|
||||||
w = (w + 1) / config.patch_size;
|
w = (w + 1) / patch_size;
|
||||||
|
|
||||||
GGML_ASSERT(h <= config.pos_embed_max_size && h > 0);
|
GGML_ASSERT(h <= pos_embed_max_size && h > 0);
|
||||||
GGML_ASSERT(w <= config.pos_embed_max_size && w > 0);
|
GGML_ASSERT(w <= pos_embed_max_size && w > 0);
|
||||||
|
|
||||||
int64_t top = (config.pos_embed_max_size - h) / 2;
|
int64_t top = (pos_embed_max_size - h) / 2;
|
||||||
int64_t left = (config.pos_embed_max_size - w) / 2;
|
int64_t left = (pos_embed_max_size - w) / 2;
|
||||||
|
|
||||||
auto spatial_pos_embed = ggml_reshape_3d(ctx, pos_embed, config.hidden_size, config.pos_embed_max_size, config.pos_embed_max_size);
|
auto spatial_pos_embed = ggml_reshape_3d(ctx, pos_embed, hidden_size, pos_embed_max_size, pos_embed_max_size);
|
||||||
|
|
||||||
// spatial_pos_embed = spatial_pos_embed[:, top : top + h, left : left + w, :]
|
// spatial_pos_embed = spatial_pos_embed[:, top : top + h, left : left + w, :]
|
||||||
spatial_pos_embed = ggml_view_3d(ctx,
|
spatial_pos_embed = ggml_view_3d(ctx,
|
||||||
spatial_pos_embed,
|
spatial_pos_embed,
|
||||||
config.hidden_size,
|
hidden_size,
|
||||||
config.pos_embed_max_size,
|
pos_embed_max_size,
|
||||||
h,
|
h,
|
||||||
spatial_pos_embed->nb[1],
|
spatial_pos_embed->nb[1],
|
||||||
spatial_pos_embed->nb[2],
|
spatial_pos_embed->nb[2],
|
||||||
@ -784,14 +734,14 @@ public:
|
|||||||
spatial_pos_embed = ggml_cont(ctx, ggml_permute(ctx, spatial_pos_embed, 0, 2, 1, 3)); // [pos_embed_max_size, h, hidden_size]
|
spatial_pos_embed = ggml_cont(ctx, ggml_permute(ctx, spatial_pos_embed, 0, 2, 1, 3)); // [pos_embed_max_size, h, hidden_size]
|
||||||
spatial_pos_embed = ggml_view_3d(ctx,
|
spatial_pos_embed = ggml_view_3d(ctx,
|
||||||
spatial_pos_embed,
|
spatial_pos_embed,
|
||||||
config.hidden_size,
|
hidden_size,
|
||||||
h,
|
h,
|
||||||
w,
|
w,
|
||||||
spatial_pos_embed->nb[1],
|
spatial_pos_embed->nb[1],
|
||||||
spatial_pos_embed->nb[2],
|
spatial_pos_embed->nb[2],
|
||||||
spatial_pos_embed->nb[2] * left); // [w, h, hidden_size]
|
spatial_pos_embed->nb[2] * left); // [w, h, hidden_size]
|
||||||
spatial_pos_embed = ggml_cont(ctx, ggml_permute(ctx, spatial_pos_embed, 0, 2, 1, 3)); // [h, w, hidden_size]
|
spatial_pos_embed = ggml_cont(ctx, ggml_permute(ctx, spatial_pos_embed, 0, 2, 1, 3)); // [h, w, hidden_size]
|
||||||
spatial_pos_embed = ggml_reshape_3d(ctx, spatial_pos_embed, config.hidden_size, h * w, 1); // [1, h*w, hidden_size]
|
spatial_pos_embed = ggml_reshape_3d(ctx, spatial_pos_embed, hidden_size, h * w, 1); // [1, h*w, hidden_size]
|
||||||
return spatial_pos_embed;
|
return spatial_pos_embed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,7 +756,7 @@ public:
|
|||||||
// return: [N, N*W, patch_size * patch_size * out_channels]
|
// return: [N, N*W, patch_size * patch_size * out_channels]
|
||||||
auto final_layer = std::dynamic_pointer_cast<FinalLayer>(blocks["final_layer"]);
|
auto final_layer = std::dynamic_pointer_cast<FinalLayer>(blocks["final_layer"]);
|
||||||
|
|
||||||
for (int i = 0; i < config.depth; i++) {
|
for (int i = 0; i < depth; i++) {
|
||||||
// skip iteration if i is in skip_layers
|
// skip iteration if i is in skip_layers
|
||||||
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i) != skip_layers.end()) {
|
if (skip_layers.size() > 0 && std::find(skip_layers.begin(), skip_layers.end(), i) != skip_layers.end()) {
|
||||||
continue;
|
continue;
|
||||||
@ -817,8 +767,6 @@ public:
|
|||||||
auto context_x = block->forward(ctx, context, x, c_mod);
|
auto context_x = block->forward(ctx, context, x, c_mod);
|
||||||
context = context_x.first;
|
context = context_x.first;
|
||||||
x = context_x.second;
|
x = context_x.second;
|
||||||
sd::ggml_graph_cut::mark_graph_cut(context, "mmdit.joint_blocks." + std::to_string(i), "context");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "mmdit.joint_blocks." + std::to_string(i), "x");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x = final_layer->forward(ctx, x, c_mod); // (N, T, patch_size ** 2 * out_channels)
|
x = final_layer->forward(ctx, x, c_mod); // (N, T, patch_size ** 2 * out_channels)
|
||||||
@ -849,7 +797,7 @@ public:
|
|||||||
x = ggml_add(ctx->ggml_ctx, patch_embed, pos_embed); // [N, H*W, hidden_size]
|
x = ggml_add(ctx->ggml_ctx, patch_embed, pos_embed); // [N, H*W, hidden_size]
|
||||||
|
|
||||||
auto c = t_embedder->forward(ctx, t); // [N, hidden_size]
|
auto c = t_embedder->forward(ctx, t); // [N, hidden_size]
|
||||||
if (y != nullptr && config.adm_in_channels != -1) {
|
if (y != nullptr && adm_in_channels != -1) {
|
||||||
auto y_embedder = std::dynamic_pointer_cast<VectorEmbedder>(blocks["y_embedder"]);
|
auto y_embedder = std::dynamic_pointer_cast<VectorEmbedder>(blocks["y_embedder"]);
|
||||||
|
|
||||||
y = y_embedder->forward(ctx, y); // [N, hidden_size]
|
y = y_embedder->forward(ctx, y); // [N, hidden_size]
|
||||||
@ -861,30 +809,22 @@ public:
|
|||||||
|
|
||||||
context = context_embedder->forward(ctx, context); // [N, L, D] aka [N, L, 1536]
|
context = context_embedder->forward(ctx, context); // [N, L, D] aka [N, L, 1536]
|
||||||
}
|
}
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "mmdit.prelude", "x");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(c, "mmdit.prelude", "c");
|
|
||||||
if (context != nullptr) {
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(context, "mmdit.prelude", "context");
|
|
||||||
}
|
|
||||||
|
|
||||||
x = forward_core_with_concat(ctx, x, c, context, skip_layers); // (N, H*W, patch_size ** 2 * out_channels)
|
x = forward_core_with_concat(ctx, x, c, context, skip_layers); // (N, H*W, patch_size ** 2 * out_channels)
|
||||||
|
|
||||||
x = DiT::unpatchify_and_crop(ctx->ggml_ctx, x, H, W, config.patch_size, config.patch_size, /*patch_last*/ false); // [N, C, H, W]
|
x = DiT::unpatchify_and_crop(ctx->ggml_ctx, x, H, W, patch_size, patch_size, /*patch_last*/ false); // [N, C, H, W]
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct MMDiTRunner : public DiffusionModelRunner {
|
struct MMDiTRunner : public GGMLRunner {
|
||||||
MMDiTConfig config;
|
|
||||||
MMDiT mmdit;
|
MMDiT mmdit;
|
||||||
|
|
||||||
MMDiTRunner(ggml_backend_t backend,
|
MMDiTRunner(ggml_backend_t backend,
|
||||||
ggml_backend_t params_backend,
|
bool offload_params_to_cpu,
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
const String2TensorStorage& tensor_storage_map = {},
|
||||||
const std::string prefix = "")
|
const std::string prefix = "")
|
||||||
: DiffusionModelRunner(backend, params_backend, prefix),
|
: GGMLRunner(backend, offload_params_to_cpu), mmdit(tensor_storage_map) {
|
||||||
config(MMDiTConfig::detect_from_weights(tensor_storage_map, prefix)),
|
|
||||||
mmdit(config) {
|
|
||||||
mmdit.init(params_ctx, tensor_storage_map, prefix);
|
mmdit.init(params_ctx, tensor_storage_map, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,7 +832,7 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
|||||||
return "mmdit";
|
return "mmdit";
|
||||||
}
|
}
|
||||||
|
|
||||||
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, const std::string prefix) {
|
||||||
mmdit.get_param_tensors(tensors, prefix);
|
mmdit.get_param_tensors(tensors, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,20 +878,6 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
|||||||
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), x.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
const auto* extra = diffusion_extra_as<SkipLayerDiffusionExtra>(diffusion_params);
|
|
||||||
static const std::vector<int> empty_skip_layers;
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context),
|
|
||||||
tensor_or_empty(diffusion_params.y),
|
|
||||||
extra->skip_layers ? *extra->skip_layers : empty_skip_layers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test() {
|
void test() {
|
||||||
ggml_init_params params;
|
ggml_init_params params;
|
||||||
params.mem_size = static_cast<size_t>(10 * 1024 * 1024); // 10 MB
|
params.mem_size = static_cast<size_t>(10 * 1024 * 1024); // 10 MB
|
||||||
@ -999,17 +925,13 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
|||||||
|
|
||||||
static void load_from_file_and_test(const std::string& file_path) {
|
static void load_from_file_and_test(const std::string& file_path) {
|
||||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||||
ggml_backend_t backend = sd_backend_cpu_init();
|
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||||
ggml_type model_data_type = GGML_TYPE_F16;
|
ggml_type model_data_type = GGML_TYPE_F16;
|
||||||
std::shared_ptr<MMDiTRunner> mmdit = std::make_shared<MMDiTRunner>(backend, backend);
|
std::shared_ptr<MMDiTRunner> mmdit = std::make_shared<MMDiTRunner>(backend, false);
|
||||||
{
|
{
|
||||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||||
|
|
||||||
if (!mmdit->alloc_params_buffer()) {
|
mmdit->alloc_params_buffer();
|
||||||
LOG_ERROR("mmdit embeds buffer allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ggml_tensor*> tensors;
|
std::map<std::string, ggml_tensor*> tensors;
|
||||||
mmdit->get_param_tensors(tensors, "model.diffusion_model");
|
mmdit->get_param_tensors(tensors, "model.diffusion_model");
|
||||||
|
|
||||||
@ -1032,4 +954,4 @@ struct MMDiTRunner : public DiffusionModelRunner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_MMDIT_HPP__
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
228
src/model.h
228
src/model.h
@ -1,14 +1,24 @@
|
|||||||
#ifndef __MODEL_H__
|
#ifndef __MODEL_H__
|
||||||
#define __MODEL_H__
|
#define __MODEL_H__
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/ordered_map.hpp"
|
|
||||||
#include "ggml-backend.h"
|
#include "ggml-backend.h"
|
||||||
#include "ggml.h"
|
#include "ggml.h"
|
||||||
#include "model_io/tensor_storage.h"
|
#include "gguf.h"
|
||||||
|
#include "json.hpp"
|
||||||
|
#include "ordered_map.hpp"
|
||||||
|
#include "zip.h"
|
||||||
|
|
||||||
|
#define SD_MAX_DIMS 5
|
||||||
|
|
||||||
enum SDVersion {
|
enum SDVersion {
|
||||||
VERSION_SD1,
|
VERSION_SD1,
|
||||||
@ -39,15 +49,9 @@ enum SDVersion {
|
|||||||
VERSION_ANIMA,
|
VERSION_ANIMA,
|
||||||
VERSION_FLUX2,
|
VERSION_FLUX2,
|
||||||
VERSION_FLUX2_KLEIN,
|
VERSION_FLUX2_KLEIN,
|
||||||
VERSION_LTXAV,
|
|
||||||
VERSION_HIDREAM_O1,
|
|
||||||
VERSION_Z_IMAGE,
|
VERSION_Z_IMAGE,
|
||||||
VERSION_OVIS_IMAGE,
|
VERSION_OVIS_IMAGE,
|
||||||
VERSION_ERNIE_IMAGE,
|
VERSION_ERNIE_IMAGE,
|
||||||
VERSION_LENS,
|
|
||||||
VERSION_LONGCAT,
|
|
||||||
VERSION_PID,
|
|
||||||
VERSION_IDEOGRAM4,
|
|
||||||
VERSION_COUNT,
|
VERSION_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,13 +111,6 @@ static inline bool sd_version_is_flux2(SDVersion version) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sd_version_is_ltxav(SDVersion version) {
|
|
||||||
if (version == VERSION_LTXAV) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sd_version_is_wan(SDVersion version) {
|
static inline bool sd_version_is_wan(SDVersion version) {
|
||||||
if (version == VERSION_WAN2 || version == VERSION_WAN2_2_I2V || version == VERSION_WAN2_2_TI2V) {
|
if (version == VERSION_WAN2 || version == VERSION_WAN2_2_I2V || version == VERSION_WAN2_2_TI2V) {
|
||||||
return true;
|
return true;
|
||||||
@ -142,13 +139,6 @@ static inline bool sd_version_is_z_image(SDVersion version) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sd_version_is_longcat(SDVersion version) {
|
|
||||||
if (version == VERSION_LONGCAT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sd_version_is_ernie_image(SDVersion version) {
|
static inline bool sd_version_is_ernie_image(SDVersion version) {
|
||||||
if (version == VERSION_ERNIE_IMAGE) {
|
if (version == VERSION_ERNIE_IMAGE) {
|
||||||
return true;
|
return true;
|
||||||
@ -156,29 +146,8 @@ static inline bool sd_version_is_ernie_image(SDVersion version) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sd_version_is_lens(SDVersion version) {
|
|
||||||
if (version == VERSION_LENS) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sd_version_is_pid(SDVersion version) {
|
|
||||||
if (version == VERSION_PID) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sd_version_is_ideogram4(SDVersion version) {
|
|
||||||
if (version == VERSION_IDEOGRAM4) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool sd_version_uses_flux2_vae(SDVersion version) {
|
static inline bool sd_version_uses_flux2_vae(SDVersion version) {
|
||||||
if (sd_version_is_flux2(version) || sd_version_is_ernie_image(version) || sd_version_is_lens(version) || sd_version_is_ideogram4(version)) {
|
if (sd_version_is_flux2(version) || sd_version_is_ernie_image(version)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -198,18 +167,12 @@ static inline bool sd_version_is_inpaint(SDVersion version) {
|
|||||||
static inline bool sd_version_is_dit(SDVersion version) {
|
static inline bool sd_version_is_dit(SDVersion version) {
|
||||||
if (sd_version_is_flux(version) ||
|
if (sd_version_is_flux(version) ||
|
||||||
sd_version_is_flux2(version) ||
|
sd_version_is_flux2(version) ||
|
||||||
sd_version_is_ltxav(version) ||
|
|
||||||
sd_version_is_sd3(version) ||
|
sd_version_is_sd3(version) ||
|
||||||
sd_version_is_wan(version) ||
|
sd_version_is_wan(version) ||
|
||||||
sd_version_is_qwen_image(version) ||
|
sd_version_is_qwen_image(version) ||
|
||||||
version == VERSION_HIDREAM_O1 ||
|
|
||||||
sd_version_is_anima(version) ||
|
sd_version_is_anima(version) ||
|
||||||
sd_version_is_z_image(version) ||
|
sd_version_is_z_image(version) ||
|
||||||
sd_version_is_ernie_image(version) ||
|
sd_version_is_ernie_image(version)) {
|
||||||
sd_version_is_lens(version) ||
|
|
||||||
sd_version_is_longcat(version) ||
|
|
||||||
sd_version_is_pid(version) ||
|
|
||||||
sd_version_is_ideogram4(version)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -232,7 +195,168 @@ enum PMVersion {
|
|||||||
PM_VERSION_2,
|
PM_VERSION_2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TensorStorage {
|
||||||
|
std::string name;
|
||||||
|
ggml_type type = GGML_TYPE_F32;
|
||||||
|
ggml_type expected_type = GGML_TYPE_COUNT;
|
||||||
|
bool is_f8_e4m3 = false;
|
||||||
|
bool is_f8_e5m2 = false;
|
||||||
|
bool is_f64 = false;
|
||||||
|
bool is_i64 = false;
|
||||||
|
int64_t ne[SD_MAX_DIMS] = {1, 1, 1, 1, 1};
|
||||||
|
int n_dims = 0;
|
||||||
|
|
||||||
|
size_t file_index = 0;
|
||||||
|
int index_in_zip = -1; // >= means stored in a zip file
|
||||||
|
uint64_t offset = 0; // offset in file
|
||||||
|
|
||||||
|
TensorStorage() = default;
|
||||||
|
|
||||||
|
TensorStorage(std::string name, ggml_type type, const int64_t* ne, int n_dims, size_t file_index, size_t offset = 0)
|
||||||
|
: name(std::move(name)), type(type), n_dims(n_dims), file_index(file_index), offset(offset) {
|
||||||
|
for (int i = 0; i < n_dims; i++) {
|
||||||
|
this->ne[i] = ne[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t nelements() const {
|
||||||
|
int64_t n = 1;
|
||||||
|
for (int i = 0; i < SD_MAX_DIMS; i++) {
|
||||||
|
n *= ne[i];
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t nbytes() const {
|
||||||
|
return nelements() * ggml_type_size(type) / ggml_blck_size(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t nbytes_to_read() const {
|
||||||
|
if (is_f8_e4m3 || is_f8_e5m2) {
|
||||||
|
return nbytes() / 2;
|
||||||
|
} else if (is_f64 || is_i64) {
|
||||||
|
return nbytes() * 2;
|
||||||
|
} else {
|
||||||
|
return nbytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsqueeze() {
|
||||||
|
if (n_dims == 2) {
|
||||||
|
n_dims = 4;
|
||||||
|
ne[3] = ne[1];
|
||||||
|
ne[2] = ne[0];
|
||||||
|
ne[1] = 1;
|
||||||
|
ne[0] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TensorStorage> chunk(size_t n) {
|
||||||
|
std::vector<TensorStorage> chunks;
|
||||||
|
uint64_t chunk_size = nbytes_to_read() / n;
|
||||||
|
// printf("%d/%d\n", chunk_size, nbytes_to_read());
|
||||||
|
reverse_ne();
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
TensorStorage chunk_i = *this;
|
||||||
|
chunk_i.ne[0] = ne[0] / n;
|
||||||
|
chunk_i.offset = offset + i * chunk_size;
|
||||||
|
chunk_i.reverse_ne();
|
||||||
|
chunks.push_back(chunk_i);
|
||||||
|
}
|
||||||
|
reverse_ne();
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse_ne() {
|
||||||
|
int64_t new_ne[SD_MAX_DIMS] = {1, 1, 1, 1, 1};
|
||||||
|
for (int i = 0; i < n_dims; i++) {
|
||||||
|
new_ne[i] = ne[n_dims - 1 - i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n_dims; i++) {
|
||||||
|
ne[i] = new_ne[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
const char* type_name = ggml_type_name(type);
|
||||||
|
if (is_f8_e4m3) {
|
||||||
|
type_name = "f8_e4m3";
|
||||||
|
} else if (is_f8_e5m2) {
|
||||||
|
type_name = "f8_e5m2";
|
||||||
|
} else if (is_f64) {
|
||||||
|
type_name = "f64";
|
||||||
|
} else if (is_i64) {
|
||||||
|
type_name = "i64";
|
||||||
|
}
|
||||||
|
ss << name << " | " << type_name << " | ";
|
||||||
|
ss << n_dims << " [";
|
||||||
|
for (int i = 0; i < SD_MAX_DIMS; i++) {
|
||||||
|
ss << ne[i];
|
||||||
|
if (i != SD_MAX_DIMS - 1) {
|
||||||
|
ss << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss << "]";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<bool(const TensorStorage&, ggml_tensor**)> on_new_tensor_cb_t;
|
||||||
|
|
||||||
typedef OrderedMap<std::string, TensorStorage> String2TensorStorage;
|
typedef OrderedMap<std::string, TensorStorage> String2TensorStorage;
|
||||||
using TensorTypeRules = std::vector<std::pair<std::string, ggml_type>>;
|
|
||||||
|
class ModelLoader {
|
||||||
|
protected:
|
||||||
|
SDVersion version_ = VERSION_COUNT;
|
||||||
|
std::vector<std::string> file_paths_;
|
||||||
|
String2TensorStorage tensor_storage_map;
|
||||||
|
|
||||||
|
void add_tensor_storage(const TensorStorage& tensor_storage);
|
||||||
|
|
||||||
|
bool parse_data_pkl(uint8_t* buffer,
|
||||||
|
size_t buffer_size,
|
||||||
|
zip_t* zip,
|
||||||
|
std::string dir,
|
||||||
|
size_t file_index,
|
||||||
|
const std::string prefix);
|
||||||
|
|
||||||
|
bool init_from_gguf_file(const std::string& file_path, const std::string& prefix = "");
|
||||||
|
bool init_from_safetensors_file(const std::string& file_path, const std::string& prefix = "");
|
||||||
|
bool init_from_ckpt_file(const std::string& file_path, const std::string& prefix = "");
|
||||||
|
bool init_from_diffusers_file(const std::string& file_path, const std::string& prefix = "");
|
||||||
|
|
||||||
|
public:
|
||||||
|
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,
|
||||||
|
const std::string& prefix = "",
|
||||||
|
SDVersion version = VERSION_COUNT);
|
||||||
|
SDVersion get_sd_version();
|
||||||
|
std::map<ggml_type, uint32_t> get_wtype_stat();
|
||||||
|
std::map<ggml_type, uint32_t> get_conditioner_wtype_stat();
|
||||||
|
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; }
|
||||||
|
void set_wtype_override(ggml_type wtype, std::string tensor_type_rules = "");
|
||||||
|
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0, bool use_mmap = false);
|
||||||
|
bool load_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
||||||
|
std::set<std::string> ignore_tensors = {},
|
||||||
|
int n_threads = 0,
|
||||||
|
bool use_mmap = false);
|
||||||
|
|
||||||
|
std::vector<std::string> get_tensor_names() const {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
||||||
|
names.push_back(name);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool save_to_gguf_file(const std::string& file_path, ggml_type type, const std::string& tensor_type_rules);
|
||||||
|
bool tensor_should_be_converted(const TensorStorage& tensor_storage, ggml_type type);
|
||||||
|
int64_t get_params_mem_size(ggml_backend_t backend, ggml_type type = GGML_TYPE_COUNT);
|
||||||
|
~ModelLoader() = default;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // __MODEL_H__
|
#endif // __MODEL_H__
|
||||||
|
|||||||
@ -1,681 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
|
||||||
#define __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "conditioning/conditioner.hpp"
|
|
||||||
#include "core/util.h"
|
|
||||||
#include "model/diffusion/dit.hpp"
|
|
||||||
#include "model/diffusion/model.hpp"
|
|
||||||
#include "model/te/llm.hpp"
|
|
||||||
|
|
||||||
namespace HiDreamO1 {
|
|
||||||
constexpr int HIDREAM_O1_GRAPH_SIZE = 32768;
|
|
||||||
constexpr int PATCH_SIZE = 32;
|
|
||||||
constexpr int TIMESTEP_TOKEN_NUM = 1;
|
|
||||||
constexpr int IMAGE_TOKEN_ID = 151655;
|
|
||||||
constexpr int VISION_START_TOKEN_ID = 151652;
|
|
||||||
|
|
||||||
struct HiDreamO1Config {
|
|
||||||
LLM::LLMConfig llm;
|
|
||||||
int patch_size = PATCH_SIZE;
|
|
||||||
|
|
||||||
static HiDreamO1Config detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
|
||||||
(void)tensor_storage_map;
|
|
||||||
(void)prefix;
|
|
||||||
HiDreamO1Config config;
|
|
||||||
config.llm.arch = LLM::LLMArch::QWEN3_VL;
|
|
||||||
config.llm.hidden_size = 4096;
|
|
||||||
config.llm.intermediate_size = 12288;
|
|
||||||
config.llm.num_layers = 36;
|
|
||||||
config.llm.num_heads = 32;
|
|
||||||
config.llm.num_kv_heads = 8;
|
|
||||||
config.llm.head_dim = 128;
|
|
||||||
config.llm.qkv_bias = false;
|
|
||||||
config.llm.qk_norm = true;
|
|
||||||
config.llm.vocab_size = 151936;
|
|
||||||
config.llm.rms_norm_eps = 1e-6f;
|
|
||||||
config.llm.vision.arch = LLM::LLMVisionArch::QWEN3_VL;
|
|
||||||
config.llm.vision.num_layers = 27;
|
|
||||||
config.llm.vision.hidden_size = 1152;
|
|
||||||
config.llm.vision.intermediate_size = 4304;
|
|
||||||
config.llm.vision.num_heads = 16;
|
|
||||||
config.llm.vision.out_hidden_size = 4096;
|
|
||||||
config.llm.vision.patch_size = 16;
|
|
||||||
config.llm.vision.spatial_merge_size = 2;
|
|
||||||
config.llm.vision.temporal_patch_size = 2;
|
|
||||||
config.llm.vision.num_position_embeddings = 2304;
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline std::string repeat_special_token(const std::string& token, int64_t count) {
|
|
||||||
std::string out;
|
|
||||||
out.reserve(static_cast<size_t>(count) * token.size());
|
|
||||||
for (int64_t i = 0; i < count; ++i) {
|
|
||||||
out += token;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::pair<int, int> calculate_dimensions(int max_size, double ratio) {
|
|
||||||
int width = static_cast<int>(std::sqrt(max_size * max_size * ratio));
|
|
||||||
int height = static_cast<int>(width / ratio);
|
|
||||||
width = (width / PATCH_SIZE) * PATCH_SIZE;
|
|
||||||
height = (height / PATCH_SIZE) * PATCH_SIZE;
|
|
||||||
width = std::max(width, PATCH_SIZE);
|
|
||||||
height = std::max(height, PATCH_SIZE);
|
|
||||||
return {width, height};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline sd::Tensor<float> resize_to_area(const sd::Tensor<float>& image, int image_size) {
|
|
||||||
int64_t width = image.shape()[0];
|
|
||||||
int64_t height = image.shape()[1];
|
|
||||||
int64_t s_max = static_cast<int64_t>(image_size) * image_size;
|
|
||||||
double scale = std::sqrt(static_cast<double>(s_max) / static_cast<double>(width * height));
|
|
||||||
|
|
||||||
std::vector<std::pair<int64_t, int64_t>> sizes = {
|
|
||||||
{(static_cast<int64_t>(std::llround(width * scale)) / PATCH_SIZE) * PATCH_SIZE, (static_cast<int64_t>(std::llround(height * scale)) / PATCH_SIZE) * PATCH_SIZE},
|
|
||||||
{(static_cast<int64_t>(std::llround(width * scale)) / PATCH_SIZE) * PATCH_SIZE, (static_cast<int64_t>(std::floor(height * scale)) / PATCH_SIZE) * PATCH_SIZE},
|
|
||||||
{(static_cast<int64_t>(std::floor(width * scale)) / PATCH_SIZE) * PATCH_SIZE, (static_cast<int64_t>(std::llround(height * scale)) / PATCH_SIZE) * PATCH_SIZE},
|
|
||||||
{(static_cast<int64_t>(std::floor(width * scale)) / PATCH_SIZE) * PATCH_SIZE, (static_cast<int64_t>(std::floor(height * scale)) / PATCH_SIZE) * PATCH_SIZE},
|
|
||||||
};
|
|
||||||
std::sort(sizes.begin(), sizes.end(), [](const auto& a, const auto& b) {
|
|
||||||
return a.first * a.second > b.first * b.second;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::pair<int64_t, int64_t> new_size = sizes.back();
|
|
||||||
for (const auto& size : sizes) {
|
|
||||||
if (size.first > 0 && size.second > 0 && size.first * size.second <= s_max) {
|
|
||||||
new_size = size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double s1 = static_cast<double>(width) / static_cast<double>(new_size.first);
|
|
||||||
double s2 = static_cast<double>(height) / static_cast<double>(new_size.second);
|
|
||||||
sd::Tensor<float> resized;
|
|
||||||
if (s1 < s2) {
|
|
||||||
int64_t resized_h = static_cast<int64_t>(std::llround(height / s1));
|
|
||||||
resized = sd::ops::interpolate(image,
|
|
||||||
{new_size.first, resized_h, image.shape()[2], image.shape()[3]},
|
|
||||||
sd::ops::InterpolateMode::Bicubic);
|
|
||||||
int64_t top = (resized_h - new_size.second) / 2;
|
|
||||||
resized = sd::ops::slice(resized, 1, top, top + new_size.second);
|
|
||||||
} else {
|
|
||||||
int64_t resized_w = static_cast<int64_t>(std::llround(width / s2));
|
|
||||||
resized = sd::ops::interpolate(image,
|
|
||||||
{resized_w, new_size.second, image.shape()[2], image.shape()[3]},
|
|
||||||
sd::ops::InterpolateMode::Bicubic);
|
|
||||||
int64_t left = (resized_w - new_size.first) / 2;
|
|
||||||
resized = sd::ops::slice(resized, 0, left, left + new_size.first);
|
|
||||||
}
|
|
||||||
return resized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::vector<int32_t> build_position_ids(const std::vector<int32_t>& input_ids,
|
|
||||||
const std::vector<std::array<int32_t, 3>>& image_grids,
|
|
||||||
const std::vector<int32_t>& skip_vision_start_token) {
|
|
||||||
std::vector<int32_t> position_ids(4 * input_ids.size(), 0);
|
|
||||||
int image_index = 0;
|
|
||||||
int st = 0;
|
|
||||||
int fix_point = 4096;
|
|
||||||
std::vector<int32_t> out_t;
|
|
||||||
std::vector<int32_t> out_h;
|
|
||||||
std::vector<int32_t> out_w;
|
|
||||||
|
|
||||||
while (st < static_cast<int>(input_ids.size())) {
|
|
||||||
int ed = st;
|
|
||||||
while (ed < static_cast<int>(input_ids.size()) && input_ids[ed] != IMAGE_TOKEN_ID) {
|
|
||||||
ed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ed >= static_cast<int>(input_ids.size())) {
|
|
||||||
int st_idx = out_t.empty() ? 0 : (*std::max_element(out_t.begin(), out_t.end()) + 1);
|
|
||||||
for (int i = 0; i < static_cast<int>(input_ids.size()) - st; ++i) {
|
|
||||||
out_t.push_back(st_idx + i);
|
|
||||||
out_h.push_back(st_idx + i);
|
|
||||||
out_w.push_back(st_idx + i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int text_len = std::max(0, ed - st - skip_vision_start_token[image_index]);
|
|
||||||
int st_idx = out_t.empty() ? 0 : (*std::max_element(out_t.begin(), out_t.end()) + 1);
|
|
||||||
for (int i = 0; i < text_len; ++i) {
|
|
||||||
out_t.push_back(st_idx + i);
|
|
||||||
out_h.push_back(st_idx + i);
|
|
||||||
out_w.push_back(st_idx + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto grid = image_grids[image_index];
|
|
||||||
int base;
|
|
||||||
if (skip_vision_start_token[image_index]) {
|
|
||||||
if (fix_point > 0) {
|
|
||||||
base = fix_point;
|
|
||||||
fix_point = 0;
|
|
||||||
} else {
|
|
||||||
base = st_idx;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
base = text_len + st_idx;
|
|
||||||
}
|
|
||||||
for (int32_t ti = 0; ti < grid[0]; ++ti) {
|
|
||||||
for (int32_t hi = 0; hi < grid[1]; ++hi) {
|
|
||||||
for (int32_t wi = 0; wi < grid[2]; ++wi) {
|
|
||||||
out_t.push_back(base + ti);
|
|
||||||
out_h.push_back(base + hi);
|
|
||||||
out_w.push_back(base + wi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
st = ed + grid[0] * grid[1] * grid[2];
|
|
||||||
image_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
GGML_ASSERT(out_t.size() == input_ids.size());
|
|
||||||
for (size_t i = 0; i < input_ids.size(); ++i) {
|
|
||||||
// ggml IMROPE consumes 4 flattened position streams:
|
|
||||||
// [t, h, w, e]
|
|
||||||
// llama.cpp's generic Qwen-VL fallback expands text positions as
|
|
||||||
// [pos, pos, pos, 0]. Keep the extra stream zeroed here too.
|
|
||||||
position_ids[i] = out_t[i];
|
|
||||||
position_ids[input_ids.size() + i] = out_h[i];
|
|
||||||
position_ids[input_ids.size() * 2 + i] = out_w[i];
|
|
||||||
position_ids[input_ids.size() * 3 + i] = 0;
|
|
||||||
}
|
|
||||||
return position_ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TimestepEmbedder : public GGMLBlock {
|
|
||||||
int frequency_embedding_size = 256;
|
|
||||||
|
|
||||||
TimestepEmbedder(int64_t hidden_size) {
|
|
||||||
blocks["mlp.0"] = std::make_shared<Linear>(frequency_embedding_size, hidden_size, true);
|
|
||||||
blocks["mlp.2"] = std::make_shared<Linear>(hidden_size, hidden_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* t) {
|
|
||||||
auto mlp_0 = std::dynamic_pointer_cast<Linear>(blocks["mlp.0"]);
|
|
||||||
auto mlp_2 = std::dynamic_pointer_cast<Linear>(blocks["mlp.2"]);
|
|
||||||
auto emb = ggml_ext_timestep_embedding(ctx->ggml_ctx, t, frequency_embedding_size, 10000, 1000.0f);
|
|
||||||
emb = mlp_0->forward(ctx, emb);
|
|
||||||
emb = ggml_silu_inplace(ctx->ggml_ctx, emb);
|
|
||||||
emb = mlp_2->forward(ctx, emb);
|
|
||||||
return emb;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BottleneckPatchEmbed : public GGMLBlock {
|
|
||||||
BottleneckPatchEmbed(int64_t in_dim, int64_t pca_dim, int64_t embed_dim) {
|
|
||||||
blocks["proj1"] = std::make_shared<Linear>(in_dim, pca_dim, false);
|
|
||||||
blocks["proj2"] = std::make_shared<Linear>(pca_dim, embed_dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto proj1 = std::dynamic_pointer_cast<Linear>(blocks["proj1"]);
|
|
||||||
auto proj2 = std::dynamic_pointer_cast<Linear>(blocks["proj2"]);
|
|
||||||
return proj2->forward(ctx, proj1->forward(ctx, x));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FinalLayer : public GGMLBlock {
|
|
||||||
FinalLayer(int64_t hidden_size, int64_t out_dim) {
|
|
||||||
blocks["linear"] = std::make_shared<Linear>(hidden_size, out_dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
|
||||||
return linear->forward(ctx, x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HiDreamO1Model : public GGMLBlock {
|
|
||||||
HiDreamO1Config config;
|
|
||||||
|
|
||||||
HiDreamO1Model() = default;
|
|
||||||
explicit HiDreamO1Model(HiDreamO1Config config)
|
|
||||||
: config(std::move(config)) {
|
|
||||||
blocks["language_model"] = std::make_shared<LLM::TextModel>(this->config.llm);
|
|
||||||
blocks["t_embedder1"] = std::make_shared<TimestepEmbedder>(this->config.llm.hidden_size);
|
|
||||||
blocks["x_embedder"] = std::make_shared<BottleneckPatchEmbed>(this->config.patch_size * this->config.patch_size * 3,
|
|
||||||
this->config.llm.hidden_size / 4,
|
|
||||||
this->config.llm.hidden_size);
|
|
||||||
blocks["final_layer2"] = std::make_shared<FinalLayer>(this->config.llm.hidden_size,
|
|
||||||
this->config.patch_size * this->config.patch_size * 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<LLM::TextModel> text_model() {
|
|
||||||
return std::dynamic_pointer_cast<LLM::TextModel>(blocks["language_model"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TimestepEmbedder> timestep_embedder() {
|
|
||||||
return std::dynamic_pointer_cast<TimestepEmbedder>(blocks["t_embedder1"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<BottleneckPatchEmbed> patch_embedder() {
|
|
||||||
return std::dynamic_pointer_cast<BottleneckPatchEmbed>(blocks["x_embedder"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<FinalLayer> final_layer() {
|
|
||||||
return std::dynamic_pointer_cast<FinalLayer>(blocks["final_layer2"]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HiDreamO1VisionRunner : public GGMLRunner {
|
|
||||||
HiDreamO1Config config;
|
|
||||||
std::shared_ptr<LLM::VisionModel> model;
|
|
||||||
|
|
||||||
std::vector<int> window_index_vec;
|
|
||||||
std::vector<int> window_inverse_index_vec;
|
|
||||||
std::vector<float> window_mask_vec;
|
|
||||||
std::vector<float> pe_vec;
|
|
||||||
std::array<std::vector<int32_t>, 4> pos_embed_idx_data_;
|
|
||||||
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),
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_desc() override {
|
|
||||||
return "hidream_o1_vision";
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix = "model.visual") {
|
|
||||||
model->get_param_tensors(tensors, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* encode_image(GGMLRunnerContext* runner_ctx, ggml_tensor* image) {
|
|
||||||
return LLM::LLMRunner::encode_image_common(this,
|
|
||||||
compute_ctx,
|
|
||||||
runner_ctx,
|
|
||||||
image,
|
|
||||||
config.llm.vision,
|
|
||||||
model,
|
|
||||||
window_index_vec,
|
|
||||||
window_inverse_index_vec,
|
|
||||||
window_mask_vec,
|
|
||||||
pe_vec,
|
|
||||||
pos_embed_idx_data_,
|
|
||||||
pos_embed_weight_data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& image_tensor) {
|
|
||||||
ggml_cgraph* gf = new_graph_custom(HIDREAM_O1_GRAPH_SIZE);
|
|
||||||
ggml_tensor* image = make_input(image_tensor);
|
|
||||||
auto runner_ctx = get_context();
|
|
||||||
auto image_embeds = encode_image(&runner_ctx, image);
|
|
||||||
ggml_build_forward_expand(gf, image_embeds);
|
|
||||||
return gf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads, const sd::Tensor<float>& image) {
|
|
||||||
auto get_graph = [&]() {
|
|
||||||
return build_graph(image);
|
|
||||||
};
|
|
||||||
auto output = GGMLRunner::compute<float>(get_graph, n_threads, false);
|
|
||||||
return output.has_value() ? std::move(output.value()) : sd::Tensor<float>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HiDreamO1Runner : public DiffusionModelRunner {
|
|
||||||
HiDreamO1Config config;
|
|
||||||
HiDreamO1Model model;
|
|
||||||
|
|
||||||
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),
|
|
||||||
config(HiDreamO1Config::detect_from_weights(tensor_storage_map, prefix)) {
|
|
||||||
model = HiDreamO1Model(config);
|
|
||||||
model.init(params_ctx, tensor_storage_map, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_desc() override {
|
|
||||||
return "hidream_o1";
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
|
|
||||||
model.get_param_tensors(tensors, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
|
||||||
const sd::Tensor<float>& timestep_tensor,
|
|
||||||
const sd::Tensor<int32_t>& input_ids_tensor,
|
|
||||||
const sd::Tensor<int32_t>& input_pos_tensor,
|
|
||||||
const sd::Tensor<int32_t>& token_types_tensor,
|
|
||||||
const sd::Tensor<int32_t>& vinput_mask_tensor,
|
|
||||||
const std::vector<std::pair<int, sd::Tensor<float>>>& image_embeds_tensor,
|
|
||||||
const std::vector<sd::Tensor<float>>& ref_images) {
|
|
||||||
ggml_cgraph* gf = new_graph_custom(HIDREAM_O1_GRAPH_SIZE);
|
|
||||||
ggml_tensor* x = make_input(x_tensor);
|
|
||||||
ggml_tensor* timestep = make_input(timestep_tensor);
|
|
||||||
ggml_tensor* input_ids = make_input(input_ids_tensor);
|
|
||||||
ggml_tensor* input_pos = make_input(input_pos_tensor);
|
|
||||||
|
|
||||||
auto text_model = model.text_model();
|
|
||||||
auto t_embedder1 = model.timestep_embedder();
|
|
||||||
auto x_embedder = model.patch_embedder();
|
|
||||||
auto final_layer2 = model.final_layer();
|
|
||||||
|
|
||||||
std::vector<ggml_tensor*> ref_image_tensors;
|
|
||||||
for (const auto& image : ref_images) {
|
|
||||||
ref_image_tensors.push_back(make_input(image));
|
|
||||||
}
|
|
||||||
|
|
||||||
attention_mask_vec = std::vector<float>(static_cast<size_t>(token_types_tensor.shape()[0] * token_types_tensor.shape()[0]), 0.0f);
|
|
||||||
int64_t total_seq_len = token_types_tensor.shape()[0];
|
|
||||||
for (int64_t query = 0; query < total_seq_len; ++query) {
|
|
||||||
bool is_gen = token_types_tensor.values()[static_cast<size_t>(query)] > 0;
|
|
||||||
for (int64_t key = 0; key < total_seq_len; ++key) {
|
|
||||||
if (!is_gen && key > query) {
|
|
||||||
attention_mask_vec[static_cast<size_t>(query * total_seq_len + key)] = -INFINITY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto attention_mask = ggml_new_tensor_2d(compute_ctx, GGML_TYPE_F32, total_seq_len, total_seq_len);
|
|
||||||
set_backend_tensor_data(attention_mask, attention_mask_vec.data());
|
|
||||||
|
|
||||||
auto runner_ctx = get_context();
|
|
||||||
auto txt = text_model->embed(&runner_ctx, input_ids);
|
|
||||||
std::vector<std::pair<int, ggml_tensor*>> image_embeds;
|
|
||||||
image_embeds.reserve(image_embeds_tensor.size());
|
|
||||||
for (const auto& image_embed : image_embeds_tensor) {
|
|
||||||
image_embeds.emplace_back(image_embed.first, make_input(image_embed.second));
|
|
||||||
}
|
|
||||||
txt = LLM::splice_image_embeds(&runner_ctx, txt, image_embeds);
|
|
||||||
|
|
||||||
auto t_emb = t_embedder1->forward(&runner_ctx, timestep);
|
|
||||||
int64_t txt_seq_len = input_ids->ne[0];
|
|
||||||
if (txt_seq_len > 1) {
|
|
||||||
auto prefix = ggml_ext_slice(compute_ctx, txt, 1, 0, txt_seq_len - 1);
|
|
||||||
txt = ggml_concat(compute_ctx, prefix, ggml_reshape_3d(compute_ctx, t_emb, t_emb->ne[0], 1, 1), 1);
|
|
||||||
} else {
|
|
||||||
txt = ggml_reshape_3d(compute_ctx, t_emb, t_emb->ne[0], 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto vinputs = DiT::pad_and_patchify(&runner_ctx, x, PATCH_SIZE, PATCH_SIZE);
|
|
||||||
int64_t target_tokens = vinputs->ne[1];
|
|
||||||
for (ggml_tensor* ref_image : ref_image_tensors) {
|
|
||||||
auto ref = DiT::pad_and_patchify(&runner_ctx, ref_image, PATCH_SIZE, PATCH_SIZE);
|
|
||||||
vinputs = ggml_concat(compute_ctx, vinputs, ref, 1);
|
|
||||||
}
|
|
||||||
auto vis = x_embedder->forward(&runner_ctx, vinputs);
|
|
||||||
|
|
||||||
auto inputs_embeds = ggml_concat(compute_ctx, txt, vis, 1);
|
|
||||||
auto hidden_states = text_model->forward_embeds(&runner_ctx, inputs_embeds, input_pos, attention_mask, {});
|
|
||||||
auto x_pred_all = final_layer2->forward(&runner_ctx, hidden_states);
|
|
||||||
|
|
||||||
int64_t x_pred_start = txt_seq_len;
|
|
||||||
if (!vinput_mask_tensor.empty()) {
|
|
||||||
int64_t seq_len = static_cast<int64_t>(vinput_mask_tensor.shape()[0]);
|
|
||||||
int64_t first_vinput = 0;
|
|
||||||
while (first_vinput < seq_len && vinput_mask_tensor.values()[static_cast<size_t>(first_vinput)] == 0) {
|
|
||||||
first_vinput++;
|
|
||||||
}
|
|
||||||
x_pred_start = first_vinput;
|
|
||||||
}
|
|
||||||
auto x_pred = ggml_ext_slice(compute_ctx, x_pred_all, 1, x_pred_start, x_pred_start + target_tokens);
|
|
||||||
x_pred = DiT::unpatchify_and_crop(compute_ctx, x_pred, x->ne[1], x->ne[0], PATCH_SIZE, PATCH_SIZE);
|
|
||||||
|
|
||||||
float sigma = 1.0f - timestep_tensor.values()[0];
|
|
||||||
sigma = std::max(1e-6f, sigma);
|
|
||||||
auto out = ggml_scale(compute_ctx, ggml_sub(compute_ctx, x, x_pred), 1.0f / sigma);
|
|
||||||
|
|
||||||
ggml_build_forward_expand(gf, out);
|
|
||||||
return gf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const sd::Tensor<float>& x,
|
|
||||||
const sd::Tensor<float>& timestep,
|
|
||||||
const sd::Tensor<int32_t>& input_ids,
|
|
||||||
const sd::Tensor<int32_t>& input_pos,
|
|
||||||
const sd::Tensor<int32_t>& token_types,
|
|
||||||
const sd::Tensor<int32_t>& vinput_mask,
|
|
||||||
const std::vector<std::pair<int, sd::Tensor<float>>>& image_embeds,
|
|
||||||
const std::vector<sd::Tensor<float>>& ref_images) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
const auto* extra = diffusion_extra_as<HiDreamO1DiffusionExtra>(diffusion_params);
|
|
||||||
GGML_ASSERT(extra != nullptr);
|
|
||||||
GGML_ASSERT(extra->input_ids != nullptr);
|
|
||||||
GGML_ASSERT(extra->input_pos != nullptr);
|
|
||||||
GGML_ASSERT(extra->token_types != nullptr);
|
|
||||||
static const std::vector<sd::Tensor<float>> empty_images;
|
|
||||||
static const std::vector<std::pair<int, sd::Tensor<float>>> empty_image_embeds;
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
*extra->input_ids,
|
|
||||||
*extra->input_pos,
|
|
||||||
*extra->token_types,
|
|
||||||
tensor_or_empty(extra->vinput_mask),
|
|
||||||
extra->image_embeds ? *extra->image_embeds : empty_image_embeds,
|
|
||||||
diffusion_params.ref_latents ? *diffusion_params.ref_latents : empty_images);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HiDreamO1Conditioner : public Conditioner {
|
|
||||||
Qwen2Tokenizer tokenizer;
|
|
||||||
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)) {}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_flash_attention_enabled(bool enabled) override {
|
|
||||||
vision_runner->set_flash_attention_enabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
|
|
||||||
vision_runner->set_weight_adapter(adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDCondition get_learned_condition(int n_threads,
|
|
||||||
const ConditionerParams& conditioner_params) override {
|
|
||||||
SDCondition result;
|
|
||||||
|
|
||||||
int width = conditioner_params.width;
|
|
||||||
int height = conditioner_params.height;
|
|
||||||
int64_t target_image_len = static_cast<int64_t>(width / PATCH_SIZE) * static_cast<int64_t>(height / PATCH_SIZE);
|
|
||||||
|
|
||||||
std::vector<sd::Tensor<float>> ref_images;
|
|
||||||
if (conditioner_params.ref_images != nullptr) {
|
|
||||||
ref_images = *conditioner_params.ref_images;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<int, sd::Tensor<float>>> vlm_images;
|
|
||||||
std::vector<std::array<int32_t, 3>> image_grids;
|
|
||||||
std::vector<int32_t> skip_vision_start;
|
|
||||||
|
|
||||||
std::string prompt = "<|im_start|>user\n";
|
|
||||||
|
|
||||||
if (ref_images.empty()) {
|
|
||||||
prompt += conditioner_params.text;
|
|
||||||
prompt += "<|im_end|>\n<|im_start|>assistant\n<|boi_token|><|tms_token|>";
|
|
||||||
auto input_ids = tokenizer.encode(prompt, nullptr);
|
|
||||||
|
|
||||||
std::vector<int32_t> input_ids_pad = input_ids;
|
|
||||||
input_ids_pad.push_back(VISION_START_TOKEN_ID);
|
|
||||||
input_ids_pad.insert(input_ids_pad.end(), target_image_len - 1, IMAGE_TOKEN_ID);
|
|
||||||
|
|
||||||
image_grids.push_back({1, static_cast<int32_t>(height / PATCH_SIZE), static_cast<int32_t>(width / PATCH_SIZE)});
|
|
||||||
skip_vision_start.push_back(1);
|
|
||||||
|
|
||||||
std::vector<int32_t> token_types(input_ids_pad.size(), 0);
|
|
||||||
int txt_seq_len = static_cast<int>(input_ids.size());
|
|
||||||
int bgn = txt_seq_len - TIMESTEP_TOKEN_NUM;
|
|
||||||
for (int i = bgn; i < static_cast<int>(token_types.size()); ++i) {
|
|
||||||
token_types[i] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto position_ids = build_position_ids(input_ids_pad, image_grids, skip_vision_start);
|
|
||||||
|
|
||||||
std::vector<int64_t> input_shape{static_cast<int64_t>(input_ids.size())};
|
|
||||||
std::vector<int64_t> position_shape{static_cast<int64_t>(input_ids_pad.size() * 4)};
|
|
||||||
std::vector<int64_t> token_type_shape{static_cast<int64_t>(token_types.size())};
|
|
||||||
std::vector<int32_t> vinput_mask(token_types.size(), 0);
|
|
||||||
for (int64_t i = txt_seq_len; i < static_cast<int64_t>(vinput_mask.size()); ++i) {
|
|
||||||
vinput_mask[static_cast<size_t>(i)] = 1;
|
|
||||||
}
|
|
||||||
std::vector<int64_t> vinput_mask_shape{static_cast<int64_t>(vinput_mask.size())};
|
|
||||||
|
|
||||||
result.c_input_ids = sd::Tensor<int32_t>(input_shape, std::move(input_ids));
|
|
||||||
result.c_position_ids = sd::Tensor<int32_t>(position_shape, position_ids);
|
|
||||||
result.c_token_types = sd::Tensor<int32_t>(token_type_shape, std::move(token_types));
|
|
||||||
result.c_vinput_mask = sd::Tensor<int32_t>(vinput_mask_shape, std::move(vinput_mask));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int K = static_cast<int>(ref_images.size());
|
|
||||||
int max_size;
|
|
||||||
if (K == 1) {
|
|
||||||
max_size = std::max(height, width);
|
|
||||||
} else if (K == 2) {
|
|
||||||
max_size = std::max(height, width) * 48 / 64;
|
|
||||||
} else if (K <= 4) {
|
|
||||||
max_size = std::max(height, width) / 2;
|
|
||||||
} else if (K <= 8) {
|
|
||||||
max_size = std::max(height, width) * 24 / 64;
|
|
||||||
} else {
|
|
||||||
max_size = std::max(height, width) / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cond_img_size;
|
|
||||||
if (K <= 4) {
|
|
||||||
cond_img_size = 384;
|
|
||||||
} else if (K <= 8) {
|
|
||||||
cond_img_size = 384 * 48 / 64;
|
|
||||||
} else {
|
|
||||||
cond_img_size = 384 / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& ref_image : ref_images) {
|
|
||||||
auto resized_ref = resize_to_area(ref_image, max_size);
|
|
||||||
resized_ref = sd::ops::clamp(resized_ref, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
// VLM image: Qwen3-VL expects mean=[0.5]/std=[0.5] (i.e. range [-1,1]),
|
|
||||||
// not CLIP normalization. Resize the already-resized ref directly to
|
|
||||||
// (cond_w, cond_h) to match the Python pipeline's pil_r.resize().
|
|
||||||
auto dims = calculate_dimensions(cond_img_size,
|
|
||||||
static_cast<double>(resized_ref.shape()[0]) / static_cast<double>(resized_ref.shape()[1]));
|
|
||||||
sd::Tensor<float> vlm_image = sd::ops::interpolate(
|
|
||||||
resized_ref,
|
|
||||||
{dims.first, dims.second, resized_ref.shape()[2], resized_ref.shape()[3]});
|
|
||||||
vlm_image = vlm_image * 2.0f - 1.0f;
|
|
||||||
int64_t image_tokens = static_cast<int64_t>(dims.first / PATCH_SIZE) * static_cast<int64_t>(dims.second / PATCH_SIZE);
|
|
||||||
|
|
||||||
auto patch_img = resized_ref * 2.0f - 1.0f;
|
|
||||||
result.c_ref_images.push_back(std::move(patch_img));
|
|
||||||
int64_t prompt_start = static_cast<int64_t>(tokenizer.encode(prompt + "<|vision_start|>", nullptr).size());
|
|
||||||
prompt += "<|vision_start|>";
|
|
||||||
prompt += repeat_special_token("<|image_pad|>", image_tokens);
|
|
||||||
prompt += "<|vision_end|>";
|
|
||||||
vlm_images.emplace_back(static_cast<int>(prompt_start), std::move(vlm_image));
|
|
||||||
image_grids.push_back({1, dims.second / PATCH_SIZE, dims.first / PATCH_SIZE});
|
|
||||||
skip_vision_start.push_back(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt += conditioner_params.text;
|
|
||||||
prompt += "<|im_end|>\n<|im_start|>assistant\n<|boi_token|><|tms_token|>";
|
|
||||||
auto input_ids = tokenizer.encode(prompt, nullptr);
|
|
||||||
|
|
||||||
std::vector<int32_t> input_ids_pad = input_ids;
|
|
||||||
input_ids_pad.push_back(VISION_START_TOKEN_ID);
|
|
||||||
input_ids_pad.insert(input_ids_pad.end(), target_image_len - 1, IMAGE_TOKEN_ID);
|
|
||||||
image_grids.push_back({1, static_cast<int32_t>(height / PATCH_SIZE), static_cast<int32_t>(width / PATCH_SIZE)});
|
|
||||||
skip_vision_start.push_back(1);
|
|
||||||
|
|
||||||
for (const auto& ref_image : result.c_ref_images) {
|
|
||||||
int64_t ref_len = static_cast<int64_t>(ref_image.shape()[0] / PATCH_SIZE) * static_cast<int64_t>(ref_image.shape()[1] / PATCH_SIZE);
|
|
||||||
input_ids_pad.push_back(VISION_START_TOKEN_ID);
|
|
||||||
input_ids_pad.insert(input_ids_pad.end(), ref_len - 1, IMAGE_TOKEN_ID);
|
|
||||||
image_grids.push_back({1, static_cast<int32_t>(ref_image.shape()[1] / PATCH_SIZE), static_cast<int32_t>(ref_image.shape()[0] / PATCH_SIZE)});
|
|
||||||
skip_vision_start.push_back(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int32_t> token_types(input_ids_pad.size(), 0);
|
|
||||||
int txt_seq_len = static_cast<int>(input_ids.size());
|
|
||||||
int bgn = txt_seq_len - TIMESTEP_TOKEN_NUM;
|
|
||||||
for (int i = bgn; i < static_cast<int>(token_types.size()); ++i) {
|
|
||||||
token_types[i] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int64_t> input_shape{static_cast<int64_t>(input_ids.size())};
|
|
||||||
std::vector<int64_t> position_shape{static_cast<int64_t>(input_ids_pad.size() * 4)};
|
|
||||||
std::vector<int64_t> token_type_shape{static_cast<int64_t>(token_types.size())};
|
|
||||||
std::vector<int32_t> vinput_mask(token_types.size(), 0);
|
|
||||||
for (int i = txt_seq_len; i < static_cast<int>(vinput_mask.size()); ++i) {
|
|
||||||
vinput_mask[static_cast<size_t>(i)] = 1;
|
|
||||||
}
|
|
||||||
std::vector<int64_t> vinput_mask_shape{static_cast<int64_t>(vinput_mask.size())};
|
|
||||||
|
|
||||||
result.c_input_ids = sd::Tensor<int32_t>(input_shape, std::move(input_ids));
|
|
||||||
result.c_position_ids = sd::Tensor<int32_t>(position_shape, build_position_ids(input_ids_pad, image_grids, skip_vision_start));
|
|
||||||
result.c_token_types = sd::Tensor<int32_t>(token_type_shape, std::move(token_types));
|
|
||||||
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);
|
|
||||||
if (image_embed.empty()) {
|
|
||||||
LOG_ERROR("hidream_o1 conditioner: encode VLM image failed");
|
|
||||||
return SDCondition();
|
|
||||||
}
|
|
||||||
result.c_image_embeds.emplace_back(vlm_image.first, std::move(image_embed));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace HiDreamO1
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_HIDREAM_O1_HPP__
|
|
||||||
@ -1,531 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_IDEOGRAM4_HPP__
|
|
||||||
#define __SD_MODEL_DIFFUSION_IDEOGRAM4_HPP__
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
|
||||||
#include "core/ggml_graph_cut.h"
|
|
||||||
#include "model/common/rope.hpp"
|
|
||||||
#include "model/diffusion/model.hpp"
|
|
||||||
|
|
||||||
namespace Ideogram4 {
|
|
||||||
constexpr int IDEOGRAM4_GRAPH_SIZE = 65536;
|
|
||||||
constexpr int OUTPUT_IMAGE_INDICATOR = 2;
|
|
||||||
constexpr int IMAGE_POSITION_OFFSET = 65536;
|
|
||||||
constexpr int DEFAULT_MROPE_SECTION_T = 24;
|
|
||||||
constexpr int DEFAULT_MROPE_SECTION_H = 20;
|
|
||||||
constexpr int DEFAULT_MROPE_SECTION_W = 20;
|
|
||||||
constexpr int TIMESTEP_MAX_PERIOD = 10000;
|
|
||||||
constexpr int LLM_HIDDEN_STATE_LAYERS = 13;
|
|
||||||
|
|
||||||
struct Ideogram4Config {
|
|
||||||
int64_t emb_dim = 4608;
|
|
||||||
int64_t num_layers = 34;
|
|
||||||
int64_t num_heads = 18;
|
|
||||||
int64_t intermediate_size = 12288;
|
|
||||||
int64_t adanln_dim = 512;
|
|
||||||
int64_t in_channels = 128;
|
|
||||||
int64_t llm_features_dim = 53248;
|
|
||||||
int64_t rope_theta = 5000000;
|
|
||||||
float norm_eps = 1e-5f;
|
|
||||||
int patch_size = 2;
|
|
||||||
int ae_channels = 32;
|
|
||||||
std::vector<int> mrope_section = {DEFAULT_MROPE_SECTION_T,
|
|
||||||
DEFAULT_MROPE_SECTION_H,
|
|
||||||
DEFAULT_MROPE_SECTION_W};
|
|
||||||
|
|
||||||
static Ideogram4Config detect_from_weights(const String2TensorStorage& tensor_storage_map,
|
|
||||||
const std::string& prefix) {
|
|
||||||
Ideogram4Config config;
|
|
||||||
int64_t detected_layers = 0;
|
|
||||||
std::string layer_prefix = prefix.empty() ? "layers." : prefix + ".layers.";
|
|
||||||
for (const auto& [name, _] : tensor_storage_map) {
|
|
||||||
if (name.find(layer_prefix) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string tail = name.substr(layer_prefix.size());
|
|
||||||
size_t dot = tail.find('.');
|
|
||||||
if (dot == std::string::npos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int layer_idx = std::atoi(tail.substr(0, dot).c_str());
|
|
||||||
detected_layers = std::max<int64_t>(detected_layers, layer_idx + 1);
|
|
||||||
}
|
|
||||||
if (detected_layers > 0) {
|
|
||||||
config.num_layers = detected_layers;
|
|
||||||
LOG_DEBUG("ideogram4: num_layers = %" PRId64 ", emb_dim = %" PRId64 ", num_heads = %" PRId64 ", intermediate_size = %" PRId64,
|
|
||||||
config.num_layers,
|
|
||||||
config.emb_dim,
|
|
||||||
config.num_heads,
|
|
||||||
config.intermediate_size);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* timestep_embedding_sin_cos(ggml_context* ctx,
|
|
||||||
ggml_tensor* timesteps,
|
|
||||||
int dim) {
|
|
||||||
GGML_ASSERT(dim % 2 == 0);
|
|
||||||
auto embedding = ggml_ext_timestep_embedding(ctx, timesteps, dim, TIMESTEP_MAX_PERIOD, 10.f);
|
|
||||||
auto chunks = ggml_ext_chunk(ctx, embedding, 2, 0);
|
|
||||||
return ggml_concat(ctx, chunks[1], chunks[0], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* to_token_modulation(ggml_context* ctx, ggml_tensor* x) {
|
|
||||||
// [N, C] -> [N, 1, C] in PyTorch layout.
|
|
||||||
if (ggml_n_dims(x) < 3 || x->ne[1] != 1) {
|
|
||||||
x = ggml_reshape_3d(ctx, x, x->ne[0], 1, x->ne[1]);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* interleave_hidden_state_layers(ggml_context* ctx, ggml_tensor* x) {
|
|
||||||
// Match upstream stack(...).permute(1, 2, 3, 0).reshape(...):
|
|
||||||
// [layers * hidden, tokens, batch] -> [hidden * layers, tokens, batch].
|
|
||||||
GGML_ASSERT(x->ne[0] % LLM_HIDDEN_STATE_LAYERS == 0);
|
|
||||||
const int64_t hidden_size = x->ne[0] / LLM_HIDDEN_STATE_LAYERS;
|
|
||||||
const int64_t token_count = x->ne[1];
|
|
||||||
const int64_t batch_count = x->ne[2];
|
|
||||||
|
|
||||||
x = ggml_reshape_4d(ctx, x, hidden_size, LLM_HIDDEN_STATE_LAYERS, token_count, batch_count);
|
|
||||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 1, 0, 2, 3));
|
|
||||||
return ggml_reshape_3d(ctx, x, hidden_size * LLM_HIDDEN_STATE_LAYERS, token_count, batch_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* modulate(ggml_context* ctx, ggml_tensor* x, ggml_tensor* scale) {
|
|
||||||
scale = to_token_modulation(ctx, scale);
|
|
||||||
return ggml_add(ctx, x, ggml_mul(ctx, x, scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* patchify(ggml_context* ctx, ggml_tensor* x, const Ideogram4Config& config) {
|
|
||||||
// x: [N, 128, H, W] with channel order [ae, ph, pw].
|
|
||||||
// return: [N, H*W, 128] with token channel order [ph, pw, ae].
|
|
||||||
const int64_t W = x->ne[0];
|
|
||||||
const int64_t H = x->ne[1];
|
|
||||||
const int64_t C = x->ne[2];
|
|
||||||
const int64_t N = x->ne[3];
|
|
||||||
|
|
||||||
GGML_ASSERT(N == 1);
|
|
||||||
GGML_ASSERT(C == config.ae_channels * config.patch_size * config.patch_size);
|
|
||||||
|
|
||||||
x = ggml_cont(ctx, x);
|
|
||||||
x = ggml_reshape_4d(ctx, x, W * H, config.patch_size, config.patch_size, config.ae_channels);
|
|
||||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 3, 1, 2, 0));
|
|
||||||
x = ggml_reshape_3d(ctx, x, C, W * H, N);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ ggml_tensor* unpatchify(ggml_context* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
int64_t H,
|
|
||||||
int64_t W,
|
|
||||||
const Ideogram4Config& config) {
|
|
||||||
const int64_t C = x->ne[0];
|
|
||||||
const int64_t N = x->ne[2];
|
|
||||||
|
|
||||||
GGML_ASSERT(N == 1);
|
|
||||||
GGML_ASSERT(C == config.ae_channels * config.patch_size * config.patch_size);
|
|
||||||
GGML_ASSERT(x->ne[1] == H * W);
|
|
||||||
|
|
||||||
x = ggml_reshape_4d(ctx, x, config.ae_channels, config.patch_size, config.patch_size, H * W);
|
|
||||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 3, 1, 2, 0));
|
|
||||||
x = ggml_reshape_4d(ctx, x, W, H, C, N);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ std::shared_ptr<Linear> make_linear(int64_t in_features,
|
|
||||||
int64_t out_features,
|
|
||||||
bool bias = true) {
|
|
||||||
return std::make_shared<Linear>(in_features, out_features, bias, false, false, 1.f, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
__STATIC_INLINE__ std::vector<float> gen_ideogram4_pe(int grid_h,
|
|
||||||
int grid_w,
|
|
||||||
int bs,
|
|
||||||
int context_len,
|
|
||||||
int head_dim,
|
|
||||||
int rope_theta,
|
|
||||||
const std::vector<int>& mrope_section) {
|
|
||||||
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));
|
|
||||||
|
|
||||||
for (int i = 0; i < context_len; ++i) {
|
|
||||||
ids[i] = {static_cast<float>(i), static_cast<float>(i), static_cast<float>(i)};
|
|
||||||
}
|
|
||||||
|
|
||||||
int cursor = context_len;
|
|
||||||
for (int y = 0; y < grid_h; ++y) {
|
|
||||||
for (int x = 0; x < grid_w; ++x) {
|
|
||||||
ids[cursor++] = {static_cast<float>(IMAGE_POSITION_OFFSET),
|
|
||||||
static_cast<float>(IMAGE_POSITION_OFFSET + y),
|
|
||||||
static_cast<float>(IMAGE_POSITION_OFFSET + x)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Rope::embed_interleaved_mrope(ids, bs, static_cast<float>(rope_theta), head_dim, mrope_section);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Ideogram4Attention : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
int64_t hidden_size;
|
|
||||||
int64_t num_heads;
|
|
||||||
int64_t head_dim;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Ideogram4Attention(int64_t hidden_size, int64_t num_heads, float eps)
|
|
||||||
: hidden_size(hidden_size), num_heads(num_heads), head_dim(hidden_size / num_heads) {
|
|
||||||
GGML_ASSERT(hidden_size % num_heads == 0);
|
|
||||||
blocks["qkv"] = make_linear(hidden_size, hidden_size * 3, false);
|
|
||||||
blocks["norm_q"] = std::make_shared<RMSNorm>(head_dim, eps);
|
|
||||||
blocks["norm_k"] = std::make_shared<RMSNorm>(head_dim, eps);
|
|
||||||
blocks["o"] = make_linear(hidden_size, hidden_size, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* pe,
|
|
||||||
ggml_tensor* mask = nullptr) {
|
|
||||||
int64_t n_token = x->ne[1];
|
|
||||||
int64_t N = x->ne[2];
|
|
||||||
|
|
||||||
auto qkv_proj = std::dynamic_pointer_cast<Linear>(blocks["qkv"]);
|
|
||||||
auto norm_q = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_q"]);
|
|
||||||
auto norm_k = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_k"]);
|
|
||||||
auto out_proj = std::dynamic_pointer_cast<Linear>(blocks["o"]);
|
|
||||||
|
|
||||||
auto qkv = qkv_proj->forward(ctx, x);
|
|
||||||
auto qkv_vec = split_qkv(ctx->ggml_ctx, qkv);
|
|
||||||
auto q = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[0], head_dim, num_heads, n_token, N);
|
|
||||||
auto k = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[1], head_dim, num_heads, n_token, N);
|
|
||||||
auto v = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[2], head_dim, num_heads, n_token, N);
|
|
||||||
|
|
||||||
q = norm_q->forward(ctx, q);
|
|
||||||
k = norm_k->forward(ctx, k);
|
|
||||||
|
|
||||||
x = Rope::attention(ctx, q, k, v, pe, mask, 1.f / 128.f, false);
|
|
||||||
x = out_proj->forward(ctx, x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4MLP : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
Ideogram4MLP(int64_t dim, int64_t hidden_dim) {
|
|
||||||
blocks["w1"] = make_linear(dim, hidden_dim, false);
|
|
||||||
blocks["w2"] = make_linear(hidden_dim, dim, false);
|
|
||||||
blocks["w3"] = make_linear(dim, hidden_dim, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto w1 = std::dynamic_pointer_cast<Linear>(blocks["w1"]);
|
|
||||||
auto w2 = std::dynamic_pointer_cast<Linear>(blocks["w2"]);
|
|
||||||
auto w3 = std::dynamic_pointer_cast<Linear>(blocks["w3"]);
|
|
||||||
|
|
||||||
auto x1 = ggml_silu(ctx->ggml_ctx, w1->forward(ctx, x));
|
|
||||||
auto x3 = w3->forward(ctx, x);
|
|
||||||
x = ggml_mul(ctx->ggml_ctx, x1, x3);
|
|
||||||
x = w2->forward(ctx, x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4TransformerBlock : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
Ideogram4TransformerBlock(const Ideogram4Config& config) {
|
|
||||||
blocks["attention"] = std::make_shared<Ideogram4Attention>(config.emb_dim, config.num_heads, config.norm_eps);
|
|
||||||
blocks["feed_forward"] = std::make_shared<Ideogram4MLP>(config.emb_dim, config.intermediate_size);
|
|
||||||
blocks["attention_norm1"] = std::make_shared<RMSNorm>(config.emb_dim, config.norm_eps);
|
|
||||||
blocks["ffn_norm1"] = std::make_shared<RMSNorm>(config.emb_dim, config.norm_eps);
|
|
||||||
blocks["attention_norm2"] = std::make_shared<RMSNorm>(config.emb_dim, config.norm_eps);
|
|
||||||
blocks["ffn_norm2"] = std::make_shared<RMSNorm>(config.emb_dim, config.norm_eps);
|
|
||||||
blocks["adaln_modulation"] = make_linear(config.adanln_dim, 4 * config.emb_dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* pe,
|
|
||||||
ggml_tensor* adaln_input,
|
|
||||||
ggml_tensor* mask = nullptr) {
|
|
||||||
auto attention = std::dynamic_pointer_cast<Ideogram4Attention>(blocks["attention"]);
|
|
||||||
auto feed_forward = std::dynamic_pointer_cast<Ideogram4MLP>(blocks["feed_forward"]);
|
|
||||||
auto attention_norm1 = std::dynamic_pointer_cast<RMSNorm>(blocks["attention_norm1"]);
|
|
||||||
auto ffn_norm1 = std::dynamic_pointer_cast<RMSNorm>(blocks["ffn_norm1"]);
|
|
||||||
auto attention_norm2 = std::dynamic_pointer_cast<RMSNorm>(blocks["attention_norm2"]);
|
|
||||||
auto ffn_norm2 = std::dynamic_pointer_cast<RMSNorm>(blocks["ffn_norm2"]);
|
|
||||||
auto adaln_modulation = std::dynamic_pointer_cast<Linear>(blocks["adaln_modulation"]);
|
|
||||||
|
|
||||||
auto mod = adaln_modulation->forward(ctx, adaln_input);
|
|
||||||
auto mods = ggml_ext_chunk(ctx->ggml_ctx, mod, 4, 0);
|
|
||||||
auto scale_msa = mods[0];
|
|
||||||
auto gate_msa = to_token_modulation(ctx->ggml_ctx, ggml_tanh(ctx->ggml_ctx, mods[1]));
|
|
||||||
auto scale_mlp = mods[2];
|
|
||||||
auto gate_mlp = to_token_modulation(ctx->ggml_ctx, ggml_tanh(ctx->ggml_ctx, mods[3]));
|
|
||||||
|
|
||||||
auto attn_out = attention_norm1->forward(ctx, x);
|
|
||||||
attn_out = modulate(ctx->ggml_ctx, attn_out, scale_msa);
|
|
||||||
attn_out = attention->forward(ctx, attn_out, pe, mask);
|
|
||||||
attn_out = attention_norm2->forward(ctx, attn_out);
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, attn_out, gate_msa));
|
|
||||||
|
|
||||||
auto ffn_out = ffn_norm1->forward(ctx, x);
|
|
||||||
ffn_out = modulate(ctx->ggml_ctx, ffn_out, scale_mlp);
|
|
||||||
ffn_out = feed_forward->forward(ctx, ffn_out);
|
|
||||||
ffn_out = ffn_norm2->forward(ctx, ffn_out);
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, ffn_out, gate_mlp));
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4EmbedScalar : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
int64_t dim;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Ideogram4EmbedScalar(int64_t dim)
|
|
||||||
: dim(dim) {
|
|
||||||
blocks["mlp_in"] = make_linear(dim, dim, true);
|
|
||||||
blocks["mlp_out"] = make_linear(dim, dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto mlp_in = std::dynamic_pointer_cast<Linear>(blocks["mlp_in"]);
|
|
||||||
auto mlp_out = std::dynamic_pointer_cast<Linear>(blocks["mlp_out"]);
|
|
||||||
|
|
||||||
x = timestep_embedding_sin_cos(ctx->ggml_ctx, x, static_cast<int>(dim));
|
|
||||||
x = ggml_silu(ctx->ggml_ctx, mlp_in->forward(ctx, x));
|
|
||||||
x = mlp_out->forward(ctx, x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4FinalLayer : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
Ideogram4FinalLayer(const Ideogram4Config& config) {
|
|
||||||
blocks["norm_final"] = std::make_shared<LayerNorm>(config.emb_dim, 1e-6f, false);
|
|
||||||
blocks["linear"] = make_linear(config.emb_dim, config.in_channels, true);
|
|
||||||
blocks["adaln_modulation"] = make_linear(config.adanln_dim, config.emb_dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* c) {
|
|
||||||
auto norm_final = std::dynamic_pointer_cast<LayerNorm>(blocks["norm_final"]);
|
|
||||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
|
||||||
auto adaln_modulation = std::dynamic_pointer_cast<Linear>(blocks["adaln_modulation"]);
|
|
||||||
|
|
||||||
auto scale = adaln_modulation->forward(ctx, ggml_silu(ctx->ggml_ctx, c));
|
|
||||||
x = norm_final->forward(ctx, x);
|
|
||||||
x = modulate(ctx->ggml_ctx, x, scale);
|
|
||||||
x = linear->forward(ctx, x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4Transformer : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
Ideogram4Config config;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Ideogram4Transformer() = default;
|
|
||||||
explicit Ideogram4Transformer(Ideogram4Config config)
|
|
||||||
: config(std::move(config)) {
|
|
||||||
blocks["input_proj"] = make_linear(this->config.in_channels, this->config.emb_dim, true);
|
|
||||||
blocks["llm_cond_norm"] = std::make_shared<RMSNorm>(this->config.llm_features_dim, 1e-6f);
|
|
||||||
blocks["llm_cond_proj"] = make_linear(this->config.llm_features_dim, this->config.emb_dim, true);
|
|
||||||
blocks["t_embedding"] = std::make_shared<Ideogram4EmbedScalar>(this->config.emb_dim);
|
|
||||||
blocks["adaln_proj"] = make_linear(this->config.emb_dim, this->config.adanln_dim, true);
|
|
||||||
blocks["embed_image_indicator"] = std::make_shared<Embedding>(2, this->config.emb_dim);
|
|
||||||
|
|
||||||
for (int i = 0; i < this->config.num_layers; ++i) {
|
|
||||||
blocks["layers." + std::to_string(i)] = std::make_shared<Ideogram4TransformerBlock>(this->config);
|
|
||||||
}
|
|
||||||
blocks["final_layer"] = std::make_shared<Ideogram4FinalLayer>(this->config);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* timestep,
|
|
||||||
ggml_tensor* context,
|
|
||||||
ggml_tensor* pe,
|
|
||||||
ggml_tensor* image_indicator_ids) {
|
|
||||||
int64_t W = x->ne[0];
|
|
||||||
int64_t H = x->ne[1];
|
|
||||||
int64_t N = x->ne[3];
|
|
||||||
GGML_ASSERT(N == 1);
|
|
||||||
|
|
||||||
auto input_proj = std::dynamic_pointer_cast<Linear>(blocks["input_proj"]);
|
|
||||||
auto llm_cond_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["llm_cond_norm"]);
|
|
||||||
auto llm_cond_proj = std::dynamic_pointer_cast<Linear>(blocks["llm_cond_proj"]);
|
|
||||||
auto t_embedding = std::dynamic_pointer_cast<Ideogram4EmbedScalar>(blocks["t_embedding"]);
|
|
||||||
auto adaln_proj = std::dynamic_pointer_cast<Linear>(blocks["adaln_proj"]);
|
|
||||||
auto embed_image_indicator = std::dynamic_pointer_cast<Embedding>(blocks["embed_image_indicator"]);
|
|
||||||
auto final_layer = std::dynamic_pointer_cast<Ideogram4FinalLayer>(blocks["final_layer"]);
|
|
||||||
|
|
||||||
auto img = patchify(ctx->ggml_ctx, x, config);
|
|
||||||
img = input_proj->forward(ctx, img);
|
|
||||||
|
|
||||||
ggml_tensor* h = img;
|
|
||||||
int64_t context_len = 0;
|
|
||||||
if (context != nullptr) {
|
|
||||||
if (ggml_n_dims(context) < 3) {
|
|
||||||
context = ggml_reshape_3d(ctx->ggml_ctx, context, context->ne[0], context->ne[1], 1);
|
|
||||||
}
|
|
||||||
context = interleave_hidden_state_layers(ctx->ggml_ctx, context);
|
|
||||||
context_len = context->ne[1];
|
|
||||||
auto txt = llm_cond_norm->forward(ctx, context);
|
|
||||||
txt = llm_cond_proj->forward(ctx, txt);
|
|
||||||
h = ggml_concat(ctx->ggml_ctx, txt, img, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto indicator_embedding = embed_image_indicator->forward(ctx, image_indicator_ids);
|
|
||||||
h = ggml_add(ctx->ggml_ctx, h, indicator_embedding);
|
|
||||||
|
|
||||||
auto t_cond = t_embedding->forward(ctx, timestep);
|
|
||||||
auto adaln_input = ggml_silu(ctx->ggml_ctx, adaln_proj->forward(ctx, t_cond));
|
|
||||||
|
|
||||||
for (int i = 0; i < config.num_layers; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<Ideogram4TransformerBlock>(blocks["layers." + std::to_string(i)]);
|
|
||||||
h = block->forward(ctx, h, pe, adaln_input, nullptr);
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(h, "ideogram4.layers." + std::to_string(i), "hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
h = final_layer->forward(ctx, h, adaln_input);
|
|
||||||
if (context_len > 0) {
|
|
||||||
h = ggml_ext_slice(ctx->ggml_ctx, h, 1, context_len, h->ne[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
h = unpatchify(ctx->ggml_ctx, h, H, W, config);
|
|
||||||
h = ggml_ext_scale(ctx->ggml_ctx, h, -1.f);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Ideogram4Runner : public DiffusionModelRunner {
|
|
||||||
protected:
|
|
||||||
bool should_use_uncond_model(const DiffusionParams& diffusion_params) const {
|
|
||||||
return has_uncond_model &&
|
|
||||||
diffusion_params.context == nullptr &&
|
|
||||||
diffusion_params.y != nullptr &&
|
|
||||||
!diffusion_params.y->empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Ideogram4Config config;
|
|
||||||
Ideogram4Transformer model;
|
|
||||||
Ideogram4Transformer uncond_model;
|
|
||||||
bool has_uncond_model = false;
|
|
||||||
std::string uncond_prefix;
|
|
||||||
std::vector<float> pe_vec;
|
|
||||||
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),
|
|
||||||
config(Ideogram4Config::detect_from_weights(tensor_storage_map, prefix)),
|
|
||||||
uncond_prefix(prefix + ".uncond") {
|
|
||||||
model = Ideogram4Transformer(config);
|
|
||||||
model.init(params_ctx, tensor_storage_map, prefix);
|
|
||||||
for (const auto& pair : tensor_storage_map) {
|
|
||||||
const std::string& name = pair.first;
|
|
||||||
if (starts_with(name, uncond_prefix)) {
|
|
||||||
has_uncond_model = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (has_uncond_model) {
|
|
||||||
LOG_DEBUG("using uncond model");
|
|
||||||
uncond_model = Ideogram4Transformer(config);
|
|
||||||
uncond_model.init(params_ctx, tensor_storage_map, uncond_prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_desc() override {
|
|
||||||
return "ideogram4";
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
|
|
||||||
model.get_param_tensors(tensors, prefix);
|
|
||||||
if (has_uncond_model) {
|
|
||||||
uncond_model.get_param_tensors(tensors, this->uncond_prefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
|
||||||
const sd::Tensor<float>& timesteps_tensor,
|
|
||||||
const sd::Tensor<float>& context_tensor,
|
|
||||||
bool use_uncond_model = false) {
|
|
||||||
ggml_cgraph* gf = new_graph_custom(IDEOGRAM4_GRAPH_SIZE);
|
|
||||||
ggml_tensor* x = make_input(x_tensor);
|
|
||||||
ggml_tensor* timesteps = make_input(timesteps_tensor);
|
|
||||||
GGML_ASSERT(x->ne[3] == 1);
|
|
||||||
Ideogram4Transformer& active_model = use_uncond_model ? uncond_model : model;
|
|
||||||
|
|
||||||
ggml_tensor* context = nullptr;
|
|
||||||
int64_t context_len = 0;
|
|
||||||
if (!context_tensor.empty()) {
|
|
||||||
context = make_input(context_tensor);
|
|
||||||
context_len = context->ne[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t grid_w = x->ne[0];
|
|
||||||
int64_t grid_h = x->ne[1];
|
|
||||||
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);
|
|
||||||
set_backend_tensor_data(pe, pe_vec.data());
|
|
||||||
|
|
||||||
image_indicator_vec.assign(static_cast<size_t>(pos_len), 1);
|
|
||||||
for (int64_t i = 0; i < context_len; ++i) {
|
|
||||||
image_indicator_vec[static_cast<size_t>(i)] = 0;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const sd::Tensor<float>& x,
|
|
||||||
const sd::Tensor<float>& timesteps,
|
|
||||||
const sd::Tensor<float>& context,
|
|
||||||
bool use_uncond_model = false) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
bool use_uncond_model = should_use_uncond_model(diffusion_params);
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context),
|
|
||||||
use_uncond_model);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Ideogram4
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_IDEOGRAM4_HPP__
|
|
||||||
@ -1,426 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_LENS_HPP__
|
|
||||||
#define __SD_MODEL_DIFFUSION_LENS_HPP__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "model/common/block.hpp"
|
|
||||||
#include "model/common/rope.hpp"
|
|
||||||
#include "model/diffusion/flux.hpp"
|
|
||||||
#include "model/diffusion/model.hpp"
|
|
||||||
#include "model/diffusion/qwen_image.hpp"
|
|
||||||
|
|
||||||
namespace Lens {
|
|
||||||
constexpr int LENS_GRAPH_SIZE = 40960;
|
|
||||||
|
|
||||||
struct LensConfig {
|
|
||||||
int patch_size = 2;
|
|
||||||
int64_t in_channels = 128;
|
|
||||||
int64_t out_channels = 32;
|
|
||||||
int num_layers = 48;
|
|
||||||
int64_t attention_head_dim = 64;
|
|
||||||
int64_t num_attention_heads = 24;
|
|
||||||
int64_t joint_attention_dim = 2880;
|
|
||||||
int selected_layer_count = 4;
|
|
||||||
int theta = 10000;
|
|
||||||
std::vector<int> axes_dim = {8, 28, 28};
|
|
||||||
int axes_dim_sum = 64;
|
|
||||||
|
|
||||||
static LensConfig detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
|
||||||
LensConfig config;
|
|
||||||
config.num_layers = 0;
|
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
|
||||||
if (!starts_with(name, prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ends_with(name, "img_in.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
config.in_channels = tensor_storage.ne[0];
|
|
||||||
int64_t inner_dim = tensor_storage.ne[1];
|
|
||||||
if (config.attention_head_dim > 0) {
|
|
||||||
config.num_attention_heads = inner_dim / config.attention_head_dim;
|
|
||||||
}
|
|
||||||
} else if (ends_with(name, "txt_in.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
config.selected_layer_count = static_cast<int>(tensor_storage.ne[0] / config.joint_attention_dim);
|
|
||||||
} else if (ends_with(name, "proj_out.weight") && tensor_storage.n_dims == 2) {
|
|
||||||
int64_t patch_area = config.patch_size * config.patch_size;
|
|
||||||
config.out_channels = tensor_storage.ne[1] / patch_area;
|
|
||||||
} else if (ends_with(name, "transformer_blocks.0.attn.norm_q.weight") && tensor_storage.n_dims == 1) {
|
|
||||||
config.attention_head_dim = tensor_storage.ne[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = name.find("transformer_blocks.");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
auto items = split_string(name.substr(pos), '.');
|
|
||||||
if (items.size() > 1) {
|
|
||||||
int block_index = atoi(items[1].c_str());
|
|
||||||
if (block_index + 1 > config.num_layers) {
|
|
||||||
config.num_layers = block_index + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (config.num_layers == 0) {
|
|
||||||
config.num_layers = 48;
|
|
||||||
}
|
|
||||||
config.axes_dim_sum = 0;
|
|
||||||
for (int axis_dim : config.axes_dim) {
|
|
||||||
config.axes_dim_sum += axis_dim;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("lens: num_layers = %d, selected_layer_count = %d, hidden_size = %" PRId64 ", num_attention_heads = %" PRId64 ", attention_head_dim = %" PRId64 ", in_channels = %" PRId64 ", out_channels = %" PRId64,
|
|
||||||
config.num_layers,
|
|
||||||
config.selected_layer_count,
|
|
||||||
config.num_attention_heads * config.attention_head_dim,
|
|
||||||
config.num_attention_heads,
|
|
||||||
config.attention_head_dim,
|
|
||||||
config.in_channels,
|
|
||||||
config.out_channels);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensTimestepProjEmbeddings : public GGMLBlock {
|
|
||||||
LensTimestepProjEmbeddings(int64_t embedding_dim) {
|
|
||||||
blocks["timestep_embedder"] = std::make_shared<Qwen::TimestepEmbedding>(256, embedding_dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* timesteps) {
|
|
||||||
auto timestep_embedder = std::dynamic_pointer_cast<Qwen::TimestepEmbedding>(blocks["timestep_embedder"]);
|
|
||||||
auto timesteps_proj = ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, 256, 10000, 1000.f);
|
|
||||||
return timestep_embedder->forward(ctx, timesteps_proj);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensGateMLP : public GGMLBlock {
|
|
||||||
LensGateMLP(int64_t dim, int64_t hidden_dim) {
|
|
||||||
blocks["w1"] = std::make_shared<Linear>(dim, hidden_dim, false);
|
|
||||||
blocks["w2"] = std::make_shared<Linear>(hidden_dim, dim, false);
|
|
||||||
blocks["w3"] = std::make_shared<Linear>(dim, hidden_dim, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto w1 = std::dynamic_pointer_cast<Linear>(blocks["w1"]);
|
|
||||||
auto w2 = std::dynamic_pointer_cast<Linear>(blocks["w2"]);
|
|
||||||
auto w3 = std::dynamic_pointer_cast<Linear>(blocks["w3"]);
|
|
||||||
|
|
||||||
auto gate = ggml_silu(ctx->ggml_ctx, w1->forward(ctx, x));
|
|
||||||
auto up = w3->forward(ctx, x);
|
|
||||||
x = ggml_mul(ctx->ggml_ctx, gate, up);
|
|
||||||
return w2->forward(ctx, x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensJointAttention : public GGMLBlock {
|
|
||||||
int64_t dim_head;
|
|
||||||
int64_t num_heads;
|
|
||||||
|
|
||||||
LensJointAttention(int64_t query_dim,
|
|
||||||
int64_t dim_head,
|
|
||||||
int64_t num_heads,
|
|
||||||
float eps = 1e-5f)
|
|
||||||
: dim_head(dim_head), num_heads(num_heads) {
|
|
||||||
int64_t inner_dim = dim_head * num_heads;
|
|
||||||
blocks["img_qkv"] = std::make_shared<Linear>(query_dim, inner_dim * 3, true);
|
|
||||||
blocks["txt_qkv"] = std::make_shared<Linear>(query_dim, inner_dim * 3, true);
|
|
||||||
|
|
||||||
blocks["norm_q"] = std::make_shared<RMSNorm>(dim_head, eps);
|
|
||||||
blocks["norm_k"] = std::make_shared<RMSNorm>(dim_head, eps);
|
|
||||||
blocks["norm_added_q"] = std::make_shared<RMSNorm>(dim_head, eps);
|
|
||||||
blocks["norm_added_k"] = std::make_shared<RMSNorm>(dim_head, eps);
|
|
||||||
|
|
||||||
blocks["to_out.0"] = std::make_shared<Linear>(inner_dim, query_dim, true);
|
|
||||||
blocks["to_add_out"] = std::make_shared<Linear>(inner_dim, query_dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<ggml_tensor*, ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* img,
|
|
||||||
ggml_tensor* txt,
|
|
||||||
ggml_tensor* pe,
|
|
||||||
ggml_tensor* mask = nullptr) {
|
|
||||||
auto img_qkv = std::dynamic_pointer_cast<Linear>(blocks["img_qkv"]);
|
|
||||||
auto txt_qkv = std::dynamic_pointer_cast<Linear>(blocks["txt_qkv"]);
|
|
||||||
auto norm_q = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_q"]);
|
|
||||||
auto norm_k = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_k"]);
|
|
||||||
auto norm_add_q = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_added_q"]);
|
|
||||||
auto norm_add_k = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_added_k"]);
|
|
||||||
auto to_out_0 = std::dynamic_pointer_cast<Linear>(blocks["to_out.0"]);
|
|
||||||
auto to_add_out = std::dynamic_pointer_cast<Linear>(blocks["to_add_out"]);
|
|
||||||
int64_t n_img = img->ne[1];
|
|
||||||
int64_t n_txt = txt->ne[1];
|
|
||||||
int64_t N = img->ne[2];
|
|
||||||
int64_t inner = dim_head * num_heads;
|
|
||||||
|
|
||||||
auto img_qkv_vec = split_qkv(ctx->ggml_ctx, img_qkv->forward(ctx, img));
|
|
||||||
auto txt_qkv_vec = split_qkv(ctx->ggml_ctx, txt_qkv->forward(ctx, txt));
|
|
||||||
|
|
||||||
auto img_q = ggml_reshape_4d(ctx->ggml_ctx, img_qkv_vec[0], dim_head, num_heads, n_img, N);
|
|
||||||
auto img_k = ggml_reshape_4d(ctx->ggml_ctx, img_qkv_vec[1], dim_head, num_heads, n_img, N);
|
|
||||||
auto img_v = ggml_reshape_4d(ctx->ggml_ctx, img_qkv_vec[2], dim_head, num_heads, n_img, N);
|
|
||||||
|
|
||||||
img_q = norm_q->forward(ctx, img_q);
|
|
||||||
img_k = norm_k->forward(ctx, img_k);
|
|
||||||
|
|
||||||
auto txt_q = ggml_reshape_4d(ctx->ggml_ctx, txt_qkv_vec[0], dim_head, num_heads, n_txt, N);
|
|
||||||
auto txt_k = ggml_reshape_4d(ctx->ggml_ctx, txt_qkv_vec[1], dim_head, num_heads, n_txt, N);
|
|
||||||
auto txt_v = ggml_reshape_4d(ctx->ggml_ctx, txt_qkv_vec[2], dim_head, num_heads, n_txt, N);
|
|
||||||
|
|
||||||
txt_q = norm_add_q->forward(ctx, txt_q);
|
|
||||||
txt_k = norm_add_k->forward(ctx, txt_k);
|
|
||||||
|
|
||||||
auto q = ggml_concat(ctx->ggml_ctx, img_q, txt_q, 2);
|
|
||||||
auto k = ggml_concat(ctx->ggml_ctx, img_k, txt_k, 2);
|
|
||||||
auto v = ggml_concat(ctx->ggml_ctx, img_v, txt_v, 2);
|
|
||||||
|
|
||||||
auto attn = Rope::attention(ctx, q, k, v, pe, mask, (1.0f / 128.f));
|
|
||||||
|
|
||||||
auto img_attn_out = ggml_view_3d(ctx->ggml_ctx,
|
|
||||||
attn,
|
|
||||||
inner,
|
|
||||||
n_img,
|
|
||||||
N,
|
|
||||||
attn->nb[1],
|
|
||||||
attn->nb[2],
|
|
||||||
0);
|
|
||||||
auto txt_attn_out = ggml_view_3d(ctx->ggml_ctx,
|
|
||||||
attn,
|
|
||||||
inner,
|
|
||||||
n_txt,
|
|
||||||
N,
|
|
||||||
attn->nb[1],
|
|
||||||
attn->nb[2],
|
|
||||||
n_img * attn->nb[1]);
|
|
||||||
|
|
||||||
img_attn_out = to_out_0->forward(ctx, ggml_cont(ctx->ggml_ctx, img_attn_out));
|
|
||||||
txt_attn_out = to_add_out->forward(ctx, ggml_cont(ctx->ggml_ctx, txt_attn_out));
|
|
||||||
return {img_attn_out, txt_attn_out};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensTransformerBlock : public GGMLBlock {
|
|
||||||
LensTransformerBlock(int64_t dim,
|
|
||||||
int64_t num_attention_heads,
|
|
||||||
int64_t attention_head_dim,
|
|
||||||
float eps = 1e-6f) {
|
|
||||||
int64_t mlp_hidden_dim = dim / 3 * 8;
|
|
||||||
blocks["img_mod.1"] = std::make_shared<Linear>(dim, 6 * dim, true);
|
|
||||||
blocks["txt_mod.1"] = std::make_shared<Linear>(dim, 6 * dim, true);
|
|
||||||
blocks["img_norm1"] = std::make_shared<RMSNorm>(dim, eps);
|
|
||||||
blocks["img_norm2"] = std::make_shared<RMSNorm>(dim, eps);
|
|
||||||
blocks["txt_norm1"] = std::make_shared<RMSNorm>(dim, eps);
|
|
||||||
blocks["txt_norm2"] = std::make_shared<RMSNorm>(dim, eps);
|
|
||||||
blocks["img_mlp"] = std::make_shared<LensGateMLP>(dim, mlp_hidden_dim);
|
|
||||||
blocks["txt_mlp"] = std::make_shared<LensGateMLP>(dim, mlp_hidden_dim);
|
|
||||||
blocks["attn"] = std::make_shared<LensJointAttention>(dim, attention_head_dim, num_attention_heads);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<ggml_tensor*, ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* img,
|
|
||||||
ggml_tensor* txt,
|
|
||||||
ggml_tensor* t_emb,
|
|
||||||
ggml_tensor* pe) {
|
|
||||||
auto img_mod_1 = std::dynamic_pointer_cast<Linear>(blocks["img_mod.1"]);
|
|
||||||
auto txt_mod_1 = std::dynamic_pointer_cast<Linear>(blocks["txt_mod.1"]);
|
|
||||||
auto img_norm1 = std::dynamic_pointer_cast<RMSNorm>(blocks["img_norm1"]);
|
|
||||||
auto img_norm2 = std::dynamic_pointer_cast<RMSNorm>(blocks["img_norm2"]);
|
|
||||||
auto txt_norm1 = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm1"]);
|
|
||||||
auto txt_norm2 = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm2"]);
|
|
||||||
auto img_mlp = std::dynamic_pointer_cast<LensGateMLP>(blocks["img_mlp"]);
|
|
||||||
auto txt_mlp = std::dynamic_pointer_cast<LensGateMLP>(blocks["txt_mlp"]);
|
|
||||||
auto attn = std::dynamic_pointer_cast<LensJointAttention>(blocks["attn"]);
|
|
||||||
|
|
||||||
auto temb = ggml_silu(ctx->ggml_ctx, t_emb);
|
|
||||||
|
|
||||||
auto img_mod_params = img_mod_1->forward(ctx, temb);
|
|
||||||
auto img_mod_vec = ggml_ext_chunk(ctx->ggml_ctx, img_mod_params, 6, 0);
|
|
||||||
auto txt_mod_params = txt_mod_1->forward(ctx, temb);
|
|
||||||
auto txt_mod_vec = ggml_ext_chunk(ctx->ggml_ctx, txt_mod_params, 6, 0);
|
|
||||||
|
|
||||||
auto img_normed = img_norm1->forward(ctx, img);
|
|
||||||
auto img_modulated = Flux::modulate(ctx->ggml_ctx, img_normed, img_mod_vec[0], img_mod_vec[1]);
|
|
||||||
auto txt_normed = txt_norm1->forward(ctx, txt);
|
|
||||||
auto txt_modulated = Flux::modulate(ctx->ggml_ctx, txt_normed, txt_mod_vec[0], txt_mod_vec[1]);
|
|
||||||
|
|
||||||
auto [img_attn_output, txt_attn_output] = attn->forward(ctx, img_modulated, txt_modulated, pe);
|
|
||||||
|
|
||||||
img = ggml_add(ctx->ggml_ctx, img, ggml_mul(ctx->ggml_ctx, img_attn_output, img_mod_vec[2]));
|
|
||||||
txt = ggml_add(ctx->ggml_ctx, txt, ggml_mul(ctx->ggml_ctx, txt_attn_output, txt_mod_vec[2]));
|
|
||||||
|
|
||||||
auto img_normed2 = img_norm2->forward(ctx, img);
|
|
||||||
auto img_modulated2 = Flux::modulate(ctx->ggml_ctx, img_normed2, img_mod_vec[3], img_mod_vec[4]);
|
|
||||||
auto txt_normed2 = txt_norm2->forward(ctx, txt);
|
|
||||||
auto txt_modulated2 = Flux::modulate(ctx->ggml_ctx, txt_normed2, txt_mod_vec[3], txt_mod_vec[4]);
|
|
||||||
|
|
||||||
img = ggml_add(ctx->ggml_ctx, img, ggml_mul(ctx->ggml_ctx, img_mlp->forward(ctx, img_modulated2), img_mod_vec[5]));
|
|
||||||
txt = ggml_add(ctx->ggml_ctx, txt, ggml_mul(ctx->ggml_ctx, txt_mlp->forward(ctx, txt_modulated2), txt_mod_vec[5]));
|
|
||||||
return {img, txt};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensAdaLayerNormContinuous : public GGMLBlock {
|
|
||||||
int64_t hidden_size;
|
|
||||||
float eps;
|
|
||||||
|
|
||||||
LensAdaLayerNormContinuous(int64_t hidden_size, float eps = 1e-6f)
|
|
||||||
: hidden_size(hidden_size), eps(eps) {
|
|
||||||
blocks["linear"] = std::make_shared<Linear>(hidden_size, hidden_size * 2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* conditioning) {
|
|
||||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
|
||||||
auto mods = ggml_ext_chunk(ctx->ggml_ctx, linear->forward(ctx, ggml_silu(ctx->ggml_ctx, conditioning)), 2, 0);
|
|
||||||
auto scale = mods[0];
|
|
||||||
auto shift = mods[1];
|
|
||||||
x = ggml_norm(ctx->ggml_ctx, x, eps);
|
|
||||||
return Flux::modulate(ctx->ggml_ctx, x, shift, scale);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class LensModel : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
LensConfig config;
|
|
||||||
|
|
||||||
LensModel() = default;
|
|
||||||
LensModel(LensConfig config)
|
|
||||||
: config(config) {
|
|
||||||
int64_t inner_dim = config.num_attention_heads * config.attention_head_dim;
|
|
||||||
blocks["time_text_embed"] = std::make_shared<LensTimestepProjEmbeddings>(inner_dim);
|
|
||||||
blocks["img_in"] = std::make_shared<Linear>(config.in_channels, inner_dim, true);
|
|
||||||
blocks["txt_in"] = std::make_shared<Linear>(config.joint_attention_dim * config.selected_layer_count, inner_dim, true);
|
|
||||||
for (int i = 0; i < config.selected_layer_count; ++i) {
|
|
||||||
blocks["txt_norm." + std::to_string(i)] = std::make_shared<RMSNorm>(config.joint_attention_dim, 1e-5f);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < config.num_layers; ++i) {
|
|
||||||
blocks["transformer_blocks." + std::to_string(i)] = std::make_shared<LensTransformerBlock>(inner_dim,
|
|
||||||
config.num_attention_heads,
|
|
||||||
config.attention_head_dim);
|
|
||||||
}
|
|
||||||
blocks["norm_out"] = std::make_shared<LensAdaLayerNormContinuous>(inner_dim, 1e-6f);
|
|
||||||
blocks["proj_out"] = std::make_shared<Linear>(inner_dim, config.patch_size * config.patch_size * config.out_channels, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* timestep,
|
|
||||||
ggml_tensor* context,
|
|
||||||
ggml_tensor* pe) {
|
|
||||||
GGML_ASSERT(context != nullptr);
|
|
||||||
int64_t W = x->ne[0];
|
|
||||||
int64_t H = x->ne[1];
|
|
||||||
int64_t C = x->ne[2];
|
|
||||||
int64_t N = x->ne[3];
|
|
||||||
|
|
||||||
auto time_text_embed = std::dynamic_pointer_cast<LensTimestepProjEmbeddings>(blocks["time_text_embed"]);
|
|
||||||
auto img_in = std::dynamic_pointer_cast<Linear>(blocks["img_in"]);
|
|
||||||
auto txt_in = std::dynamic_pointer_cast<Linear>(blocks["txt_in"]);
|
|
||||||
auto norm_out = std::dynamic_pointer_cast<LensAdaLayerNormContinuous>(blocks["norm_out"]);
|
|
||||||
auto proj_out = std::dynamic_pointer_cast<Linear>(blocks["proj_out"]);
|
|
||||||
|
|
||||||
auto t_emb = time_text_embed->forward(ctx, timestep);
|
|
||||||
|
|
||||||
auto img = ggml_reshape_3d(ctx->ggml_ctx, x, W * H, C, N);
|
|
||||||
img = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, img, 1, 0, 2, 3));
|
|
||||||
img = img_in->forward(ctx, img);
|
|
||||||
|
|
||||||
std::vector<ggml_tensor*> txt_chunks = ggml_ext_chunk(ctx->ggml_ctx, context, config.selected_layer_count, 0);
|
|
||||||
ggml_tensor* txt = nullptr;
|
|
||||||
for (int i = 0; i < config.selected_layer_count; ++i) {
|
|
||||||
auto txt_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm." + std::to_string(i)]);
|
|
||||||
auto chunk = txt_norm->forward(ctx, txt_chunks[i]);
|
|
||||||
txt = txt == nullptr ? chunk : ggml_concat(ctx->ggml_ctx, txt, chunk, 0);
|
|
||||||
}
|
|
||||||
txt = txt_in->forward(ctx, txt);
|
|
||||||
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(img, "lens.prelude", "img");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(txt, "lens.prelude", "txt");
|
|
||||||
|
|
||||||
for (int i = 0; i < config.num_layers; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<LensTransformerBlock>(blocks["transformer_blocks." + std::to_string(i)]);
|
|
||||||
auto out = block->forward(ctx, img, txt, t_emb, pe);
|
|
||||||
img = out.first;
|
|
||||||
txt = out.second;
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(img, "lens.transformer_blocks." + std::to_string(i), "img");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(txt, "lens.transformer_blocks." + std::to_string(i), "txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
img = norm_out->forward(ctx, img, t_emb);
|
|
||||||
img = proj_out->forward(ctx, img);
|
|
||||||
|
|
||||||
auto out = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, img, 1, 0, 2, 3));
|
|
||||||
out = ggml_reshape_4d(ctx->ggml_ctx, out, W, H, config.patch_size * config.patch_size * config.out_channels, N);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LensRunner : public DiffusionModelRunner {
|
|
||||||
LensConfig config;
|
|
||||||
LensModel 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),
|
|
||||||
config(LensConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
|
||||||
lens = LensModel(config);
|
|
||||||
lens.init(params_ctx, tensor_storage_map, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_desc() override {
|
|
||||||
return "lens";
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
|
|
||||||
lens.get_param_tensors(tensors, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
|
||||||
const sd::Tensor<float>& timesteps_tensor,
|
|
||||||
const sd::Tensor<float>& context_tensor) {
|
|
||||||
ggml_cgraph* gf = new_graph_custom(LENS_GRAPH_SIZE);
|
|
||||||
ggml_tensor* x = make_input(x_tensor);
|
|
||||||
ggml_tensor* timesteps = make_input(timesteps_tensor);
|
|
||||||
GGML_ASSERT(x->ne[3] == 1);
|
|
||||||
GGML_ASSERT(!context_tensor.empty());
|
|
||||||
ggml_tensor* context = make_input(context_tensor);
|
|
||||||
|
|
||||||
pe_vec = Rope::gen_lens_pe(static_cast<int>(x->ne[1]),
|
|
||||||
static_cast<int>(x->ne[0]),
|
|
||||||
static_cast<int>(x->ne[3]),
|
|
||||||
static_cast<int>(context->ne[1]),
|
|
||||||
config.theta,
|
|
||||||
circular_y_enabled,
|
|
||||||
circular_x_enabled,
|
|
||||||
config.axes_dim);
|
|
||||||
int pos_len = static_cast<int>(pe_vec.size() / config.axes_dim_sum / 2);
|
|
||||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, config.axes_dim_sum / 2, pos_len);
|
|
||||||
set_backend_tensor_data(pe, pe_vec.data());
|
|
||||||
|
|
||||||
auto runner_ctx = get_context();
|
|
||||||
ggml_tensor* out = lens.forward(&runner_ctx, x, timesteps, context, pe);
|
|
||||||
ggml_build_forward_expand(gf, out);
|
|
||||||
return gf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const sd::Tensor<float>& x,
|
|
||||||
const sd::Tensor<float>& timesteps,
|
|
||||||
const sd::Tensor<float>& context) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
tensor_or_empty(diffusion_params.context));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Lens
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_LENS_HPP__
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,107 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_MODEL_HPP__
|
|
||||||
#define __SD_MODEL_DIFFUSION_MODEL_HPP__
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
|
||||||
#include "core/tensor_ggml.hpp"
|
|
||||||
|
|
||||||
struct UNetDiffusionExtra {
|
|
||||||
int num_video_frames = -1;
|
|
||||||
const std::vector<sd::Tensor<float>>* controls = nullptr;
|
|
||||||
float control_strength = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SkipLayerDiffusionExtra {
|
|
||||||
const std::vector<int>* skip_layers = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FluxDiffusionExtra {
|
|
||||||
const sd::Tensor<float>* guidance = nullptr;
|
|
||||||
const std::vector<int>* skip_layers = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimaDiffusionExtra {
|
|
||||||
const sd::Tensor<int32_t>* t5_ids = nullptr;
|
|
||||||
const sd::Tensor<float>* t5_weights = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WanDiffusionExtra {
|
|
||||||
const sd::Tensor<float>* vace_context = nullptr;
|
|
||||||
float vace_strength = 1.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HiDreamO1DiffusionExtra {
|
|
||||||
const sd::Tensor<int32_t>* input_ids = nullptr;
|
|
||||||
const sd::Tensor<int32_t>* input_pos = nullptr;
|
|
||||||
const sd::Tensor<int32_t>* token_types = nullptr;
|
|
||||||
const sd::Tensor<int32_t>* vinput_mask = nullptr;
|
|
||||||
const std::vector<std::pair<int, sd::Tensor<float>>>* image_embeds = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LTXAVDiffusionExtra {
|
|
||||||
const sd::Tensor<float>* audio_x = nullptr;
|
|
||||||
const sd::Tensor<float>* audio_timesteps = nullptr;
|
|
||||||
int audio_length = 0;
|
|
||||||
float frame_rate = 24.f;
|
|
||||||
const sd::Tensor<float>* video_positions = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
using DiffusionExtraParams = std::variant<std::monostate,
|
|
||||||
UNetDiffusionExtra,
|
|
||||||
SkipLayerDiffusionExtra,
|
|
||||||
FluxDiffusionExtra,
|
|
||||||
AnimaDiffusionExtra,
|
|
||||||
WanDiffusionExtra,
|
|
||||||
HiDreamO1DiffusionExtra,
|
|
||||||
LTXAVDiffusionExtra>;
|
|
||||||
|
|
||||||
struct DiffusionParams {
|
|
||||||
const sd::Tensor<float>* x = nullptr;
|
|
||||||
const sd::Tensor<float>* timesteps = nullptr;
|
|
||||||
const sd::Tensor<float>* context = nullptr;
|
|
||||||
const sd::Tensor<float>* c_concat = nullptr;
|
|
||||||
const sd::Tensor<float>* y = nullptr;
|
|
||||||
const std::vector<sd::Tensor<float>>* ref_latents = nullptr;
|
|
||||||
bool increase_ref_index = false;
|
|
||||||
DiffusionExtraParams extra = std::monostate{};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static inline const T* diffusion_extra_as(const DiffusionParams& params) {
|
|
||||||
const auto* extra = std::get_if<T>(¶ms.extra);
|
|
||||||
GGML_ASSERT(extra != nullptr);
|
|
||||||
return extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static inline const sd::Tensor<T>& tensor_or_empty(const sd::Tensor<T>* tensor) {
|
|
||||||
static const sd::Tensor<T> kEmpty;
|
|
||||||
return tensor != nullptr ? *tensor : kEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DiffusionModelRunner : public GGMLRunner {
|
|
||||||
protected:
|
|
||||||
std::string prefix;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DiffusionModelRunner(ggml_backend_t backend,
|
|
||||||
ggml_backend_t params_backend,
|
|
||||||
const std::string& prefix)
|
|
||||||
: GGMLRunner(backend, params_backend),
|
|
||||||
prefix(prefix) {}
|
|
||||||
|
|
||||||
virtual sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) = 0;
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {
|
|
||||||
get_param_tensors(tensors, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors,
|
|
||||||
const std::string& prefix) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_MODEL_HPP__
|
|
||||||
@ -1,847 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_DIFFUSION_PID_HPP__
|
|
||||||
#define __SD_MODEL_DIFFUSION_PID_HPP__
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
|
||||||
#include "model/common/rope.hpp"
|
|
||||||
#include "model/diffusion/dit.hpp"
|
|
||||||
#include "model/diffusion/mmdit.hpp"
|
|
||||||
|
|
||||||
namespace Pid {
|
|
||||||
constexpr int PID_GRAPH_SIZE = 196608;
|
|
||||||
constexpr float PID_PI = 3.14159265358979323846f;
|
|
||||||
|
|
||||||
struct PixelDiTConfig {
|
|
||||||
int64_t in_channels = 3;
|
|
||||||
int64_t hidden_size = 1536;
|
|
||||||
int64_t num_groups = 24;
|
|
||||||
int64_t patch_mlp_hidden_dim = 4096;
|
|
||||||
int64_t pixel_hidden_size = 16;
|
|
||||||
int64_t pixel_attn_hidden_size = 1152;
|
|
||||||
int64_t pixel_num_groups = 16;
|
|
||||||
int64_t patch_depth = 14;
|
|
||||||
int64_t pixel_depth = 2;
|
|
||||||
int64_t patch_size = 16;
|
|
||||||
int64_t txt_embed_dim = 2304;
|
|
||||||
int64_t txt_max_length = 300;
|
|
||||||
float text_rope_theta = 10000.f;
|
|
||||||
int64_t lq_latent_channels = 16;
|
|
||||||
int64_t lq_hidden_dim = 512;
|
|
||||||
int64_t lq_num_res_blocks = 4;
|
|
||||||
int64_t lq_interval = 2;
|
|
||||||
int64_t lq_sr_scale = 4;
|
|
||||||
int64_t lq_latent_down_factor = 8;
|
|
||||||
int64_t rope_ref_grid_h = 64;
|
|
||||||
int64_t rope_ref_grid_w = 64;
|
|
||||||
|
|
||||||
static PixelDiTConfig detect_from_weights(const String2TensorStorage& tensor_storage_map, const std::string& prefix) {
|
|
||||||
PixelDiTConfig config;
|
|
||||||
for (const auto& [name, tensor_storage] : tensor_storage_map) {
|
|
||||||
if (!starts_with(name, prefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
size_t pos = name.find("patch_blocks.");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
auto items = split_string(name.substr(pos), '.');
|
|
||||||
if (items.size() > 1) {
|
|
||||||
int block_index = atoi(items[1].c_str());
|
|
||||||
config.patch_depth = std::max<int64_t>(config.patch_depth, block_index + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos = name.find("pixel_blocks.");
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
auto items = split_string(name.substr(pos), '.');
|
|
||||||
if (items.size() > 1) {
|
|
||||||
int block_index = atoi(items[1].c_str());
|
|
||||||
config.pixel_depth = std::max<int64_t>(config.pixel_depth, block_index + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (name.find("lq_proj.latent_proj.0.weight") != std::string::npos) {
|
|
||||||
config.lq_latent_channels = tensor_storage.ne[2];
|
|
||||||
config.lq_latent_down_factor = config.lq_latent_channels >= 64 ? 16 : 8;
|
|
||||||
}
|
|
||||||
if (name.find("patch_blocks.0.mlp_x.w1.weight") != std::string::npos) {
|
|
||||||
config.patch_mlp_hidden_dim = tensor_storage.ne[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG_DEBUG("pid: patch_depth = %" PRId64 ", pixel_depth = %" PRId64 ", patch_mlp_hidden_dim = %" PRId64 ", lq_latent_channels = %" PRId64 ", lq_latent_down_factor = %" PRId64,
|
|
||||||
config.patch_depth,
|
|
||||||
config.pixel_depth,
|
|
||||||
config.patch_mlp_hidden_dim,
|
|
||||||
config.lq_latent_channels,
|
|
||||||
config.lq_latent_down_factor);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::vector<float> make_rope_1d(int length,
|
|
||||||
int dim,
|
|
||||||
float theta) {
|
|
||||||
GGML_ASSERT(dim % 2 == 0);
|
|
||||||
return Rope::flatten(Rope::rope(Rope::linspace(0.f, static_cast<float>(length - 1), length), dim, theta));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<float> make_rope_2d(int height,
|
|
||||||
int width,
|
|
||||||
int dim,
|
|
||||||
float theta = 10000.f,
|
|
||||||
float scale = 16.f,
|
|
||||||
int ref_grid_h = 0,
|
|
||||||
int ref_grid_w = 0) {
|
|
||||||
GGML_ASSERT(dim % 4 == 0);
|
|
||||||
return Rope::embed_2d_interleaved(height, width, dim, theta, scale, ref_grid_h, ref_grid_w);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<float> make_pixel_abs_pos(int height,
|
|
||||||
int width,
|
|
||||||
int dim) {
|
|
||||||
GGML_ASSERT(dim % 4 == 0);
|
|
||||||
int half_dim = dim / 2;
|
|
||||||
std::vector<float> x_pos;
|
|
||||||
std::vector<float> y_pos;
|
|
||||||
x_pos.reserve(static_cast<size_t>(height) * width);
|
|
||||||
y_pos.reserve(static_cast<size_t>(height) * width);
|
|
||||||
for (int iy = 0; iy < height; ++iy) {
|
|
||||||
for (int ix = 0; ix < width; ++ix) {
|
|
||||||
x_pos.push_back(static_cast<float>(ix));
|
|
||||||
y_pos.push_back(static_cast<float>(iy));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto x_emb = timestep_embedding(x_pos, half_dim, 10000, false);
|
|
||||||
auto y_emb = timestep_embedding(y_pos, half_dim, 10000, false);
|
|
||||||
|
|
||||||
std::vector<float> out(static_cast<size_t>(dim) * height * width);
|
|
||||||
for (int pos = 0; pos < height * width; ++pos) {
|
|
||||||
size_t out_base = static_cast<size_t>(pos) * dim;
|
|
||||||
size_t emb_base = static_cast<size_t>(pos) * half_dim;
|
|
||||||
for (int i = 0; i < half_dim; ++i) {
|
|
||||||
out[out_base + i] = x_emb[emb_base + i];
|
|
||||||
out[out_base + half_dim + i] = y_emb[emb_base + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ggml_tensor* apply_adaln(ggml_context* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* shift,
|
|
||||||
ggml_tensor* scale) {
|
|
||||||
return ggml_add(ctx, ggml_add(ctx, x, ggml_mul(ctx, x, scale)), shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PatchTokenEmbedder : public GGMLBlock {
|
|
||||||
bool use_rms_norm;
|
|
||||||
|
|
||||||
PatchTokenEmbedder(int64_t in_chans,
|
|
||||||
int64_t embed_dim,
|
|
||||||
bool use_rms_norm = false,
|
|
||||||
bool bias = true)
|
|
||||||
: use_rms_norm(use_rms_norm) {
|
|
||||||
blocks["proj"] = std::make_shared<Linear>(in_chans, embed_dim, bias);
|
|
||||||
if (use_rms_norm) {
|
|
||||||
blocks["norm"] = std::make_shared<RMSNorm>(embed_dim, 1e-6f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
|
||||||
x = proj->forward(ctx, x);
|
|
||||||
if (use_rms_norm) {
|
|
||||||
auto norm = std::dynamic_pointer_cast<RMSNorm>(blocks["norm"]);
|
|
||||||
x = norm->forward(ctx, x);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PixelDiTTimestepEmbedder : public GGMLBlock {
|
|
||||||
int frequency_embedding_size;
|
|
||||||
|
|
||||||
PixelDiTTimestepEmbedder(int64_t hidden_size,
|
|
||||||
int frequency_embedding_size = 256)
|
|
||||||
: frequency_embedding_size(frequency_embedding_size) {
|
|
||||||
blocks["mlp.0"] = std::make_shared<Linear>(frequency_embedding_size, hidden_size, true, true);
|
|
||||||
blocks["mlp.2"] = std::make_shared<Linear>(hidden_size, hidden_size, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* t) {
|
|
||||||
auto mlp_0 = std::dynamic_pointer_cast<Linear>(blocks["mlp.0"]);
|
|
||||||
auto mlp_2 = std::dynamic_pointer_cast<Linear>(blocks["mlp.2"]);
|
|
||||||
auto t_emb = ggml_ext_timestep_embedding(ctx->ggml_ctx, t, frequency_embedding_size, 10);
|
|
||||||
t_emb = mlp_0->forward(ctx, t_emb);
|
|
||||||
t_emb = ggml_silu_inplace(ctx->ggml_ctx, t_emb);
|
|
||||||
return mlp_2->forward(ctx, t_emb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FeedForward : public GGMLBlock {
|
|
||||||
FeedForward(int64_t dim, int64_t hidden_dim) {
|
|
||||||
blocks["w1"] = std::make_shared<Linear>(dim, hidden_dim, false);
|
|
||||||
blocks["w2"] = std::make_shared<Linear>(hidden_dim, dim, false);
|
|
||||||
blocks["w3"] = std::make_shared<Linear>(dim, hidden_dim, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto w1 = std::dynamic_pointer_cast<Linear>(blocks["w1"]);
|
|
||||||
auto w2 = std::dynamic_pointer_cast<Linear>(blocks["w2"]);
|
|
||||||
auto w3 = std::dynamic_pointer_cast<Linear>(blocks["w3"]);
|
|
||||||
auto h = ggml_silu_inplace(ctx->ggml_ctx, w1->forward(ctx, x));
|
|
||||||
h = ggml_mul_inplace(ctx->ggml_ctx, h, w3->forward(ctx, x));
|
|
||||||
return w2->forward(ctx, h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FinalLayer : public GGMLBlock {
|
|
||||||
FinalLayer(int64_t hidden_size, int64_t out_channels) {
|
|
||||||
blocks["norm"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
|
||||||
blocks["linear"] = std::make_shared<Linear>(hidden_size, out_channels, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto norm = std::dynamic_pointer_cast<RMSNorm>(blocks["norm"]);
|
|
||||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
|
||||||
return linear->forward(ctx, norm->forward(ctx, x));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RotaryAttention : public GGMLBlock {
|
|
||||||
int64_t dim;
|
|
||||||
int64_t num_heads;
|
|
||||||
|
|
||||||
RotaryAttention(int64_t dim, int64_t num_heads)
|
|
||||||
: dim(dim), num_heads(num_heads) {
|
|
||||||
int64_t head_dim = dim / num_heads;
|
|
||||||
blocks["qkv"] = std::make_shared<Linear>(dim, dim * 3, false);
|
|
||||||
blocks["q_norm"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["k_norm"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["proj"] = std::make_shared<Linear>(dim, dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* pos) {
|
|
||||||
auto qkv_proj = std::dynamic_pointer_cast<Linear>(blocks["qkv"]);
|
|
||||||
auto q_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["q_norm"]);
|
|
||||||
auto k_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["k_norm"]);
|
|
||||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
|
||||||
|
|
||||||
auto qkv = qkv_proj->forward(ctx, x);
|
|
||||||
auto qkv_vec = split_qkv(ctx->ggml_ctx, qkv);
|
|
||||||
int64_t L = x->ne[1];
|
|
||||||
int64_t N = x->ne[2];
|
|
||||||
int64_t head_dim = dim / num_heads;
|
|
||||||
auto q = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[0], head_dim, num_heads, L, N);
|
|
||||||
auto k = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[1], head_dim, num_heads, L, N);
|
|
||||||
auto v = ggml_reshape_4d(ctx->ggml_ctx, qkv_vec[2], head_dim, num_heads, L, N);
|
|
||||||
q = q_norm->forward(ctx, q);
|
|
||||||
k = k_norm->forward(ctx, k);
|
|
||||||
x = Rope::attention(ctx, q, k, v, pos, nullptr, 1.0f / 128.f, true);
|
|
||||||
return proj->forward(ctx, x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MMDiTJointAttention : public GGMLBlock {
|
|
||||||
int64_t dim;
|
|
||||||
int64_t num_heads;
|
|
||||||
|
|
||||||
MMDiTJointAttention(int64_t dim, int64_t num_heads)
|
|
||||||
: dim(dim), num_heads(num_heads) {
|
|
||||||
int64_t head_dim = dim / num_heads;
|
|
||||||
blocks["qkv_x"] = std::make_shared<Linear>(dim, dim * 3, false);
|
|
||||||
blocks["qkv_y"] = std::make_shared<Linear>(dim, dim * 3, false);
|
|
||||||
blocks["q_norm_x"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["k_norm_x"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["q_norm_y"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["k_norm_y"] = std::make_shared<RMSNorm>(head_dim, 1e-6f);
|
|
||||||
blocks["proj_x"] = std::make_shared<Linear>(dim, dim, true);
|
|
||||||
blocks["proj_y"] = std::make_shared<Linear>(dim, dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<ggml_tensor*, ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* y,
|
|
||||||
ggml_tensor* pos_img,
|
|
||||||
ggml_tensor* pos_txt) {
|
|
||||||
auto qkv_x_proj = std::dynamic_pointer_cast<Linear>(blocks["qkv_x"]);
|
|
||||||
auto qkv_y_proj = std::dynamic_pointer_cast<Linear>(blocks["qkv_y"]);
|
|
||||||
auto q_norm_x = std::dynamic_pointer_cast<RMSNorm>(blocks["q_norm_x"]);
|
|
||||||
auto k_norm_x = std::dynamic_pointer_cast<RMSNorm>(blocks["k_norm_x"]);
|
|
||||||
auto q_norm_y = std::dynamic_pointer_cast<RMSNorm>(blocks["q_norm_y"]);
|
|
||||||
auto k_norm_y = std::dynamic_pointer_cast<RMSNorm>(blocks["k_norm_y"]);
|
|
||||||
auto proj_x = std::dynamic_pointer_cast<Linear>(blocks["proj_x"]);
|
|
||||||
auto proj_y = std::dynamic_pointer_cast<Linear>(blocks["proj_y"]);
|
|
||||||
|
|
||||||
int64_t Nx = x->ne[1];
|
|
||||||
int64_t Ny = y->ne[1];
|
|
||||||
int64_t N = x->ne[2];
|
|
||||||
int64_t head_dim = dim / num_heads;
|
|
||||||
|
|
||||||
auto qkv_x = split_qkv(ctx->ggml_ctx, qkv_x_proj->forward(ctx, x));
|
|
||||||
auto qx = ggml_reshape_4d(ctx->ggml_ctx, qkv_x[0], head_dim, num_heads, Nx, N);
|
|
||||||
auto kx = ggml_reshape_4d(ctx->ggml_ctx, qkv_x[1], head_dim, num_heads, Nx, N);
|
|
||||||
auto vx = ggml_reshape_4d(ctx->ggml_ctx, qkv_x[2], head_dim, num_heads, Nx, N);
|
|
||||||
qx = q_norm_x->forward(ctx, qx);
|
|
||||||
kx = k_norm_x->forward(ctx, kx);
|
|
||||||
|
|
||||||
auto qkv_y = split_qkv(ctx->ggml_ctx, qkv_y_proj->forward(ctx, y));
|
|
||||||
auto qy = ggml_reshape_4d(ctx->ggml_ctx, qkv_y[0], head_dim, num_heads, Ny, N);
|
|
||||||
auto ky = ggml_reshape_4d(ctx->ggml_ctx, qkv_y[1], head_dim, num_heads, Ny, N);
|
|
||||||
auto vy = ggml_reshape_4d(ctx->ggml_ctx, qkv_y[2], head_dim, num_heads, Ny, N);
|
|
||||||
qy = q_norm_y->forward(ctx, qy);
|
|
||||||
ky = k_norm_y->forward(ctx, ky);
|
|
||||||
|
|
||||||
auto q_joint = ggml_concat(ctx->ggml_ctx, qy, qx, 2);
|
|
||||||
auto k_joint = ggml_concat(ctx->ggml_ctx, ky, kx, 2);
|
|
||||||
auto v_joint = ggml_concat(ctx->ggml_ctx, vy, vx, 2);
|
|
||||||
auto pos_joint = ggml_concat(ctx->ggml_ctx, pos_txt, pos_img, 3);
|
|
||||||
auto out = Rope::attention(ctx, q_joint, k_joint, v_joint, pos_joint, nullptr, 1.0f, true);
|
|
||||||
|
|
||||||
auto out_y = ggml_ext_slice(ctx->ggml_ctx, out, 1, 0, Ny);
|
|
||||||
auto out_x = ggml_ext_slice(ctx->ggml_ctx, out, 1, Ny, Ny + Nx);
|
|
||||||
return {proj_x->forward(ctx, out_x), proj_y->forward(ctx, out_y)};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MMDiTBlockT2I : public GGMLBlock {
|
|
||||||
int64_t hidden_size;
|
|
||||||
|
|
||||||
MMDiTBlockT2I(int64_t hidden_size, int64_t groups, int64_t mlp_hidden_dim)
|
|
||||||
: hidden_size(hidden_size) {
|
|
||||||
blocks["norm_x1"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
|
||||||
blocks["norm_y1"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
|
||||||
blocks["attn"] = std::make_shared<MMDiTJointAttention>(hidden_size, groups);
|
|
||||||
blocks["norm_x2"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
|
||||||
blocks["norm_y2"] = std::make_shared<RMSNorm>(hidden_size, 1e-6f);
|
|
||||||
blocks["mlp_x"] = std::make_shared<FeedForward>(hidden_size, mlp_hidden_dim);
|
|
||||||
blocks["mlp_y"] = std::make_shared<FeedForward>(hidden_size, mlp_hidden_dim);
|
|
||||||
blocks["adaLN_modulation_img.0"] = std::make_shared<Linear>(hidden_size, 6 * hidden_size, true);
|
|
||||||
blocks["adaLN_modulation_txt.0"] = std::make_shared<Linear>(hidden_size, 6 * hidden_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<ggml_tensor*, ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* y,
|
|
||||||
ggml_tensor* c,
|
|
||||||
ggml_tensor* pos_img,
|
|
||||||
ggml_tensor* pos_txt) {
|
|
||||||
auto norm_x1 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_x1"]);
|
|
||||||
auto norm_y1 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_y1"]);
|
|
||||||
auto attn = std::dynamic_pointer_cast<MMDiTJointAttention>(blocks["attn"]);
|
|
||||||
auto norm_x2 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_x2"]);
|
|
||||||
auto norm_y2 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm_y2"]);
|
|
||||||
auto mlp_x = std::dynamic_pointer_cast<FeedForward>(blocks["mlp_x"]);
|
|
||||||
auto mlp_y = std::dynamic_pointer_cast<FeedForward>(blocks["mlp_y"]);
|
|
||||||
auto ada_img = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation_img.0"]);
|
|
||||||
auto ada_txt = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation_txt.0"]);
|
|
||||||
|
|
||||||
auto mx = ggml_ext_chunk(ctx->ggml_ctx, ada_img->forward(ctx, c), 6, 0);
|
|
||||||
auto my = ggml_ext_chunk(ctx->ggml_ctx, ada_txt->forward(ctx, c), 6, 0);
|
|
||||||
|
|
||||||
auto x_norm = apply_adaln(ctx->ggml_ctx, norm_x1->forward(ctx, x), mx[0], mx[1]);
|
|
||||||
auto y_norm = apply_adaln(ctx->ggml_ctx, norm_y1->forward(ctx, y), my[0], my[1]);
|
|
||||||
auto attn_out = attn->forward(ctx, x_norm, y_norm, pos_img, pos_txt);
|
|
||||||
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, attn_out.first, mx[2]));
|
|
||||||
y = ggml_add(ctx->ggml_ctx, y, ggml_mul(ctx->ggml_ctx, attn_out.second, my[2]));
|
|
||||||
|
|
||||||
auto x_mlp = mlp_x->forward(ctx, apply_adaln(ctx->ggml_ctx, norm_x2->forward(ctx, x), mx[3], mx[4]));
|
|
||||||
auto y_mlp = mlp_y->forward(ctx, apply_adaln(ctx->ggml_ctx, norm_y2->forward(ctx, y), my[3], my[4]));
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, x_mlp, mx[5]));
|
|
||||||
y = ggml_add(ctx->ggml_ctx, y, ggml_mul(ctx->ggml_ctx, y_mlp, my[5]));
|
|
||||||
return {x, y};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PixelTokenEmbedder : public GGMLBlock {
|
|
||||||
int64_t in_channels;
|
|
||||||
int64_t hidden_size_output;
|
|
||||||
|
|
||||||
PixelTokenEmbedder(int64_t in_channels, int64_t hidden_size_output)
|
|
||||||
: in_channels(in_channels), hidden_size_output(hidden_size_output) {
|
|
||||||
blocks["proj"] = std::make_shared<Linear>(in_channels, hidden_size_output, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* inputs,
|
|
||||||
int64_t patch_size,
|
|
||||||
ggml_tensor* pos_full) {
|
|
||||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
|
||||||
int64_t W = inputs->ne[0];
|
|
||||||
int64_t H = inputs->ne[1];
|
|
||||||
int64_t B = inputs->ne[3];
|
|
||||||
int64_t L = (W / patch_size) * (H / patch_size);
|
|
||||||
int64_t P2 = patch_size * patch_size;
|
|
||||||
|
|
||||||
auto x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, inputs, 2, 0, 1, 3));
|
|
||||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, in_channels, W * H, B);
|
|
||||||
x = proj->forward(ctx, x);
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, pos_full);
|
|
||||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, hidden_size_output, W, H, B);
|
|
||||||
x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 1, 2, 0, 3));
|
|
||||||
x = DiT::patchify(ctx->ggml_ctx, x, static_cast<int>(patch_size), static_cast<int>(patch_size), false);
|
|
||||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, hidden_size_output, P2, L * B);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PiTBlock : public GGMLBlock {
|
|
||||||
int64_t pixel_dim;
|
|
||||||
int64_t context_dim;
|
|
||||||
int64_t attn_dim;
|
|
||||||
int64_t num_heads;
|
|
||||||
int64_t patch_size;
|
|
||||||
|
|
||||||
PiTBlock(int64_t pixel_dim,
|
|
||||||
int64_t context_dim,
|
|
||||||
int64_t patch_size,
|
|
||||||
int64_t attn_dim,
|
|
||||||
int64_t num_heads)
|
|
||||||
: pixel_dim(pixel_dim),
|
|
||||||
context_dim(context_dim),
|
|
||||||
attn_dim(attn_dim),
|
|
||||||
num_heads(num_heads),
|
|
||||||
patch_size(patch_size) {
|
|
||||||
int64_t p2 = patch_size * patch_size;
|
|
||||||
blocks["compress_to_attn"] = std::make_shared<Linear>(p2 * pixel_dim, attn_dim, true);
|
|
||||||
blocks["expand_from_attn"] = std::make_shared<Linear>(attn_dim, p2 * pixel_dim, true);
|
|
||||||
blocks["norm1"] = std::make_shared<RMSNorm>(pixel_dim, 1e-6f);
|
|
||||||
blocks["attn"] = std::make_shared<RotaryAttention>(attn_dim, num_heads);
|
|
||||||
blocks["norm2"] = std::make_shared<RMSNorm>(pixel_dim, 1e-6f);
|
|
||||||
blocks["mlp"] = std::make_shared<Mlp>(pixel_dim, pixel_dim * 4);
|
|
||||||
blocks["adaLN_modulation.0"] = std::make_shared<Linear>(context_dim, 6 * pixel_dim * p2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* s_cond,
|
|
||||||
int64_t image_height,
|
|
||||||
int64_t image_width,
|
|
||||||
ggml_tensor* pos_comp) {
|
|
||||||
auto compress = std::dynamic_pointer_cast<Linear>(blocks["compress_to_attn"]);
|
|
||||||
auto expand = std::dynamic_pointer_cast<Linear>(blocks["expand_from_attn"]);
|
|
||||||
auto norm1 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm1"]);
|
|
||||||
auto attn = std::dynamic_pointer_cast<RotaryAttention>(blocks["attn"]);
|
|
||||||
auto norm2 = std::dynamic_pointer_cast<RMSNorm>(blocks["norm2"]);
|
|
||||||
auto mlp = std::dynamic_pointer_cast<Mlp>(blocks["mlp"]);
|
|
||||||
auto ada = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.0"]);
|
|
||||||
|
|
||||||
int64_t Hs = image_height / patch_size;
|
|
||||||
int64_t Ws = image_width / patch_size;
|
|
||||||
int64_t L = Hs * Ws;
|
|
||||||
int64_t BL = x->ne[2];
|
|
||||||
int64_t B = BL / L;
|
|
||||||
int64_t P2 = patch_size * patch_size;
|
|
||||||
|
|
||||||
auto ada_params = ada->forward(ctx, s_cond);
|
|
||||||
ada_params = ggml_reshape_3d(ctx->ggml_ctx, ada_params, 6 * pixel_dim, P2, BL);
|
|
||||||
auto mod = ggml_ext_chunk(ctx->ggml_ctx, ada_params, 6, 0);
|
|
||||||
|
|
||||||
auto x_norm = apply_adaln(ctx->ggml_ctx, norm1->forward(ctx, x), mod[0], mod[1]);
|
|
||||||
auto x_flat = ggml_reshape_2d(ctx->ggml_ctx, x_norm, P2 * pixel_dim, BL);
|
|
||||||
auto x_comp = compress->forward(ctx, x_flat);
|
|
||||||
x_comp = ggml_reshape_3d(ctx->ggml_ctx, x_comp, attn_dim, L, B);
|
|
||||||
auto attn_out = attn->forward(ctx, x_comp, pos_comp);
|
|
||||||
auto attn_flat = expand->forward(ctx, ggml_reshape_2d(ctx->ggml_ctx, attn_out, attn_dim, BL));
|
|
||||||
auto attn_exp = ggml_reshape_3d(ctx->ggml_ctx, attn_flat, pixel_dim, P2, BL);
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, attn_exp, mod[2]));
|
|
||||||
|
|
||||||
auto mlp_out = mlp->forward(ctx, apply_adaln(ctx->ggml_ctx, norm2->forward(ctx, x), mod[3], mod[4]));
|
|
||||||
return ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, mlp_out, mod[5]));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SigmaAwareGate : public GGMLBlock {
|
|
||||||
int64_t dim;
|
|
||||||
|
|
||||||
SigmaAwareGate(int64_t dim)
|
|
||||||
: dim(dim) {
|
|
||||||
blocks["content_proj"] = std::make_shared<Linear>(dim * 2, dim, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_params(ggml_context* ctx,
|
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
|
||||||
std::string prefix = "") override {
|
|
||||||
params["log_alpha"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* lq,
|
|
||||||
ggml_tensor* sigma) {
|
|
||||||
auto content_proj = std::dynamic_pointer_cast<Linear>(blocks["content_proj"]);
|
|
||||||
|
|
||||||
auto content_logit = content_proj->forward(ctx, ggml_concat(ctx->ggml_ctx, x, lq, 0));
|
|
||||||
sigma = ggml_reshape_3d(ctx->ggml_ctx, sigma, 1, 1, sigma->ne[0]);
|
|
||||||
auto alpha = ggml_exp(ctx->ggml_ctx, params["log_alpha"]);
|
|
||||||
auto offset = ggml_neg(ctx->ggml_ctx, ggml_mul(ctx->ggml_ctx, alpha, sigma));
|
|
||||||
auto gate = ggml_sigmoid(ctx->ggml_ctx, ggml_add(ctx->ggml_ctx, content_logit, offset));
|
|
||||||
return ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, gate, lq));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PiDResBlock : public GGMLBlock {
|
|
||||||
PiDResBlock(int64_t channels) {
|
|
||||||
blocks["block.0"] = std::make_shared<GroupNorm>(4, channels, 1e-5f);
|
|
||||||
blocks["block.2"] = std::make_shared<Conv2d>(channels, channels, std::pair<int, int>{3, 3}, std::pair<int, int>{1, 1}, std::pair<int, int>{1, 1});
|
|
||||||
blocks["block.3"] = std::make_shared<GroupNorm>(4, channels, 1e-5f);
|
|
||||||
blocks["block.5"] = std::make_shared<Conv2d>(channels, channels, std::pair<int, int>{3, 3}, std::pair<int, int>{1, 1}, std::pair<int, int>{1, 1});
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto norm1 = std::dynamic_pointer_cast<GroupNorm>(blocks["block.0"]);
|
|
||||||
auto conv1 = std::dynamic_pointer_cast<Conv2d>(blocks["block.2"]);
|
|
||||||
auto norm2 = std::dynamic_pointer_cast<GroupNorm>(blocks["block.3"]);
|
|
||||||
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["block.5"]);
|
|
||||||
auto h = ggml_silu_inplace(ctx->ggml_ctx, norm1->forward(ctx, x));
|
|
||||||
h = conv1->forward(ctx, h);
|
|
||||||
h = ggml_silu_inplace(ctx->ggml_ctx, norm2->forward(ctx, h));
|
|
||||||
h = conv2->forward(ctx, h);
|
|
||||||
return ggml_add(ctx->ggml_ctx, x, h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LQProjection2D : public GGMLBlock {
|
|
||||||
PixelDiTConfig config;
|
|
||||||
|
|
||||||
LQProjection2D(const PixelDiTConfig& config)
|
|
||||||
: config(config) {
|
|
||||||
blocks["latent_proj.0"] = std::make_shared<Conv2d>(config.lq_latent_channels, config.lq_hidden_dim, std::pair<int, int>{3, 3}, std::pair<int, int>{1, 1}, std::pair<int, int>{1, 1});
|
|
||||||
blocks["latent_proj.2"] = std::make_shared<Conv2d>(config.lq_hidden_dim, config.lq_hidden_dim, std::pair<int, int>{3, 3}, std::pair<int, int>{1, 1}, std::pair<int, int>{1, 1});
|
|
||||||
for (int i = 0; i < config.lq_num_res_blocks; ++i) {
|
|
||||||
blocks["latent_proj." + std::to_string(3 + i)] = std::make_shared<PiDResBlock>(config.lq_hidden_dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
int num_outputs = static_cast<int>((config.patch_depth + config.lq_interval - 1) / config.lq_interval);
|
|
||||||
for (int i = 0; i < num_outputs; ++i) {
|
|
||||||
blocks["output_heads." + std::to_string(i)] = std::make_shared<Linear>(config.lq_hidden_dim, config.hidden_size, true);
|
|
||||||
blocks["gate_modules." + std::to_string(i)] = std::make_shared<SigmaAwareGate>(config.hidden_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_gate_active(int block_idx) const {
|
|
||||||
return block_idx % config.lq_interval == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_output_index(int block_idx) const {
|
|
||||||
return block_idx / static_cast<int>(config.lq_interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* gate(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* lq,
|
|
||||||
ggml_tensor* sigma,
|
|
||||||
int out_idx) {
|
|
||||||
auto gate_module = std::dynamic_pointer_cast<SigmaAwareGate>(blocks["gate_modules." + std::to_string(out_idx)]);
|
|
||||||
return gate_module->forward(ctx, x, lq, sigma);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ggml_tensor*> forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* lq_latent,
|
|
||||||
int64_t target_pH,
|
|
||||||
int64_t target_pW) {
|
|
||||||
auto conv0 = std::dynamic_pointer_cast<Conv2d>(blocks["latent_proj.0"]);
|
|
||||||
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["latent_proj.2"]);
|
|
||||||
float z_to_patch_ratio = static_cast<float>(config.lq_sr_scale * config.lq_latent_down_factor) /
|
|
||||||
static_cast<float>(config.patch_size);
|
|
||||||
GGML_ASSERT(z_to_patch_ratio >= 1.0f);
|
|
||||||
if (lq_latent->ne[0] != target_pW || lq_latent->ne[1] != target_pH) {
|
|
||||||
lq_latent = ggml_interpolate(ctx->ggml_ctx,
|
|
||||||
lq_latent,
|
|
||||||
target_pW,
|
|
||||||
target_pH,
|
|
||||||
lq_latent->ne[2],
|
|
||||||
lq_latent->ne[3],
|
|
||||||
GGML_SCALE_MODE_NEAREST);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto feat = conv0->forward(ctx, lq_latent);
|
|
||||||
feat = ggml_silu_inplace(ctx->ggml_ctx, feat);
|
|
||||||
feat = conv2->forward(ctx, feat);
|
|
||||||
for (int i = 0; i < config.lq_num_res_blocks; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<PiDResBlock>(blocks["latent_proj." + std::to_string(3 + i)]);
|
|
||||||
feat = block->forward(ctx, feat);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t B = feat->ne[3];
|
|
||||||
int64_t C = feat->ne[2];
|
|
||||||
int64_t L = target_pH * target_pW;
|
|
||||||
auto tokens = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, feat, 2, 0, 1, 3));
|
|
||||||
tokens = ggml_reshape_3d(ctx->ggml_ctx, tokens, C, L, B);
|
|
||||||
|
|
||||||
int num_outputs = static_cast<int>((config.patch_depth + config.lq_interval - 1) / config.lq_interval);
|
|
||||||
std::vector<ggml_tensor*> outputs;
|
|
||||||
outputs.reserve(num_outputs);
|
|
||||||
for (int i = 0; i < num_outputs; ++i) {
|
|
||||||
auto head = std::dynamic_pointer_cast<Linear>(blocks["output_heads." + std::to_string(i)]);
|
|
||||||
outputs.push_back(head->forward(ctx, tokens));
|
|
||||||
}
|
|
||||||
return outputs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PixelDiT : public GGMLBlock {
|
|
||||||
PixelDiTConfig config;
|
|
||||||
|
|
||||||
PixelDiT() = default;
|
|
||||||
|
|
||||||
PixelDiT(const PixelDiTConfig& config)
|
|
||||||
: config(config) {
|
|
||||||
blocks["pixel_embedder"] = std::make_shared<PixelTokenEmbedder>(config.in_channels, config.pixel_hidden_size);
|
|
||||||
blocks["s_embedder"] = std::make_shared<PatchTokenEmbedder>(config.in_channels * config.patch_size * config.patch_size, config.hidden_size, false, true);
|
|
||||||
blocks["t_embedder"] = std::make_shared<PixelDiTTimestepEmbedder>(config.hidden_size);
|
|
||||||
blocks["y_embedder"] = std::make_shared<PatchTokenEmbedder>(config.txt_embed_dim, config.hidden_size, true, true);
|
|
||||||
for (int i = 0; i < config.patch_depth; ++i) {
|
|
||||||
blocks["patch_blocks." + std::to_string(i)] = std::make_shared<MMDiTBlockT2I>(config.hidden_size, config.num_groups, config.patch_mlp_hidden_dim);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < config.pixel_depth; ++i) {
|
|
||||||
blocks["pixel_blocks." + std::to_string(i)] = std::make_shared<PiTBlock>(config.pixel_hidden_size,
|
|
||||||
config.hidden_size,
|
|
||||||
config.patch_size,
|
|
||||||
config.pixel_attn_hidden_size,
|
|
||||||
config.pixel_num_groups);
|
|
||||||
}
|
|
||||||
blocks["final_layer"] = std::make_shared<FinalLayer>(config.pixel_hidden_size, config.in_channels);
|
|
||||||
blocks["lq_proj"] = std::make_shared<LQProjection2D>(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_params(ggml_context* ctx,
|
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
|
||||||
std::string prefix = "") override {
|
|
||||||
params["y_pos_embedding"] = ggml_new_tensor_3d(ctx, GGML_TYPE_F32, config.hidden_size, config.txt_max_length, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
|
||||||
ggml_tensor* x,
|
|
||||||
ggml_tensor* timesteps,
|
|
||||||
ggml_tensor* context,
|
|
||||||
ggml_tensor* lq_latent,
|
|
||||||
ggml_tensor* degrade_sigma,
|
|
||||||
ggml_tensor* pos_img,
|
|
||||||
ggml_tensor* pos_txt,
|
|
||||||
ggml_tensor* pixel_pos_full,
|
|
||||||
ggml_tensor* pixel_pos_comp) {
|
|
||||||
auto pixel_embedder = std::dynamic_pointer_cast<PixelTokenEmbedder>(blocks["pixel_embedder"]);
|
|
||||||
auto s_embedder = std::dynamic_pointer_cast<PatchTokenEmbedder>(blocks["s_embedder"]);
|
|
||||||
auto t_embedder = std::dynamic_pointer_cast<PixelDiTTimestepEmbedder>(blocks["t_embedder"]);
|
|
||||||
auto y_embedder = std::dynamic_pointer_cast<PatchTokenEmbedder>(blocks["y_embedder"]);
|
|
||||||
auto final_layer = std::dynamic_pointer_cast<FinalLayer>(blocks["final_layer"]);
|
|
||||||
auto lq_proj = std::dynamic_pointer_cast<LQProjection2D>(blocks["lq_proj"]);
|
|
||||||
|
|
||||||
int64_t W_orig = x->ne[0];
|
|
||||||
int64_t H_orig = x->ne[1];
|
|
||||||
x = DiT::pad_to_patch_size(ctx, x, static_cast<int>(config.patch_size), static_cast<int>(config.patch_size));
|
|
||||||
int64_t W = x->ne[0];
|
|
||||||
int64_t H = x->ne[1];
|
|
||||||
int64_t B = x->ne[3];
|
|
||||||
int64_t Hs = H / config.patch_size;
|
|
||||||
int64_t Ws = W / config.patch_size;
|
|
||||||
int64_t L = Hs * Ws;
|
|
||||||
int64_t P2 = config.patch_size * config.patch_size;
|
|
||||||
|
|
||||||
auto x_patches = DiT::patchify(ctx->ggml_ctx, x, static_cast<int>(config.patch_size), static_cast<int>(config.patch_size), true);
|
|
||||||
auto t_emb = t_embedder->forward(ctx, timesteps);
|
|
||||||
auto condition = ggml_silu(ctx->ggml_ctx, t_emb);
|
|
||||||
|
|
||||||
GGML_ASSERT(context != nullptr);
|
|
||||||
int64_t Ltxt = std::min<int64_t>(context->ne[1], config.txt_max_length);
|
|
||||||
auto y = ggml_ext_slice(ctx->ggml_ctx, context, 1, 0, Ltxt);
|
|
||||||
auto y_emb = y_embedder->forward(ctx, y);
|
|
||||||
auto y_pos = ggml_ext_slice(ctx->ggml_ctx, params["y_pos_embedding"], 1, 0, Ltxt);
|
|
||||||
y_emb = ggml_add(ctx->ggml_ctx, y_emb, y_pos);
|
|
||||||
|
|
||||||
std::vector<ggml_tensor*> lq_features = lq_proj->forward(ctx, lq_latent, Hs, Ws);
|
|
||||||
|
|
||||||
auto s = s_embedder->forward(ctx, x_patches);
|
|
||||||
|
|
||||||
for (int i = 0; i < config.patch_depth; ++i) {
|
|
||||||
if (lq_proj->is_gate_active(i)) {
|
|
||||||
int out_idx = lq_proj->get_output_index(i);
|
|
||||||
if (out_idx < static_cast<int>(lq_features.size())) {
|
|
||||||
s = lq_proj->gate(ctx, s, lq_features[out_idx], degrade_sigma, out_idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto block = std::dynamic_pointer_cast<MMDiTBlockT2I>(blocks["patch_blocks." + std::to_string(i)]);
|
|
||||||
auto out = block->forward(ctx,
|
|
||||||
s,
|
|
||||||
y_emb,
|
|
||||||
condition,
|
|
||||||
pos_img,
|
|
||||||
pos_txt);
|
|
||||||
s = out.first;
|
|
||||||
y_emb = out.second;
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(s, "pid.patch_blocks." + std::to_string(i), "s");
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(y_emb, "pid.patch_blocks." + std::to_string(i), "y");
|
|
||||||
}
|
|
||||||
s = ggml_silu(ctx->ggml_ctx, ggml_add(ctx->ggml_ctx, s, t_emb));
|
|
||||||
|
|
||||||
auto s_cond = ggml_reshape_2d(ctx->ggml_ctx, s, config.hidden_size, L * B);
|
|
||||||
auto pixels = pixel_embedder->forward(ctx, x, config.patch_size, pixel_pos_full);
|
|
||||||
for (int i = 0; i < config.pixel_depth; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<PiTBlock>(blocks["pixel_blocks." + std::to_string(i)]);
|
|
||||||
pixels = block->forward(ctx, pixels, s_cond, H, W, pixel_pos_comp);
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(pixels, "pid.pixel_blocks." + std::to_string(i), "pixels");
|
|
||||||
}
|
|
||||||
|
|
||||||
pixels = final_layer->forward(ctx, pixels);
|
|
||||||
pixels = ggml_reshape_3d(ctx->ggml_ctx, pixels, config.in_channels * P2, L, B);
|
|
||||||
auto out = DiT::unpatchify(ctx->ggml_ctx,
|
|
||||||
pixels,
|
|
||||||
Hs,
|
|
||||||
Ws,
|
|
||||||
static_cast<int>(config.patch_size),
|
|
||||||
static_cast<int>(config.patch_size),
|
|
||||||
false);
|
|
||||||
out = ggml_ext_slice(ctx->ggml_ctx, out, 1, 0, H_orig);
|
|
||||||
out = ggml_ext_slice(ctx->ggml_ctx, out, 0, 0, W_orig);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PiDRunner : public DiffusionModelRunner {
|
|
||||||
PixelDiTConfig config;
|
|
||||||
PixelDiT model;
|
|
||||||
std::vector<float> pos_img_vec;
|
|
||||||
std::vector<float> pos_txt_vec;
|
|
||||||
std::vector<float> pixel_pos_vec;
|
|
||||||
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),
|
|
||||||
config(PixelDiTConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
|
||||||
model = PixelDiT(config);
|
|
||||||
model.init(params_ctx, tensor_storage_map, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_desc() override {
|
|
||||||
return "PiD";
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
|
|
||||||
model.get_param_tensors(tensors, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
|
||||||
const sd::Tensor<float>& timesteps_tensor,
|
|
||||||
const sd::Tensor<float>& context_tensor,
|
|
||||||
const sd::Tensor<float>& lq_latent_tensor,
|
|
||||||
const sd::Tensor<float>& degrade_sigma_tensor) {
|
|
||||||
ggml_cgraph* gf = new_graph_custom(PID_GRAPH_SIZE);
|
|
||||||
ggml_tensor* x = make_input(x_tensor);
|
|
||||||
ggml_tensor* timesteps = make_input(timesteps_tensor);
|
|
||||||
ggml_tensor* context = make_input(context_tensor);
|
|
||||||
ggml_tensor* lq_latent = make_input(lq_latent_tensor);
|
|
||||||
ggml_tensor* degrade_sigma = make_input(degrade_sigma_tensor);
|
|
||||||
|
|
||||||
int64_t W = x->ne[0];
|
|
||||||
int64_t H = x->ne[1];
|
|
||||||
int64_t B = x->ne[3];
|
|
||||||
int64_t Wp = align_up(static_cast<int>(W), static_cast<int>(config.patch_size));
|
|
||||||
int64_t Hp = align_up(static_cast<int>(H), static_cast<int>(config.patch_size));
|
|
||||||
int64_t Hs = Hp / config.patch_size;
|
|
||||||
int64_t Ws = Wp / config.patch_size;
|
|
||||||
|
|
||||||
pos_img_vec = make_rope_2d(static_cast<int>(Hs),
|
|
||||||
static_cast<int>(Ws),
|
|
||||||
static_cast<int>(config.hidden_size / config.num_groups),
|
|
||||||
10000.f,
|
|
||||||
16.f,
|
|
||||||
static_cast<int>(config.rope_ref_grid_h),
|
|
||||||
static_cast<int>(config.rope_ref_grid_w));
|
|
||||||
auto pos_img = ggml_new_tensor_4d(compute_ctx,
|
|
||||||
GGML_TYPE_F32,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
config.hidden_size / config.num_groups / 2,
|
|
||||||
Hs * Ws);
|
|
||||||
set_backend_tensor_data(pos_img, pos_img_vec.data());
|
|
||||||
|
|
||||||
int64_t Ltxt = std::min<int64_t>(context->ne[1], config.txt_max_length);
|
|
||||||
pos_txt_vec = make_rope_1d(static_cast<int>(Ltxt),
|
|
||||||
static_cast<int>(config.hidden_size / config.num_groups),
|
|
||||||
config.text_rope_theta);
|
|
||||||
auto pos_txt = ggml_new_tensor_4d(compute_ctx,
|
|
||||||
GGML_TYPE_F32,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
config.hidden_size / config.num_groups / 2,
|
|
||||||
Ltxt);
|
|
||||||
set_backend_tensor_data(pos_txt, pos_txt_vec.data());
|
|
||||||
|
|
||||||
pixel_pos_vec = make_pixel_abs_pos(static_cast<int>(Hp),
|
|
||||||
static_cast<int>(Wp),
|
|
||||||
static_cast<int>(config.pixel_hidden_size));
|
|
||||||
auto pixel_pos = ggml_new_tensor_3d(compute_ctx,
|
|
||||||
GGML_TYPE_F32,
|
|
||||||
config.pixel_hidden_size,
|
|
||||||
Wp * Hp,
|
|
||||||
1);
|
|
||||||
set_backend_tensor_data(pixel_pos, pixel_pos_vec.data());
|
|
||||||
|
|
||||||
pixel_pos_comp_vec = make_rope_2d(static_cast<int>(Hs),
|
|
||||||
static_cast<int>(Ws),
|
|
||||||
static_cast<int>(config.pixel_attn_hidden_size / config.pixel_num_groups),
|
|
||||||
10000.f,
|
|
||||||
16.f,
|
|
||||||
static_cast<int>(config.rope_ref_grid_h),
|
|
||||||
static_cast<int>(config.rope_ref_grid_w));
|
|
||||||
auto pixel_pos_comp = ggml_new_tensor_4d(compute_ctx,
|
|
||||||
GGML_TYPE_F32,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
config.pixel_attn_hidden_size / config.pixel_num_groups / 2,
|
|
||||||
Hs * Ws);
|
|
||||||
set_backend_tensor_data(pixel_pos_comp, pixel_pos_comp_vec.data());
|
|
||||||
|
|
||||||
auto runner_ctx = get_context();
|
|
||||||
auto out = model.forward(&runner_ctx,
|
|
||||||
x,
|
|
||||||
timesteps,
|
|
||||||
context,
|
|
||||||
lq_latent,
|
|
||||||
degrade_sigma,
|
|
||||||
pos_img,
|
|
||||||
pos_txt,
|
|
||||||
pixel_pos,
|
|
||||||
pixel_pos_comp);
|
|
||||||
ggml_build_forward_expand(gf, out);
|
|
||||||
return gf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const sd::Tensor<float>& x,
|
|
||||||
const sd::Tensor<float>& timesteps,
|
|
||||||
const sd::Tensor<float>& context,
|
|
||||||
const sd::Tensor<float>& lq_latent,
|
|
||||||
const sd::Tensor<float>& degrade_sigma) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(int n_threads,
|
|
||||||
const DiffusionParams& diffusion_params) override {
|
|
||||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.context != nullptr);
|
|
||||||
GGML_ASSERT(diffusion_params.ref_latents != nullptr);
|
|
||||||
GGML_ASSERT(!diffusion_params.ref_latents->empty());
|
|
||||||
auto degrade_sigma = sd::Tensor<float>::from_vector({0.0f});
|
|
||||||
return compute(n_threads,
|
|
||||||
*diffusion_params.x,
|
|
||||||
*diffusion_params.timesteps,
|
|
||||||
*diffusion_params.context,
|
|
||||||
diffusion_params.ref_latents->front(),
|
|
||||||
degrade_sigma);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Pid
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_DIFFUSION_PID_HPP__
|
|
||||||
File diff suppressed because it is too large
Load Diff
2104
src/model/te/llm.hpp
2104
src/model/te/llm.hpp
File diff suppressed because it is too large
Load Diff
@ -1,551 +0,0 @@
|
|||||||
#ifndef __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
|
||||||
#define __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
|
||||||
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/ggml_extend.hpp"
|
|
||||||
#include "core/ggml_graph_cut.h"
|
|
||||||
#include "core/util.h"
|
|
||||||
#include "model/diffusion/dit.hpp"
|
|
||||||
#include "model_loader.h"
|
|
||||||
|
|
||||||
namespace LTXVUpsampler {
|
|
||||||
constexpr int LTX_UPSAMPLER_GRAPH_SIZE = 10240;
|
|
||||||
|
|
||||||
struct LatentUpsamplerConfig {
|
|
||||||
int64_t in_channels = 128;
|
|
||||||
int64_t mid_channels = 1024;
|
|
||||||
int num_blocks_per_stage = 4;
|
|
||||||
int dims = 3;
|
|
||||||
bool spatial_upsample = true;
|
|
||||||
bool temporal_upsample = false;
|
|
||||||
bool rational_resampler = false;
|
|
||||||
float spatial_scale = 2.f;
|
|
||||||
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 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
class VideoGroupNorm : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
int num_groups;
|
|
||||||
int64_t num_channels;
|
|
||||||
float eps;
|
|
||||||
std::string prefix;
|
|
||||||
|
|
||||||
void init_params(ggml_context* ctx,
|
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
|
||||||
const std::string prefix = "") override {
|
|
||||||
SD_UNUSED(tensor_storage_map);
|
|
||||||
this->prefix = prefix;
|
|
||||||
params["weight"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, num_channels);
|
|
||||||
params["bias"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, num_channels);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
VideoGroupNorm(int num_groups, int64_t num_channels, float eps = 1e-05f)
|
|
||||||
: num_groups(num_groups),
|
|
||||||
num_channels(num_channels),
|
|
||||||
eps(eps) {}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
// LTX video latent layout is [W, H, T, C]. ggml_group_norm treats ne[2]
|
|
||||||
// as channels, so fold only H/T internally and restore the same layout.
|
|
||||||
GGML_ASSERT(x->ne[3] == num_channels);
|
|
||||||
const int64_t W = x->ne[0];
|
|
||||||
const int64_t H = x->ne[1];
|
|
||||||
const int64_t T = x->ne[2];
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, x);
|
|
||||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H * T, num_channels, 1);
|
|
||||||
x = ggml_group_norm(ctx->ggml_ctx, x, num_groups, eps);
|
|
||||||
|
|
||||||
ggml_tensor* weight = params["weight"];
|
|
||||||
ggml_tensor* bias = params["bias"];
|
|
||||||
if (ctx->weight_adapter) {
|
|
||||||
weight = ctx->weight_adapter->patch_weight(ctx->ggml_ctx, ctx->backend, weight, prefix + "weight");
|
|
||||||
bias = ctx->weight_adapter->patch_weight(ctx->ggml_ctx, ctx->backend, bias, prefix + "bias");
|
|
||||||
}
|
|
||||||
weight = ggml_reshape_4d(ctx->ggml_ctx, weight, 1, 1, num_channels, 1);
|
|
||||||
bias = ggml_reshape_4d(ctx->ggml_ctx, bias, 1, 1, num_channels, 1);
|
|
||||||
x = ggml_mul_inplace(ctx->ggml_ctx, x, weight);
|
|
||||||
x = ggml_add_inplace(ctx->ggml_ctx, x, bias);
|
|
||||||
return ggml_reshape_4d(ctx->ggml_ctx, x, W, H, T, num_channels);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ResBlock : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
ResBlock(int64_t channels, int dims = 3) {
|
|
||||||
GGML_ASSERT(dims == 3);
|
|
||||||
blocks["conv1"] = std::shared_ptr<GGMLBlock>(new Conv3d(channels, channels, {3, 3, 3}, {1, 1, 1}, {1, 1, 1}));
|
|
||||||
blocks["norm1"] = std::shared_ptr<GGMLBlock>(new VideoGroupNorm(32, channels));
|
|
||||||
blocks["conv2"] = std::shared_ptr<GGMLBlock>(new Conv3d(channels, channels, {3, 3, 3}, {1, 1, 1}, {1, 1, 1}));
|
|
||||||
blocks["norm2"] = std::shared_ptr<GGMLBlock>(new VideoGroupNorm(32, channels));
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
auto conv1 = std::dynamic_pointer_cast<Conv3d>(blocks["conv1"]);
|
|
||||||
auto norm1 = std::dynamic_pointer_cast<VideoGroupNorm>(blocks["norm1"]);
|
|
||||||
auto conv2 = std::dynamic_pointer_cast<Conv3d>(blocks["conv2"]);
|
|
||||||
auto norm2 = std::dynamic_pointer_cast<VideoGroupNorm>(blocks["norm2"]);
|
|
||||||
|
|
||||||
ggml_tensor* residual = x;
|
|
||||||
|
|
||||||
x = conv1->forward(ctx, x);
|
|
||||||
x = norm1->forward(ctx, x);
|
|
||||||
x = ggml_silu_inplace(ctx->ggml_ctx, x);
|
|
||||||
x = conv2->forward(ctx, x);
|
|
||||||
x = norm2->forward(ctx, x);
|
|
||||||
x = ggml_add(ctx->ggml_ctx, x, residual);
|
|
||||||
return ggml_silu(ctx->ggml_ctx, x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PixelShuffleND : public UnaryBlock {
|
|
||||||
protected:
|
|
||||||
int upscale_factor;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PixelShuffleND(int upscale_factor)
|
|
||||||
: upscale_factor(upscale_factor) {}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
||||||
GGML_ASSERT(upscale_factor > 0);
|
|
||||||
int64_t h = x->ne[1];
|
|
||||||
int64_t w = x->ne[0];
|
|
||||||
GGML_ASSERT(x->ne[2] % (upscale_factor * upscale_factor) == 0);
|
|
||||||
// x: [b*f, c*p1*p2, h, w] -> [b*f, c, h*p1, w*p2]
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 2, 0, 1, 3)); // [b*f, h, w, c*p1*p2]
|
|
||||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, x->ne[0], x->ne[1] * x->ne[2], x->ne[3]); // [b*f, h*w, c*p1*p2]
|
|
||||||
return DiT::unpatchify(ctx->ggml_ctx, x, h, w, upscale_factor, upscale_factor, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TemporalPixelShuffleND : public UnaryBlock {
|
|
||||||
protected:
|
|
||||||
int upscale_factor;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TemporalPixelShuffleND(int upscale_factor)
|
|
||||||
: upscale_factor(upscale_factor) {}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
||||||
GGML_ASSERT(upscale_factor > 0);
|
|
||||||
GGML_ASSERT(x->ne[3] % upscale_factor == 0);
|
|
||||||
const int64_t W = x->ne[0];
|
|
||||||
const int64_t H = x->ne[1];
|
|
||||||
const int64_t F = x->ne[2];
|
|
||||||
const int64_t C = x->ne[3] / upscale_factor;
|
|
||||||
|
|
||||||
// x: [b, c*p, f, h, w] -> [b, c, f*p, h, w]
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, x);
|
|
||||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W * H, F, upscale_factor, C);
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 2, 1, 3));
|
|
||||||
return ggml_reshape_4d(ctx->ggml_ctx, x, W, H, F * upscale_factor, C);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlurDownsample : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
int64_t channels;
|
|
||||||
int stride;
|
|
||||||
ggml_tensor* kernel = nullptr;
|
|
||||||
std::vector<float> kernel_data;
|
|
||||||
|
|
||||||
void init_params(ggml_context* ctx,
|
|
||||||
const String2TensorStorage& tensor_storage_map = {},
|
|
||||||
const std::string prefix = "") override {
|
|
||||||
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());
|
|
||||||
|
|
||||||
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) {
|
|
||||||
for (int y = 0; y < 5; ++y) {
|
|
||||||
for (int x = 0; x < 5; ++x) {
|
|
||||||
kernel_data[static_cast<size_t>(x + 5 * (y + 5 * c))] =
|
|
||||||
binomial[y] * binomial[x] / 256.f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(x->ne[2] == channels);
|
|
||||||
if (ctx->conv2d_direct_enabled) {
|
|
||||||
return ggml_conv_2d_dw_direct(ctx->ggml_ctx, kernel, x, stride, stride, 2, 2, 1, 1);
|
|
||||||
}
|
|
||||||
return ggml_conv_2d_dw(ctx->ggml_ctx, kernel, x, stride, stride, 2, 2, 1, 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SpatialRationalResampler : public GGMLBlock {
|
|
||||||
protected:
|
|
||||||
int64_t mid_channels;
|
|
||||||
int num;
|
|
||||||
int den;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SpatialRationalResampler(int64_t mid_channels, int num, int den)
|
|
||||||
: mid_channels(mid_channels),
|
|
||||||
num(num),
|
|
||||||
den(den) {
|
|
||||||
GGML_ASSERT(num >= 1);
|
|
||||||
GGML_ASSERT(den >= 1);
|
|
||||||
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(mid_channels, num * num * mid_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
||||||
blocks["pixel_shuffle"] = std::shared_ptr<GGMLBlock>(new PixelShuffleND(num));
|
|
||||||
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"]);
|
|
||||||
auto blur_down = std::dynamic_pointer_cast<BlurDownsample>(blocks["blur_down"]);
|
|
||||||
|
|
||||||
// rearrange(x, "b c f h w -> (b f) c h w")
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
|
||||||
x = conv->forward(ctx, x);
|
|
||||||
x = pixel_shuffle->forward(ctx, x);
|
|
||||||
x = blur_down->forward(ctx, x);
|
|
||||||
return ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class LatentUpsampler : public GGMLBlock {
|
|
||||||
public:
|
|
||||||
LatentUpsamplerConfig config;
|
|
||||||
|
|
||||||
explicit LatentUpsampler(LatentUpsamplerConfig config)
|
|
||||||
: config(std::move(config)) {
|
|
||||||
GGML_ASSERT(this->config.dims == 3);
|
|
||||||
GGML_ASSERT(this->config.spatial_upsample || this->config.temporal_upsample);
|
|
||||||
|
|
||||||
blocks["initial_conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(this->config.in_channels,
|
|
||||||
this->config.mid_channels,
|
|
||||||
{3, 3, 3},
|
|
||||||
{1, 1, 1},
|
|
||||||
{1, 1, 1}));
|
|
||||||
blocks["initial_norm"] = std::shared_ptr<GGMLBlock>(new VideoGroupNorm(32, this->config.mid_channels));
|
|
||||||
for (int i = 0; i < this->config.num_blocks_per_stage; ++i) {
|
|
||||||
blocks["res_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new ResBlock(this->config.mid_channels, this->config.dims));
|
|
||||||
}
|
|
||||||
if (this->config.rational_resampler) {
|
|
||||||
blocks["upsampler"] = std::shared_ptr<GGMLBlock>(new SpatialRationalResampler(this->config.mid_channels,
|
|
||||||
this->config.spatial_up_num,
|
|
||||||
this->config.spatial_down_den));
|
|
||||||
} else if (this->config.temporal_upsample) {
|
|
||||||
blocks["upsampler.0"] = std::shared_ptr<GGMLBlock>(new Conv3d(this->config.mid_channels,
|
|
||||||
this->config.temporal_up_factor * this->config.mid_channels,
|
|
||||||
{3, 3, 3},
|
|
||||||
{1, 1, 1},
|
|
||||||
{1, 1, 1}));
|
|
||||||
blocks["upsampler.1"] = std::shared_ptr<GGMLBlock>(new TemporalPixelShuffleND(this->config.temporal_up_factor));
|
|
||||||
} else {
|
|
||||||
blocks["upsampler.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(this->config.mid_channels,
|
|
||||||
4 * this->config.mid_channels,
|
|
||||||
{3, 3},
|
|
||||||
{1, 1},
|
|
||||||
{1, 1}));
|
|
||||||
blocks["upsampler.1"] = std::shared_ptr<GGMLBlock>(new PixelShuffleND(2));
|
|
||||||
}
|
|
||||||
for (int i = 0; i < this->config.num_blocks_per_stage; ++i) {
|
|
||||||
blocks["post_upsample_res_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new ResBlock(this->config.mid_channels, this->config.dims));
|
|
||||||
}
|
|
||||||
blocks["final_conv"] = std::shared_ptr<GGMLBlock>(new Conv3d(this->config.mid_channels,
|
|
||||||
this->config.in_channels,
|
|
||||||
{3, 3, 3},
|
|
||||||
{1, 1, 1},
|
|
||||||
{1, 1, 1}));
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
||||||
// x: [b, c, f, h, w]
|
|
||||||
// return: [b, c, scaled_f, scaled_h, scaled_w]
|
|
||||||
auto initial_conv = std::dynamic_pointer_cast<Conv3d>(blocks["initial_conv"]);
|
|
||||||
auto initial_norm = std::dynamic_pointer_cast<VideoGroupNorm>(blocks["initial_norm"]);
|
|
||||||
auto final_conv = std::dynamic_pointer_cast<Conv3d>(blocks["final_conv"]);
|
|
||||||
|
|
||||||
x = initial_conv->forward(ctx, x);
|
|
||||||
x = initial_norm->forward(ctx, x);
|
|
||||||
x = ggml_silu(ctx->ggml_ctx, x);
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "ltx_latent_upsampler.initial", "x");
|
|
||||||
|
|
||||||
for (int i = 0; i < config.num_blocks_per_stage; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<ResBlock>(blocks["res_blocks." + std::to_string(i)]);
|
|
||||||
x = block->forward(ctx, x);
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "ltx_latent_upsampler.res_blocks." + std::to_string(i), "x");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.rational_resampler) {
|
|
||||||
auto upsampler = std::dynamic_pointer_cast<SpatialRationalResampler>(blocks["upsampler"]);
|
|
||||||
x = upsampler->forward(ctx, x);
|
|
||||||
} else if (config.temporal_upsample) {
|
|
||||||
auto upsample_conv = std::dynamic_pointer_cast<Conv3d>(blocks["upsampler.0"]);
|
|
||||||
auto pixel_shuffle = std::dynamic_pointer_cast<TemporalPixelShuffleND>(blocks["upsampler.1"]);
|
|
||||||
x = upsample_conv->forward(ctx, x); // [b, c*2, f, h, w]
|
|
||||||
x = pixel_shuffle->forward(ctx, x); // [b, c, f*2, h, w]
|
|
||||||
x = ggml_ext_slice(ctx->ggml_ctx, x, 2, 1, x->ne[2]); // x[:, :, 1:, :, :]
|
|
||||||
} else {
|
|
||||||
auto upsample_conv = std::dynamic_pointer_cast<Conv2d>(blocks["upsampler.0"]);
|
|
||||||
auto pixel_shuffle = std::dynamic_pointer_cast<PixelShuffleND>(blocks["upsampler.1"]);
|
|
||||||
|
|
||||||
// rearrange(x, "b c f h w -> (b f) c h w"),
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2)); // [b*f, c, h, w]
|
|
||||||
x = upsample_conv->forward(ctx, x); // [b*f, c*4, h, w]
|
|
||||||
x = pixel_shuffle->forward(ctx, x); // [b*f, c, h*2, w*2]
|
|
||||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2)); // [b*c, f, h, w]
|
|
||||||
}
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "ltx_latent_upsampler.spatial_up", "x");
|
|
||||||
|
|
||||||
for (int i = 0; i < config.num_blocks_per_stage; ++i) {
|
|
||||||
auto block = std::dynamic_pointer_cast<ResBlock>(blocks["post_upsample_res_blocks." + std::to_string(i)]);
|
|
||||||
x = block->forward(ctx, x);
|
|
||||||
sd::ggml_graph_cut::mark_graph_cut(x, "ltx_latent_upsampler.post_blocks." + std::to_string(i), "x");
|
|
||||||
}
|
|
||||||
|
|
||||||
x = final_conv->forward(ctx, x);
|
|
||||||
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 {
|
|
||||||
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);
|
|
||||||
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",
|
|
||||||
config.dims,
|
|
||||||
config.spatial_upsample,
|
|
||||||
config.temporal_upsample,
|
|
||||||
config.rational_resampler,
|
|
||||||
config.spatial_scale,
|
|
||||||
config.temporal_up_factor);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor) {
|
|
||||||
if (!model) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
ggml_cgraph* gf = new_graph_custom(LTX_UPSAMPLER_GRAPH_SIZE);
|
|
||||||
ggml_tensor* x = make_input(x_tensor);
|
|
||||||
auto runner_ctx = get_context();
|
|
||||||
ggml_tensor* out = model->forward(&runner_ctx, x);
|
|
||||||
ggml_build_forward_expand(gf, out);
|
|
||||||
return gf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sd::Tensor<float> compute(const int n_threads,
|
|
||||||
const sd::Tensor<float>& x) {
|
|
||||||
if (!model) {
|
|
||||||
LOG_ERROR("LTX latent upsampler is not loaded");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (x.dim() != 4 && x.dim() != 5) {
|
|
||||||
LOG_ERROR("LTX latent upsampler expects 4D or 5D video latent, got dim=%lld",
|
|
||||||
(long long)x.dim());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (x.dim() == 5 && x.shape()[4] != 1) {
|
|
||||||
LOG_ERROR("LTX latent upsampler currently supports batch size 1, got batch=%lld",
|
|
||||||
(long long)x.shape()[4]);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (x.shape()[3] != model->config.in_channels) {
|
|
||||||
LOG_ERROR("LTX latent upsampler expected %" PRId64 " channels, got %lld",
|
|
||||||
model->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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace LTXVUpsampler
|
|
||||||
|
|
||||||
#endif // __SD_MODEL_UPSCALER_LTX_LATENT_UPSCALER_HPP__
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user