mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-10-02 10:24:37 +08:00
Compare commits
142
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64a7698347 | ||
|
|
0723ee51c9 | ||
|
|
90ef5f8246 | ||
|
|
db6f4791b4 | ||
|
|
b25785bc10 | ||
|
|
0585e2609d | ||
|
|
683d6d08a8 | ||
|
|
40a6a8710e | ||
|
|
e3702585cb | ||
|
|
a7d6d296c7 | ||
|
|
2e9242e37f | ||
|
|
c64994dc1d | ||
|
|
5436f6b814 | ||
|
|
1c32fa03bc | ||
|
|
9727c6bb98 | ||
|
|
beb99a2de2 | ||
|
|
aa68b875b9 | ||
|
|
5b261b9cee | ||
|
|
e70d0205ca | ||
|
|
02af48a97f | ||
|
|
e12d5e0aaf | ||
|
|
940a2018e1 | ||
|
|
b451728b2f | ||
|
|
11f436c483 | ||
|
|
35843c77ea | ||
|
|
6ad46bb700 | ||
|
|
1ba30ce005 | ||
|
|
2abe9451c4 | ||
|
|
f3140eadbb | ||
|
|
98ba155fc6 | ||
|
|
513f36d495 | ||
|
|
1e0d2821bb | ||
|
|
fd693ac6a2 | ||
|
|
171b2222a5 | ||
|
|
567f9f14f0 | ||
|
|
1e5f207006 | ||
|
|
79426d578e | ||
|
|
97ad3e7ff9 | ||
|
|
8909523e92 | ||
|
|
8376dfba2a | ||
|
|
0ebe6fe118 | ||
|
|
55c2e05d98 | ||
|
|
52a97b3ac1 | ||
|
|
2c9b1e2594 | ||
|
|
288e2d63c0 | ||
|
|
dc46993b55 | ||
|
|
a6a8569ea0 | ||
|
|
9e7befa320 | ||
|
|
c607fc3ed4 | ||
|
|
b54bec3f18 | ||
|
|
5869987fe4 | ||
|
|
48956ffb87 | ||
|
|
ddc4a18b92 | ||
|
|
fce6afcc6a | ||
|
|
49d6570c43 | ||
|
|
6bbaf161ad | ||
|
|
87cdbd5978 | ||
|
|
b017918106 | ||
|
|
ac5a215998 | ||
|
|
abb36d66b5 | ||
|
|
ff4fdbb88d | ||
|
|
abb115cd02 | ||
|
|
c648001030 | ||
|
|
c587a43c99 | ||
|
|
f8fe4e7db9 | ||
|
|
1c07fb6fb1 | ||
|
|
675208dcb6 | ||
|
|
d7f430cd69 | ||
|
|
141a4b4113 | ||
|
|
21ce9fe2cf | ||
|
|
cb1d975e96 | ||
|
|
2eb3845df5 | ||
|
|
4c6475f917 | ||
|
|
f0fa7ddc40 | ||
|
|
a7c7905c6d | ||
|
|
eea77cbad9 | ||
|
|
0e86d90ee4 | ||
|
|
5900ef6605 | ||
|
|
5b8996f74a | ||
|
|
f7f05fb185 | ||
|
|
6167e2927a | ||
|
|
f6b9aa1a43 | ||
|
|
7eb30d00e5 | ||
|
|
59080d3ce1 | ||
|
|
8c3c788f31 | ||
|
|
f54524f620 | ||
|
|
eed97a5e1d | ||
|
|
fb86bf4cb0 | ||
|
|
bd1eaef93e | ||
|
|
ab835f7d39 | ||
|
|
26f3f61d37 | ||
|
|
1896b28ef2 | ||
|
|
0739361bfe | ||
|
|
ca0bd9396e | ||
|
|
a772dca27a | ||
|
|
6d84a30c66 | ||
|
|
dafc32d0dd | ||
|
|
225162f270 | ||
|
|
b9e4718fac | ||
|
|
1ce1c1adca | ||
|
|
19fbfd8639 | ||
|
|
76c72628b1 | ||
|
|
3bae667f3d | ||
|
|
8d0819c548 | ||
|
|
7a8ff2e819 | ||
|
|
0927e8e322 | ||
|
|
83ef4e44ce | ||
|
|
7dac89ad75 | ||
|
|
9251756086 | ||
|
|
ecf5db97ae | ||
|
|
ea46fd6948 | ||
|
|
23de7fc44a | ||
|
|
d42fd59464 | ||
|
|
0d8b39f0ba | ||
|
|
539b5b9374 | ||
|
|
b1fc16b504 | ||
|
|
d6c87dce5c | ||
|
|
a28d04dd81 | ||
|
|
45d0ebb30c | ||
|
|
b1cc40c35c | ||
|
|
884e23eeeb | ||
|
|
c9b5735116 | ||
|
|
10c6501bd0 | ||
|
|
10feacf031 | ||
|
|
655f8a5169 | ||
|
|
d7c7a34712 | ||
|
|
81556f3136 | ||
|
|
3fb275a67b | ||
|
|
30b3ac8e62 | ||
|
|
195d170136 | ||
|
|
f50a7f66aa | ||
|
|
85e9a12988 | ||
|
|
fbd42b6fc1 | ||
|
|
19d876ee30 | ||
|
|
f27f2b2aa2 | ||
|
|
99609761dc | ||
|
|
69c73789fe | ||
|
|
838beb9b5e | ||
|
|
f23b803a6b | ||
|
|
1be2491dcf | ||
|
|
3753223982 | ||
|
|
59ca2b0f16 |
@@ -0,0 +1,73 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Report a bug or unexpected behavior
|
||||
title: "[Bug] "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please use this template and include as many details as possible to help us reproduce and fix the issue.
|
||||
- type: textarea
|
||||
id: commit
|
||||
attributes:
|
||||
label: Git commit
|
||||
description: Which commit are you trying to compile?
|
||||
placeholder: |
|
||||
$git rev-parse HEAD
|
||||
40a6a8710ec15b1b5db6b5a098409f6bc8f654a4
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System & Version
|
||||
placeholder: e.g. “Ubuntu 22.04”, “Windows 11 23H2”, “macOS 14.3”
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: backends
|
||||
attributes:
|
||||
label: GGML backends
|
||||
description: Which GGML backends do you know to be affected?
|
||||
options: [CPU, CUDA, HIP, Metal, Musa, SYCL, Vulkan, OpenCL]
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: cmd_arguments
|
||||
attributes:
|
||||
label: Command-line arguments used
|
||||
placeholder: The full command line you ran (with all flags)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps_to_reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
placeholder: A step-by-step list of what you did
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: What you expected to happen
|
||||
placeholder: Describe the expected behavior or result
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: actual_behavior
|
||||
attributes:
|
||||
label: What actually happened
|
||||
placeholder: Describe what you saw instead (errors, logs, crash, etc.)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs_and_errors
|
||||
attributes:
|
||||
label: Logs / error messages / stack trace
|
||||
placeholder: Paste complete logs or error output
|
||||
- type: textarea
|
||||
id: additional_info
|
||||
attributes:
|
||||
label: Additional context / environment details
|
||||
placeholder: e.g. CPU model, GPU, RAM, model file versions, quantization type, etc.
|
||||
@@ -0,0 +1,33 @@
|
||||
name: 💡 Feature Request
|
||||
description: Suggest a new feature or improvement
|
||||
title: "[Feature] "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for suggesting an improvement! Please fill in the fields below.
|
||||
- type: input
|
||||
id: summary
|
||||
attributes:
|
||||
label: Feature Summary
|
||||
placeholder: A one-line summary of the feature you’d like
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Detailed Description
|
||||
placeholder: What problem does this solve? How do you expect it to work?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives you considered
|
||||
placeholder: Any alternative designs or workarounds you tried
|
||||
- type: textarea
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Any extra information (use cases, related functionalities, constraints)
|
||||
@@ -146,10 +146,10 @@ jobs:
|
||||
sd-${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}-bin-${{ steps.system-info.outputs.OS_TYPE }}-${{ steps.system-info.outputs.OS_NAME }}-${{ steps.system-info.outputs.OS_VERSION }}-${{ steps.system-info.outputs.CPU_ARCH }}.zip
|
||||
|
||||
windows-latest-cmake:
|
||||
runs-on: windows-2019
|
||||
runs-on: windows-2025
|
||||
|
||||
env:
|
||||
VULKAN_VERSION: 1.3.261.1
|
||||
VULKAN_VERSION: 1.4.328.1
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
- build: "avx512"
|
||||
defines: "-DGGML_NATIVE=OFF -DGGML_AVX512=ON -DGGML_AVX=ON -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON"
|
||||
- build: "cuda12"
|
||||
defines: "-DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_CUDA_ARCHITECTURES=90;89;80;75"
|
||||
defines: "-DSD_CUDA=ON -DSD_BUILD_SHARED_LIBS=ON -DCMAKE_CUDA_ARCHITECTURES=90;89;86;80;75"
|
||||
# - build: "rocm5.5"
|
||||
# defines: '-G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS="gfx1100;gfx1102;gfx1030" -DSD_BUILD_SHARED_LIBS=ON'
|
||||
- build: 'vulkan'
|
||||
@@ -199,9 +199,9 @@ jobs:
|
||||
version: 1.11.1
|
||||
- name: Install Vulkan SDK
|
||||
id: get_vulkan
|
||||
if: ${{ matrix.build == 'vulkan' }}
|
||||
if: ${{ matrix.build == 'vulkan' }} https://sdk.lunarg.com/sdk/download/1.4.328.1/windows/vulkansdk-windows-X64-1.4.328.1.exe
|
||||
run: |
|
||||
curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/VulkanSDK-${env:VULKAN_VERSION}-Installer.exe"
|
||||
curl.exe -o $env:RUNNER_TEMP/VulkanSDK-Installer.exe -L "https://sdk.lunarg.com/sdk/download/${env:VULKAN_VERSION}/windows/vulkansdk-windows-X64-${env:VULKAN_VERSION}.exe"
|
||||
& "$env:RUNNER_TEMP\VulkanSDK-Installer.exe" --accept-licenses --default-answer --confirm-command install
|
||||
Add-Content $env:GITHUB_ENV "VULKAN_SDK=C:\VulkanSDK\${env:VULKAN_VERSION}"
|
||||
Add-Content $env:GITHUB_PATH "C:\VulkanSDK\${env:VULKAN_VERSION}\bin"
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
|
||||
- name: Copy and pack Cuda runtime
|
||||
id: pack_cuda_runtime
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.build == 'cuda12' ) || github.event.inputs.create_release == 'true' }}
|
||||
if: ${{ matrix.build == 'cuda12' && (github.event_name == 'push' && github.ref == 'refs/heads/master' || github.event.inputs.create_release == 'true') }}
|
||||
run: |
|
||||
echo "Cuda install location: ${{steps.cuda-toolkit.outputs.CUDA_PATH}}"
|
||||
$dst='.\build\bin\cudart\'
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
7z a cudart-sd-bin-win-cu12-x64.zip $dst\*
|
||||
|
||||
- name: Upload Cuda runtime
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.build == 'cuda12' ) || github.event.inputs.create_release == 'true' }}
|
||||
if: ${{ matrix.build == 'cuda12' && (github.event_name == 'push' && github.ref == 'refs/heads/master' || github.event.inputs.create_release == 'true') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sd-cudart-sd-bin-win-cu12-x64.zip
|
||||
@@ -288,6 +288,11 @@ jobs:
|
||||
- windows-latest-cmake
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
id: download-artifact
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -296,20 +301,27 @@ jobs:
|
||||
pattern: sd-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Get commit count
|
||||
id: commit_count
|
||||
run: |
|
||||
echo "count=$(git rev-list --count HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get commit hash
|
||||
id: commit
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'master' }}
|
||||
uses: anzz1/action-create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ env.BRANCH_NAME }}-${{ steps.commit.outputs.short }}
|
||||
tag_name: ${{ format('{0}-{1}-{2}', env.BRANCH_NAME, steps.commit_count.outputs.count, steps.commit.outputs.short) }}
|
||||
|
||||
- name: Upload release
|
||||
id: upload_release
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || github.ref_name == 'master' }}
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
+3
-2
@@ -1,13 +1,14 @@
|
||||
build*/
|
||||
cmake-build-*/
|
||||
test/
|
||||
.vscode/
|
||||
.idea/
|
||||
.cache/
|
||||
*.swp
|
||||
.vscode/
|
||||
*.bat
|
||||
*.bin
|
||||
*.exe
|
||||
*.gguf
|
||||
output*.png
|
||||
models*
|
||||
*.log
|
||||
*.log
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "ggml"]
|
||||
path = ggml
|
||||
url = https://github.com/ggerganov/ggml.git
|
||||
url = https://github.com/ggml-org/ggml.git
|
||||
|
||||
+34
-7
@@ -28,10 +28,13 @@ option(SD_CUDA "sd: cuda backend" OFF)
|
||||
option(SD_HIPBLAS "sd: rocm backend" OFF)
|
||||
option(SD_METAL "sd: metal backend" OFF)
|
||||
option(SD_VULKAN "sd: vulkan backend" OFF)
|
||||
option(SD_OPENCL "sd: opencl backend" OFF)
|
||||
option(SD_SYCL "sd: sycl backend" OFF)
|
||||
option(SD_MUSA "sd: musa backend" OFF)
|
||||
option(SD_FAST_SOFTMAX "sd: x1.5 faster softmax, indeterministic (sometimes, same seed don't generate same image), cuda only" OFF)
|
||||
option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF)
|
||||
option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF)
|
||||
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
||||
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
||||
|
||||
if(SD_CUDA)
|
||||
@@ -52,6 +55,12 @@ if (SD_VULKAN)
|
||||
add_definitions(-DSD_USE_VULKAN)
|
||||
endif ()
|
||||
|
||||
if (SD_OPENCL)
|
||||
message("-- Use OpenCL as backend stable-diffusion")
|
||||
set(GGML_OPENCL ON)
|
||||
add_definitions(-DSD_USE_OPENCL)
|
||||
endif ()
|
||||
|
||||
if (SD_HIPBLAS)
|
||||
message("-- Use HIPBLAS as backend stable-diffusion")
|
||||
set(GGML_HIP ON)
|
||||
@@ -78,24 +87,28 @@ file(GLOB SD_LIB_SOURCES
|
||||
"*.hpp"
|
||||
)
|
||||
|
||||
# we can get only one share lib
|
||||
if(SD_BUILD_SHARED_LIBS)
|
||||
message("-- Build shared library")
|
||||
message(${SD_LIB_SOURCES})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
if(NOT SD_BUILD_SHARED_GGML_LIB)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif()
|
||||
add_library(${SD_LIB} SHARED ${SD_LIB_SOURCES})
|
||||
add_definitions(-DSD_BUILD_SHARED_LIB)
|
||||
target_compile_definitions(${SD_LIB} PRIVATE -DSD_BUILD_DLL)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
else()
|
||||
message("-- Build static library")
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
if(NOT SD_BUILD_SHARED_GGML_LIB)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif()
|
||||
add_library(${SD_LIB} STATIC ${SD_LIB_SOURCES})
|
||||
endif()
|
||||
|
||||
if(SD_SYCL)
|
||||
message("-- Use SYCL as backend stable-diffusion")
|
||||
set(GGML_SYCL ON)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -fsycl")
|
||||
add_definitions(-DSD_USE_SYCL)
|
||||
# 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
|
||||
@@ -110,23 +123,37 @@ endif()
|
||||
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
# see https://github.com/ggerganov/ggml/pull/682
|
||||
add_definitions(-DGGML_MAX_NAME=128)
|
||||
if (NOT SD_USE_SYSTEM_GGML)
|
||||
# see https://github.com/ggerganov/ggml/pull/682
|
||||
add_definitions(-DGGML_MAX_NAME=128)
|
||||
endif()
|
||||
|
||||
# deps
|
||||
# Only add ggml if it hasn't been added yet
|
||||
if (NOT TARGET ggml)
|
||||
add_subdirectory(ggml)
|
||||
if (SD_USE_SYSTEM_GGML)
|
||||
find_package(ggml REQUIRED)
|
||||
if (NOT ggml_FOUND)
|
||||
message(FATAL_ERROR "System-installed GGML library not found.")
|
||||
endif()
|
||||
add_library(ggml ALIAS ggml::ggml)
|
||||
else()
|
||||
add_subdirectory(ggml)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
target_link_libraries(${SD_LIB} PUBLIC ggml zip)
|
||||
target_include_directories(${SD_LIB} PUBLIC . thirdparty)
|
||||
target_compile_features(${SD_LIB} PUBLIC cxx_std_11)
|
||||
target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17)
|
||||
|
||||
|
||||
if (SD_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
set(SD_PUBLIC_HEADERS stable-diffusion.h)
|
||||
set_target_properties(${SD_LIB} PROPERTIES PUBLIC_HEADER "${SD_PUBLIC_HEADERS}")
|
||||
|
||||
install(TARGETS ${SD_LIB} LIBRARY PUBLIC_HEADER)
|
||||
|
||||
+9
-4
@@ -1,16 +1,21 @@
|
||||
ARG UBUNTU_VERSION=22.04
|
||||
|
||||
FROM ubuntu:$UBUNTU_VERSION as build
|
||||
FROM ubuntu:$UBUNTU_VERSION AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y build-essential git cmake
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential git cmake
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN mkdir build && cd build && cmake .. && cmake --build . --config Release
|
||||
RUN cmake . -B ./build
|
||||
RUN cmake --build ./build --config Release --parallel
|
||||
|
||||
FROM ubuntu:$UBUNTU_VERSION as runtime
|
||||
FROM ubuntu:$UBUNTU_VERSION AS runtime
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --yes --no-install-recommends libgomp1 && \
|
||||
apt-get clean
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
|
||||
|
||||
+9
-5
@@ -1,18 +1,22 @@
|
||||
ARG MUSA_VERSION=rc3.1.0
|
||||
ARG MUSA_VERSION=rc4.2.0
|
||||
ARG UBUNTU_VERSION=22.04
|
||||
|
||||
FROM mthreads/musa:${MUSA_VERSION}-devel-ubuntu22.04 as build
|
||||
FROM mthreads/musa:${MUSA_VERSION}-devel-ubuntu${UBUNTU_VERSION}-amd64 as build
|
||||
|
||||
RUN apt-get update && apt-get install -y cmake
|
||||
RUN apt-get update && apt-get install -y ccache cmake git
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN mkdir build && cd build && \
|
||||
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_MUSA=ON -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_C_FLAGS="${CMAKE_C_FLAGS} -fopenmp -I/usr/lib/llvm-14/lib/clang/14.0.0/include -L/usr/lib/llvm-14/lib" \
|
||||
-DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -fopenmp -I/usr/lib/llvm-14/lib/clang/14.0.0/include -L/usr/lib/llvm-14/lib" \
|
||||
-DSD_MUSA=ON -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake --build . --config Release
|
||||
|
||||
FROM mthreads/musa:${MUSA_VERSION}-runtime-ubuntu22.04 as runtime
|
||||
FROM mthreads/musa:${MUSA_VERSION}-runtime-ubuntu${UBUNTU_VERSION}-amd64 as runtime
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
ARG SYCL_VERSION=2025.1.0-0
|
||||
|
||||
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS build
|
||||
|
||||
RUN apt-get update && apt-get install -y cmake
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN mkdir build && cd build && \
|
||||
cmake .. -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DSD_SYCL=ON -DCMAKE_BUILD_TYPE=Release && \
|
||||
cmake --build . --config Release -j$(nproc)
|
||||
|
||||
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS runtime
|
||||
|
||||
COPY --from=build /sd.cpp/build/bin/sd /sd
|
||||
|
||||
ENTRYPOINT [ "/sd" ]
|
||||
@@ -4,36 +4,72 @@
|
||||
|
||||
# stable-diffusion.cpp
|
||||
|
||||
Inference of Stable Diffusion and Flux in pure C/C++
|
||||
<div align="center">
|
||||
<a href="https://trendshift.io/repositories/9714" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9714" alt="leejet%2Fstable-diffusion.cpp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</div>
|
||||
|
||||
Diffusion model(SD,Flux,Wan,...) inference in pure C/C++
|
||||
|
||||
***Note that this project is under active development. \
|
||||
API and command-line option may change frequently.***
|
||||
|
||||
## 🔥Important News
|
||||
|
||||
* **2025/10/13** 🚀 stable-diffusion.cpp now supports **Qwen-Image-Edit / Qwen-Image-Edit 2509**
|
||||
👉 Details: [PR #877](https://github.com/leejet/stable-diffusion.cpp/pull/877)
|
||||
|
||||
* **2025/10/12** 🚀 stable-diffusion.cpp now supports **Qwen-Image**
|
||||
👉 Details: [PR #851](https://github.com/leejet/stable-diffusion.cpp/pull/851)
|
||||
|
||||
* **2025/09/14** 🚀 stable-diffusion.cpp now supports **Wan2.1 Vace**
|
||||
👉 Details: [PR #819](https://github.com/leejet/stable-diffusion.cpp/pull/819)
|
||||
|
||||
* **2025/09/06** 🚀 stable-diffusion.cpp now supports **Wan2.1 / Wan2.2**
|
||||
👉 Details: [PR #778](https://github.com/leejet/stable-diffusion.cpp/pull/778)
|
||||
|
||||
## Features
|
||||
|
||||
- Plain C/C++ implementation based on [ggml](https://github.com/ggerganov/ggml), working in the same way as [llama.cpp](https://github.com/ggerganov/llama.cpp)
|
||||
- Super lightweight and without external dependencies
|
||||
- SD1.x, SD2.x, SDXL and [SD3/SD3.5](./docs/sd3.md) support
|
||||
- !!!The VAE in SDXL encounters NaN issues under FP16, but unfortunately, the ggml_conv_2d only operates under FP16. Hence, a parameter is needed to specify the VAE that has fixed the FP16 NaN issue. You can find it here: [SDXL VAE FP16 Fix](https://huggingface.co/madebyollin/sdxl-vae-fp16-fix/blob/main/sdxl_vae.safetensors).
|
||||
- [Flux-dev/Flux-schnell Support](./docs/flux.md)
|
||||
|
||||
- [SD-Turbo](https://huggingface.co/stabilityai/sd-turbo) and [SDXL-Turbo](https://huggingface.co/stabilityai/sdxl-turbo) support
|
||||
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker) support.
|
||||
- 16-bit, 32-bit float support
|
||||
- 2-bit, 3-bit, 4-bit, 5-bit and 8-bit integer quantization support
|
||||
- Accelerated memory-efficient CPU inference
|
||||
- Only requires ~2.3GB when using txt2img with fp16 precision to generate a 512x512 image, enabling Flash Attention just requires ~1.8GB.
|
||||
- AVX, AVX2 and AVX512 support for x86 architectures
|
||||
- Full CUDA, Metal, Vulkan and SYCL backend for GPU acceleration.
|
||||
- Can load ckpt, safetensors and diffusers models/checkpoints. Standalone VAEs models
|
||||
- No need to convert to `.ggml` or `.gguf` anymore!
|
||||
- Supported models
|
||||
- Image Models
|
||||
- SD1.x, SD2.x, [SD-Turbo](https://huggingface.co/stabilityai/sd-turbo)
|
||||
- SDXL, [SDXL-Turbo](https://huggingface.co/stabilityai/sdxl-turbo)
|
||||
- [SD3/SD3.5](./docs/sd3.md)
|
||||
- [Flux-dev/Flux-schnell](./docs/flux.md)
|
||||
- [Chroma](./docs/chroma.md)
|
||||
- [Qwen Image](./docs/qwen_image.md)
|
||||
- Image Edit Models
|
||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||
- [Qwen Image Edit/Qwen Image Edit 2509](./docs/qwen_image_edit.md)
|
||||
- Video Models
|
||||
- [Wan2.1/Wan2.2](./docs/wan.md)
|
||||
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker) support.
|
||||
- Control Net support with SD 1.5
|
||||
- LoRA support, same as [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#lora)
|
||||
- Latent Consistency Models support (LCM/LCM-LoRA)
|
||||
- Faster and memory efficient latent decoding with [TAESD](https://github.com/madebyollin/taesd)
|
||||
- Upscale images generated with [ESRGAN](https://github.com/xinntao/Real-ESRGAN)
|
||||
- Supported backends
|
||||
- CPU (AVX, AVX2 and AVX512 support for x86 architectures)
|
||||
- CUDA
|
||||
- Vulkan
|
||||
- Metal
|
||||
- OpenCL
|
||||
- SYCL
|
||||
- Supported weight formats
|
||||
- Pytorch checkpoint (`.ckpt` or `.pth`)
|
||||
- Safetensors (`./safetensors`)
|
||||
- GGUF (`.gguf`)
|
||||
- Supported platforms
|
||||
- Linux
|
||||
- Mac OS
|
||||
- Windows
|
||||
- Android (via Termux, [Local Diffusion](https://github.com/rmatif/Local-Diffusion))
|
||||
- Flash Attention for memory usage optimization
|
||||
- Original `txt2img` and `img2img` mode
|
||||
- Negative prompt
|
||||
- [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui) style tokenizer (not all the features, only token weighting for now)
|
||||
- LoRA support, same as [stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#lora)
|
||||
- Latent Consistency Models support (LCM/LCM-LoRA)
|
||||
- Faster and memory efficient latent decoding with [TAESD](https://github.com/madebyollin/taesd)
|
||||
- Upscale images generated with [ESRGAN](https://github.com/xinntao/Real-ESRGAN)
|
||||
- VAE tiling processing for reduce memory usage
|
||||
- Control Net support with SD 1.5
|
||||
- Sampling method
|
||||
- `Euler A`
|
||||
- `Euler`
|
||||
@@ -45,264 +81,45 @@ Inference of Stable Diffusion and Flux in pure C/C++
|
||||
- [`LCM`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13952)
|
||||
- Cross-platform reproducibility (`--rng cuda`, consistent with the `stable-diffusion-webui GPU RNG`)
|
||||
- Embedds generation parameters into png output as webui-compatible text string
|
||||
- Supported platforms
|
||||
- Linux
|
||||
- Mac OS
|
||||
- Windows
|
||||
- Android (via Termux)
|
||||
|
||||
### TODO
|
||||
## Quick Start
|
||||
|
||||
- [ ] More sampling methods
|
||||
- [ ] Make inference faster
|
||||
- The current implementation of ggml_conv_2d is slow and has high memory usage
|
||||
- [ ] Continuing to reduce memory usage (quantizing the weights of ggml_conv_2d)
|
||||
- [ ] Implement Inpainting support
|
||||
### Get the sd executable
|
||||
|
||||
## Usage
|
||||
- Download pre-built binaries from the [releases page](https://github.com/leejet/stable-diffusion.cpp/releases)
|
||||
- Or build from source by following the [build guide](./docs/build.md)
|
||||
|
||||
For most users, you can download the built executable program from the latest [release](https://github.com/leejet/stable-diffusion.cpp/releases/latest).
|
||||
If the built product does not meet your requirements, you can choose to build it manually.
|
||||
### Download model weights
|
||||
|
||||
### Get the Code
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/leejet/stable-diffusion.cpp
|
||||
cd stable-diffusion.cpp
|
||||
```
|
||||
|
||||
- If you have already cloned the repository, you can use the following command to update the repository to the latest code.
|
||||
|
||||
```
|
||||
cd stable-diffusion.cpp
|
||||
git pull origin master
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
### Download weights
|
||||
|
||||
- download original weights(.ckpt or .safetensors). For example
|
||||
- Stable Diffusion v1.4 from https://huggingface.co/CompVis/stable-diffusion-v-1-4-original
|
||||
- download weights(.ckpt or .safetensors or .gguf). For example
|
||||
- Stable Diffusion v1.5 from https://huggingface.co/runwayml/stable-diffusion-v1-5
|
||||
- Stable Diffuison v2.1 from https://huggingface.co/stabilityai/stable-diffusion-2-1
|
||||
- Stable Diffusion 3 2B from https://huggingface.co/stabilityai/stable-diffusion-3-medium
|
||||
|
||||
```shell
|
||||
curl -L -O https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/resolve/main/sd-v1-4.ckpt
|
||||
# curl -L -O https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors
|
||||
# curl -L -O https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-nonema-pruned.safetensors
|
||||
# curl -L -O https://huggingface.co/stabilityai/stable-diffusion-3-medium/resolve/main/sd3_medium_incl_clips_t5xxlfp16.safetensors
|
||||
```sh
|
||||
curl -L -O https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
#### Build from scratch
|
||||
|
||||
```shell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using OpenBLAS
|
||||
|
||||
```
|
||||
cmake .. -DGGML_OPENBLAS=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using CUDA
|
||||
|
||||
This provides BLAS acceleration using the CUDA cores of your Nvidia GPU. Make sure to have the CUDA toolkit installed. You can download it from your Linux distro's package manager (e.g. `apt install nvidia-cuda-toolkit`) or from here: [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads). Recommended to have at least 4 GB of VRAM.
|
||||
|
||||
```
|
||||
cmake .. -DSD_CUDA=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using HipBLAS
|
||||
This provides BLAS acceleration using the ROCm cores of your AMD GPU. Make sure to have the ROCm toolkit installed.
|
||||
|
||||
Windows User Refer to [docs/hipBLAS_on_Windows.md](docs%2FhipBLAS_on_Windows.md) for a comprehensive guide.
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
##### Using MUSA
|
||||
|
||||
This provides BLAS acceleration using the MUSA cores of your Moore Threads GPU. Make sure to have the MUSA toolkit installed.
|
||||
|
||||
```bash
|
||||
cmake .. -DCMAKE_C_COMPILER=/usr/local/musa/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/musa/bin/clang++ -DSD_MUSA=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using Metal
|
||||
|
||||
Using Metal makes the computation run on the GPU. Currently, there are some issues with Metal when performing operations on very large matrices, making it highly inefficient at the moment. Performance improvements are expected in the near future.
|
||||
|
||||
```
|
||||
cmake .. -DSD_METAL=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using Vulkan
|
||||
|
||||
Install Vulkan SDK from https://www.lunarg.com/vulkan-sdk/.
|
||||
|
||||
```
|
||||
cmake .. -DSD_VULKAN=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
##### Using SYCL
|
||||
|
||||
Using SYCL makes the computation run on the Intel GPU. Please make sure you have installed the related driver and [Intel® oneAPI Base toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) before start. More details and steps can refer to [llama.cpp SYCL backend](https://github.com/ggerganov/llama.cpp/blob/master/docs/backend/SYCL.md#linux).
|
||||
|
||||
```
|
||||
# Export relevant ENV variables
|
||||
source /opt/intel/oneapi/setvars.sh
|
||||
|
||||
# Option 1: Use FP32 (recommended for better performance in most cases)
|
||||
cmake .. -DSD_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx
|
||||
|
||||
# Option 2: Use FP16
|
||||
cmake .. -DSD_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL_F16=ON
|
||||
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
Example of text2img by using SYCL backend:
|
||||
|
||||
- download `stable-diffusion` model weight, refer to [download-weight](#download-weights).
|
||||
|
||||
- run `./bin/sd -m ../models/sd3_medium_incl_clips_t5xxlfp16.safetensors --cfg-scale 5 --steps 30 --sampling-method euler -H 1024 -W 1024 --seed 42 -p "fantasy medieval village world inside a glass sphere , high detail, fantasy, realistic, light effect, hyper detail, volumetric lighting, cinematic, macro, depth of field, blur, red light and clouds from the back, highly detailed epic cinematic concept art cg render made in maya, blender and photoshop, octane render, excellent composition, dynamic dramatic cinematic lighting, aesthetic, very inspirational, world inside a glass sphere by james gurney by artgerm with james jean, joe fenton and tristan eaton by ross tran, fine details, 4k resolution"`
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/sycl_sd3_output.png" width="360x">
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
##### Using Flash Attention
|
||||
|
||||
Enabling flash attention for the diffusion model reduces memory usage by varying amounts of MB.
|
||||
eg.:
|
||||
- flux 768x768 ~600mb
|
||||
- SD2 768x768 ~1400mb
|
||||
|
||||
For most backends, it slows things down, but for cuda it generally speeds it up too.
|
||||
At the moment, it is only supported for some models and some backends (like cpu, cuda/rocm, metal).
|
||||
|
||||
Run by adding `--diffusion-fa` to the arguments and watch for:
|
||||
```
|
||||
[INFO ] stable-diffusion.cpp:312 - Using flash attention in the diffusion model
|
||||
```
|
||||
and the compute buffer shrink in the debug log:
|
||||
```
|
||||
[DEBUG] ggml_extend.hpp:1004 - flux compute buffer size: 650.00 MB(VRAM)
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```
|
||||
usage: ./bin/sd [arguments]
|
||||
|
||||
arguments:
|
||||
-h, --help show this help message and exit
|
||||
-M, --mode [MODEL] run mode (txt2img or img2img or convert, default: txt2img)
|
||||
-t, --threads N number of threads to use during computation (default: -1)
|
||||
If threads <= 0, then threads will be set to the number of CPU physical cores
|
||||
-m, --model [MODEL] path to full model
|
||||
--diffusion-model path to the standalone diffusion model
|
||||
--clip_l path to the clip-l text encoder
|
||||
--clip_g path to the clip-l text encoder
|
||||
--t5xxl path to the the t5xxl text encoder
|
||||
--vae [VAE] path to vae
|
||||
--taesd [TAESD_PATH] path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--control-net [CONTROL_PATH] path to control net model
|
||||
--embd-dir [EMBEDDING_PATH] path to embeddings
|
||||
--stacked-id-embd-dir [DIR] path to PHOTOMAKER stacked id embeddings
|
||||
--input-id-images-dir [DIR] path to PHOTOMAKER input id images dir
|
||||
--normalize-input normalize PHOTOMAKER input id images
|
||||
--upscale-model [ESRGAN_PATH] path to esrgan model. Upscale images after generate, just RealESRGAN_x4plus_anime_6B supported by now
|
||||
--upscale-repeats Run the ESRGAN upscaler this many times (default 1)
|
||||
--type [TYPE] weight type (f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_k, q3_k, q4_k)
|
||||
If not specified, the default is the type of the weight file
|
||||
--lora-model-dir [DIR] lora model directory
|
||||
-i, --init-img [IMAGE] path to the input image, required by img2img
|
||||
--control-image [IMAGE] path to image condition, control net
|
||||
-o, --output OUTPUT path to write result image to (default: ./output.png)
|
||||
-p, --prompt [PROMPT] the prompt to render
|
||||
-n, --negative-prompt PROMPT the negative prompt (default: "")
|
||||
--cfg-scale SCALE unconditional guidance scale: (default: 7.0)
|
||||
--skip-layers LAYERS Layers to skip for SLG steps: (default: [7,8,9])
|
||||
--skip-layer-start START SLG enabling point: (default: 0.01)
|
||||
--skip-layer-end END SLG disabling point: (default: 0.2)
|
||||
SLG will be enabled at step int([STEPS]*[START]) and disabled at int([STEPS]*[END])
|
||||
--strength STRENGTH strength for noising/unnoising (default: 0.75)
|
||||
--style-ratio STYLE-RATIO strength for keeping input identity (default: 20%)
|
||||
--control-strength STRENGTH strength to apply Control Net (default: 0.9)
|
||||
1.0 corresponds to full destruction of information in init image
|
||||
-H, --height H image height, in pixel space (default: 512)
|
||||
-W, --width W image width, in pixel space (default: 512)
|
||||
--sampling-method {euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm}
|
||||
sampling method (default: "euler_a")
|
||||
--steps STEPS number of sample steps (default: 20)
|
||||
--rng {std_default, cuda} RNG (default: cuda)
|
||||
-s SEED, --seed SEED RNG seed (default: 42, use random seed for < 0)
|
||||
-b, --batch-count COUNT number of images to generate
|
||||
--schedule {discrete, karras, exponential, ays, gits} Denoiser sigma schedule (default: discrete)
|
||||
--clip-skip N ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1)
|
||||
<= 0 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--diffusion-fa use flash attention in the diffusion model (for low vram)
|
||||
Might lower quality, since it implies converting k and v to f16.
|
||||
This might crash if it is not supported by the backend.
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--canny apply canny preprocessor (edge detection)
|
||||
--color Colors the logging tags according to level
|
||||
-v, --verbose print extra info
|
||||
```
|
||||
|
||||
#### txt2img example
|
||||
### Generate an image with just one command
|
||||
|
||||
```sh
|
||||
./bin/sd -m ../models/sd-v1-4.ckpt -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/sd_xl_base_1.0.safetensors --vae ../models/sdxl_vae-fp16-fix.safetensors -H 1024 -W 1024 -p "a lovely cat" -v
|
||||
# ./bin/sd -m ../models/sd3_medium_incl_clips_t5xxlfp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable Diffusion CPP\"' --cfg-scale 4.5 --sampling-method euler -v
|
||||
# ./bin/sd --diffusion-model ../models/flux1-dev-q3_k.gguf --vae ../models/ae.sft --clip_l ../models/clip_l.safetensors --t5xxl ../models/t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v
|
||||
# ./bin/sd -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v
|
||||
./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
```
|
||||
|
||||
Using formats of different precisions will yield results of varying quality.
|
||||
***For detailed command-line arguments, check out [cli doc](./examples/cli/README.md).***
|
||||
|
||||
| f32 | f16 |q8_0 |q5_0 |q5_1 |q4_0 |q4_1 |
|
||||
| ---- |---- |---- |---- |---- |---- |---- |
|
||||
|  | | | | | | |
|
||||
## Performance
|
||||
|
||||
#### img2img example
|
||||
|
||||
- `./output.png` is the image generated from the above txt2img pipeline
|
||||
|
||||
|
||||
```
|
||||
./bin/sd --mode img2img -m ../models/sd-v1-4.ckpt -p "cat with blue eyes" -i ./output.png -o ./img2img_output.png --strength 0.4
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="./assets/img2img_output.png" width="256x">
|
||||
</p>
|
||||
If you want to improve performance or reduce VRAM/RAM usage, please refer to [performance guide](./docs/performance.md).
|
||||
|
||||
## More Guides
|
||||
|
||||
- [SD1.x/SD2.x/SDXL](./docs/sd.md)
|
||||
- [SD3/SD3.5](./docs/sd3.md)
|
||||
- [Flux-dev/Flux-schnell](./docs/flux.md)
|
||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||
- [Chroma](./docs/chroma.md)
|
||||
- [🔥Qwen Image](./docs/qwen_image.md)
|
||||
- [🔥Qwen Image Edit/Qwen Image Edit 2509](./docs/qwen_image_edit.md)
|
||||
- [🔥Wan2.1/Wan2.2](./docs/wan.md)
|
||||
- [LoRA](./docs/lora.md)
|
||||
- [LCM/LCM-LoRA](./docs/lcm.md)
|
||||
- [Using PhotoMaker to personalize image generation](./docs/photo_maker.md)
|
||||
@@ -315,10 +132,12 @@ Using formats of different precisions will yield results of varying quality.
|
||||
|
||||
These projects wrap `stable-diffusion.cpp` for easier use in other languages/frameworks.
|
||||
|
||||
* Golang: [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)
|
||||
* 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)
|
||||
* Rust: [newfla/diffusion-rs](https://github.com/newfla/diffusion-rs)
|
||||
* Flutter/Dart: [rmatif/Local-Diffusion](https://github.com/rmatif/Local-Diffusion)
|
||||
|
||||
## UIs
|
||||
|
||||
@@ -326,7 +145,11 @@ These projects use `stable-diffusion.cpp` as a backend for their image generatio
|
||||
|
||||
- [Jellybox](https://jellybox.com)
|
||||
- [Stable Diffusion GUI](https://github.com/fszontagh/sd.cpp.gui.wx)
|
||||
- [Stable Diffusion CLI-GUI] (https://github.com/piallai/stable-diffusion.cpp)
|
||||
- [Stable Diffusion CLI-GUI](https://github.com/piallai/stable-diffusion.cpp)
|
||||
- [Local Diffusion](https://github.com/rmatif/Local-Diffusion)
|
||||
- [sd.cpp-webui](https://github.com/daniandtheweb/sd.cpp-webui)
|
||||
- [LocalAI](https://github.com/mudler/LocalAI)
|
||||
- [Neural-Pixel](https://github.com/Luiz-Alcantara/Neural-Pixel)
|
||||
|
||||
## Contributors
|
||||
|
||||
@@ -341,6 +164,7 @@ Thank you to all the people who have already contributed to stable-diffusion.cpp
|
||||
## References
|
||||
|
||||
- [ggml](https://github.com/ggerganov/ggml)
|
||||
- [diffusers](https://github.com/huggingface/diffusers)
|
||||
- [stable-diffusion](https://github.com/CompVis/stable-diffusion)
|
||||
- [sd3-ref](https://github.com/Stability-AI/sd3-ref)
|
||||
- [stable-diffusion-stability-ai](https://github.com/Stability-AI/stablediffusion)
|
||||
@@ -350,3 +174,5 @@ Thank you to all the people who have already contributed to stable-diffusion.cpp
|
||||
- [latent-consistency-model](https://github.com/luosiallen/latent-consistency-model)
|
||||
- [generative-models](https://github.com/Stability-AI/generative-models/)
|
||||
- [PhotoMaker](https://github.com/TencentARC/PhotoMaker)
|
||||
- [Wan2.1](https://github.com/Wan-Video/Wan2.1)
|
||||
- [Wan2.2](https://github.com/Wan-Video/Wan2.2)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 539 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 496 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 457 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 415 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 594 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
|
||||
/*================================================== CLIPTokenizer ===================================================*/
|
||||
|
||||
std::pair<std::unordered_map<std::string, float>, std::string> extract_and_remove_lora(std::string text) {
|
||||
__STATIC_INLINE__ std::pair<std::unordered_map<std::string, float>, std::string> extract_and_remove_lora(std::string text) {
|
||||
std::regex re("<lora:([^:]+):([^>]+)>");
|
||||
std::smatch matches;
|
||||
std::unordered_map<std::string, float> filename2multiplier;
|
||||
@@ -31,7 +31,7 @@ std::pair<std::unordered_map<std::string, float>, std::string> extract_and_remov
|
||||
return std::make_pair(filename2multiplier, text);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::u32string>> bytes_to_unicode() {
|
||||
__STATIC_INLINE__ std::vector<std::pair<int, std::u32string>> bytes_to_unicode() {
|
||||
std::vector<std::pair<int, std::u32string>> byte_unicode_pairs;
|
||||
std::set<int> byte_set;
|
||||
for (int b = static_cast<int>('!'); b <= static_cast<int>('~'); ++b) {
|
||||
@@ -179,9 +179,9 @@ public:
|
||||
|
||||
auto it = encoder.find(utf8_to_utf32("img</w>"));
|
||||
if (it != encoder.end()) {
|
||||
LOG_DEBUG(" trigger word img already in vocab");
|
||||
LOG_DEBUG("trigger word img already in vocab");
|
||||
} else {
|
||||
LOG_DEBUG(" trigger word img not in vocab yet");
|
||||
LOG_DEBUG("trigger word img not in vocab yet");
|
||||
}
|
||||
|
||||
int rank = 0;
|
||||
@@ -488,14 +488,14 @@ public:
|
||||
blocks["mlp"] = std::shared_ptr<GGMLBlock>(new CLIPMLP(d_model, intermediate_size));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, bool mask = true) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, ggml_backend_t backend, struct ggml_tensor* x, bool mask = true) {
|
||||
// x: [N, n_token, d_model]
|
||||
auto self_attn = std::dynamic_pointer_cast<MultiheadAttention>(blocks["self_attn"]);
|
||||
auto layer_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["layer_norm1"]);
|
||||
auto layer_norm2 = std::dynamic_pointer_cast<LayerNorm>(blocks["layer_norm2"]);
|
||||
auto mlp = std::dynamic_pointer_cast<CLIPMLP>(blocks["mlp"]);
|
||||
|
||||
x = ggml_add(ctx, x, self_attn->forward(ctx, layer_norm1->forward(ctx, x), mask));
|
||||
x = ggml_add(ctx, x, self_attn->forward(ctx, backend, layer_norm1->forward(ctx, x), mask));
|
||||
x = ggml_add(ctx, x, mlp->forward(ctx, layer_norm2->forward(ctx, x)));
|
||||
return x;
|
||||
}
|
||||
@@ -517,7 +517,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, int clip_skip = -1, bool mask = true) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
int clip_skip = -1,
|
||||
bool mask = true) {
|
||||
// x: [N, n_token, d_model]
|
||||
int layer_idx = n_layer - 1;
|
||||
// LOG_DEBUG("clip_skip %d", clip_skip);
|
||||
@@ -532,7 +536,7 @@ public:
|
||||
}
|
||||
std::string name = "layers." + std::to_string(i);
|
||||
auto layer = std::dynamic_pointer_cast<CLIPLayer>(blocks[name]);
|
||||
x = layer->forward(ctx, x, mask); // [N, n_token, d_model]
|
||||
x = layer->forward(ctx, backend, x, mask); // [N, n_token, d_model]
|
||||
// LOG_DEBUG("layer %d", i);
|
||||
}
|
||||
return x;
|
||||
@@ -544,11 +548,17 @@ protected:
|
||||
int64_t embed_dim;
|
||||
int64_t vocab_size;
|
||||
int64_t num_positions;
|
||||
bool force_clip_f32;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
enum ggml_type token_wtype = (tensor_types.find(prefix + "token_embedding.weight") != tensor_types.end()) ? tensor_types[prefix + "token_embedding.weight"] : GGML_TYPE_F32;
|
||||
enum ggml_type position_wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "position_embedding.weight") != tensor_types.end()) ? tensor_types[prefix + "position_embedding.weight"] : GGML_TYPE_F32;
|
||||
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
enum ggml_type token_wtype = GGML_TYPE_F32;
|
||||
if (!force_clip_f32) {
|
||||
token_wtype = get_type(prefix + "token_embedding.weight", tensor_types, GGML_TYPE_F32);
|
||||
if (!support_get_rows(token_wtype)) {
|
||||
token_wtype = GGML_TYPE_F32;
|
||||
}
|
||||
}
|
||||
enum ggml_type position_wtype = GGML_TYPE_F32;
|
||||
params["token_embedding.weight"] = ggml_new_tensor_2d(ctx, token_wtype, embed_dim, vocab_size);
|
||||
params["position_embedding.weight"] = ggml_new_tensor_2d(ctx, position_wtype, embed_dim, num_positions);
|
||||
}
|
||||
@@ -556,10 +566,12 @@ protected:
|
||||
public:
|
||||
CLIPEmbeddings(int64_t embed_dim,
|
||||
int64_t vocab_size = 49408,
|
||||
int64_t num_positions = 77)
|
||||
int64_t num_positions = 77,
|
||||
bool force_clip_f32 = false)
|
||||
: embed_dim(embed_dim),
|
||||
vocab_size(vocab_size),
|
||||
num_positions(num_positions) {
|
||||
num_positions(num_positions),
|
||||
force_clip_f32(force_clip_f32) {
|
||||
}
|
||||
|
||||
struct ggml_tensor* get_token_embed_weight() {
|
||||
@@ -594,10 +606,10 @@ protected:
|
||||
int64_t image_size;
|
||||
int64_t num_patches;
|
||||
int64_t num_positions;
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
enum ggml_type patch_wtype = GGML_TYPE_F16; // tensor_types.find(prefix + "patch_embedding.weight") != tensor_types.end() ? tensor_types[prefix + "patch_embedding.weight"] : GGML_TYPE_F16;
|
||||
enum ggml_type class_wtype = GGML_TYPE_F32; // tensor_types.find(prefix + "class_embedding") != tensor_types.end() ? tensor_types[prefix + "class_embedding"] : GGML_TYPE_F32;
|
||||
enum ggml_type position_wtype = GGML_TYPE_F32; // tensor_types.find(prefix + "position_embedding.weight") != tensor_types.end() ? tensor_types[prefix + "position_embedding.weight"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
enum ggml_type patch_wtype = GGML_TYPE_F16;
|
||||
enum ggml_type class_wtype = GGML_TYPE_F32;
|
||||
enum ggml_type position_wtype = GGML_TYPE_F32;
|
||||
|
||||
params["patch_embedding.weight"] = ggml_new_tensor_4d(ctx, patch_wtype, patch_size, patch_size, num_channels, embed_dim);
|
||||
params["class_embedding"] = ggml_new_tensor_1d(ctx, class_wtype, embed_dim);
|
||||
@@ -657,9 +669,9 @@ enum CLIPVersion {
|
||||
|
||||
class CLIPTextModel : public GGMLBlock {
|
||||
protected:
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
if (version == OPEN_CLIP_VIT_BIGG_14) {
|
||||
enum ggml_type wtype = GGML_TYPE_F32; // tensor_types.find(prefix + "text_projection") != tensor_types.end() ? tensor_types[prefix + "text_projection"] : GGML_TYPE_F32;
|
||||
enum ggml_type wtype = GGML_TYPE_F32;
|
||||
params["text_projection"] = ggml_new_tensor_2d(ctx, wtype, projection_dim, hidden_size);
|
||||
}
|
||||
}
|
||||
@@ -674,12 +686,11 @@ public:
|
||||
int32_t n_head = 12;
|
||||
int32_t n_layer = 12; // num_hidden_layers
|
||||
int32_t projection_dim = 1280; // only for OPEN_CLIP_VIT_BIGG_14
|
||||
int32_t clip_skip = -1;
|
||||
bool with_final_ln = true;
|
||||
|
||||
CLIPTextModel(CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
||||
int clip_skip_value = -1,
|
||||
bool with_final_ln = true)
|
||||
bool with_final_ln = true,
|
||||
bool force_clip_f32 = false)
|
||||
: version(version), with_final_ln(with_final_ln) {
|
||||
if (version == OPEN_CLIP_VIT_H_14) {
|
||||
hidden_size = 1024;
|
||||
@@ -692,37 +703,31 @@ public:
|
||||
n_head = 20;
|
||||
n_layer = 32;
|
||||
}
|
||||
set_clip_skip(clip_skip_value);
|
||||
|
||||
blocks["embeddings"] = std::shared_ptr<GGMLBlock>(new CLIPEmbeddings(hidden_size, vocab_size, n_token));
|
||||
blocks["embeddings"] = std::shared_ptr<GGMLBlock>(new CLIPEmbeddings(hidden_size, vocab_size, n_token, force_clip_f32));
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new CLIPEncoder(n_layer, hidden_size, n_head, intermediate_size));
|
||||
blocks["final_layer_norm"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size));
|
||||
}
|
||||
|
||||
void set_clip_skip(int skip) {
|
||||
if (skip <= 0) {
|
||||
return;
|
||||
}
|
||||
clip_skip = skip;
|
||||
}
|
||||
|
||||
struct ggml_tensor* get_token_embed_weight() {
|
||||
auto embeddings = std::dynamic_pointer_cast<CLIPEmbeddings>(blocks["embeddings"]);
|
||||
return embeddings->get_token_embed_weight();
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* tkn_embeddings,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false) {
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
// input_ids: [N, n_token]
|
||||
auto embeddings = std::dynamic_pointer_cast<CLIPEmbeddings>(blocks["embeddings"]);
|
||||
auto encoder = std::dynamic_pointer_cast<CLIPEncoder>(blocks["encoder"]);
|
||||
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]
|
||||
x = encoder->forward(ctx, x, return_pooled ? -1 : clip_skip, true);
|
||||
x = encoder->forward(ctx, backend, x, return_pooled ? -1 : clip_skip, true);
|
||||
if (return_pooled || with_final_ln) {
|
||||
x = final_layer_norm->forward(ctx, x);
|
||||
}
|
||||
@@ -733,7 +738,7 @@ public:
|
||||
if (text_projection != NULL) {
|
||||
pooled = ggml_nn_linear(ctx, pooled, text_projection, NULL);
|
||||
} else {
|
||||
LOG_DEBUG("Missing text_projection matrix, assuming identity...");
|
||||
LOG_DEBUG("identity projection");
|
||||
}
|
||||
return pooled; // [hidden_size, 1, 1]
|
||||
}
|
||||
@@ -774,7 +779,11 @@ public:
|
||||
blocks["post_layernorm"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* pixel_values, bool return_pooled = true) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* pixel_values,
|
||||
bool return_pooled = true,
|
||||
int clip_skip = -1) {
|
||||
// pixel_values: [N, num_channels, image_size, image_size]
|
||||
auto embeddings = std::dynamic_pointer_cast<CLIPVisionEmbeddings>(blocks["embeddings"]);
|
||||
auto pre_layernorm = std::dynamic_pointer_cast<LayerNorm>(blocks["pre_layernorm"]);
|
||||
@@ -783,7 +792,7 @@ public:
|
||||
|
||||
auto x = embeddings->forward(ctx, pixel_values); // [N, num_positions, embed_dim]
|
||||
x = pre_layernorm->forward(ctx, x);
|
||||
x = encoder->forward(ctx, x, -1, false);
|
||||
x = encoder->forward(ctx, backend, x, clip_skip, false);
|
||||
// print_ggml_tensor(x, true, "ClipVisionModel x: ");
|
||||
auto last_hidden_state = x;
|
||||
x = post_layernorm->forward(ctx, x); // [N, n_token, hidden_size]
|
||||
@@ -805,8 +814,8 @@ protected:
|
||||
int64_t out_features;
|
||||
bool transpose_weight;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
enum ggml_type wtype = tensor_types.find(prefix + "weight") != tensor_types.end() ? tensor_types[prefix + "weight"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
enum ggml_type wtype = get_type(prefix + "weight", tensor_types, GGML_TYPE_F32);
|
||||
if (transpose_weight) {
|
||||
params["weight"] = ggml_new_tensor_2d(ctx, wtype, out_features, in_features);
|
||||
} else {
|
||||
@@ -851,16 +860,23 @@ public:
|
||||
blocks["visual_projection"] = std::shared_ptr<GGMLBlock>(new CLIPProjection(hidden_size, projection_dim, transpose_proj_w));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* pixel_values) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* pixel_values,
|
||||
bool return_pooled = true,
|
||||
int clip_skip = -1) {
|
||||
// pixel_values: [N, num_channels, image_size, image_size]
|
||||
// return: [N, projection_dim]
|
||||
// return: [N, projection_dim] if return_pooled else [N, n_token, hidden_size]
|
||||
auto vision_model = std::dynamic_pointer_cast<CLIPVisionModel>(blocks["vision_model"]);
|
||||
auto visual_projection = std::dynamic_pointer_cast<CLIPProjection>(blocks["visual_projection"]);
|
||||
|
||||
auto x = vision_model->forward(ctx, pixel_values); // [N, hidden_size]
|
||||
x = visual_projection->forward(ctx, x); // [N, projection_dim]
|
||||
auto x = vision_model->forward(ctx, backend, pixel_values, return_pooled, clip_skip); // [N, hidden_size] or [N, n_token, hidden_size]
|
||||
|
||||
return x; // [N, projection_dim]
|
||||
if (return_pooled) {
|
||||
x = visual_projection->forward(ctx, x); // [N, projection_dim]
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -868,12 +884,13 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
CLIPTextModel model;
|
||||
|
||||
CLIPTextModelRunner(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
CLIPVersion version = OPENAI_CLIP_VIT_L_14,
|
||||
int clip_skip_value = 1,
|
||||
bool with_final_ln = true)
|
||||
: GGMLRunner(backend), model(version, clip_skip_value, with_final_ln) {
|
||||
bool with_final_ln = true,
|
||||
bool force_clip_f32 = false)
|
||||
: GGMLRunner(backend, offload_params_to_cpu), model(version, with_final_ln, force_clip_f32) {
|
||||
model.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
@@ -881,19 +898,17 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
return "clip";
|
||||
}
|
||||
|
||||
void set_clip_skip(int clip_skip) {
|
||||
model.set_clip_skip(clip_skip);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
model.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* embeddings,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false) {
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
size_t N = input_ids->ne[1];
|
||||
size_t n_token = input_ids->ne[0];
|
||||
if (input_ids->ne[0] > model.n_token) {
|
||||
@@ -901,14 +916,15 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
input_ids = ggml_reshape_2d(ctx, input_ids, model.n_token, input_ids->ne[0] / model.n_token);
|
||||
}
|
||||
|
||||
return model.forward(ctx, input_ids, embeddings, max_token_idx, return_pooled);
|
||||
return model.forward(ctx, backend, input_ids, embeddings, max_token_idx, return_pooled, clip_skip);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* input_ids,
|
||||
int num_custom_embeddings = 0,
|
||||
void* custom_embeddings_data = NULL,
|
||||
size_t max_token_idx = 0,
|
||||
bool return_pooled = false) {
|
||||
bool return_pooled = false,
|
||||
int clip_skip = -1) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
|
||||
input_ids = to_backend(input_ids);
|
||||
@@ -927,7 +943,7 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
embeddings = ggml_concat(compute_ctx, token_embed_weight, custom_embeddings, 1);
|
||||
}
|
||||
|
||||
struct ggml_tensor* hidden_states = forward(compute_ctx, input_ids, embeddings, max_token_idx, return_pooled);
|
||||
struct ggml_tensor* hidden_states = forward(compute_ctx, runtime_backend, input_ids, embeddings, max_token_idx, return_pooled, clip_skip);
|
||||
|
||||
ggml_build_forward_expand(gf, hidden_states);
|
||||
|
||||
@@ -940,10 +956,11 @@ struct CLIPTextModelRunner : public GGMLRunner {
|
||||
void* custom_embeddings_data,
|
||||
size_t max_token_idx,
|
||||
bool return_pooled,
|
||||
int clip_skip,
|
||||
ggml_tensor** output,
|
||||
ggml_context* output_ctx = NULL) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(input_ids, num_custom_embeddings, custom_embeddings_data, max_token_idx, return_pooled);
|
||||
return build_graph(input_ids, num_custom_embeddings, custom_embeddings_data, max_token_idx, return_pooled, clip_skip);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
|
||||
+62
-19
@@ -56,8 +56,8 @@ public:
|
||||
// x: [N, channels, h, w]
|
||||
auto conv = std::dynamic_pointer_cast<Conv2d>(blocks["conv"]);
|
||||
|
||||
x = ggml_upscale(ctx, x, 2); // [N, channels, h*2, w*2]
|
||||
x = conv->forward(ctx, x); // [N, out_channels, h*2, w*2]
|
||||
x = ggml_upscale(ctx, x, 2, GGML_SCALE_MODE_NEAREST); // [N, channels, h*2, w*2]
|
||||
x = conv->forward(ctx, x); // [N, out_channels, h*2, w*2]
|
||||
return x;
|
||||
}
|
||||
};
|
||||
@@ -177,14 +177,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class GEGLU : public GGMLBlock {
|
||||
class GEGLU : public UnaryBlock {
|
||||
protected:
|
||||
int64_t dim_in;
|
||||
int64_t dim_out;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, std::string prefix = "") {
|
||||
enum ggml_type wtype = (tensor_types.find(prefix + "proj.weight") != tensor_types.end()) ? tensor_types[prefix + "proj.weight"] : GGML_TYPE_F32;
|
||||
enum ggml_type bias_wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "proj.bias") != tensor_types.end()) ? tensor_types[prefix + "proj.bias"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, std::string prefix = "") {
|
||||
enum ggml_type wtype = get_type(prefix + "proj.weight", tensor_types, GGML_TYPE_F32);
|
||||
enum ggml_type bias_wtype = GGML_TYPE_F32;
|
||||
params["proj.weight"] = ggml_new_tensor_2d(ctx, wtype, dim_in, dim_out * 2);
|
||||
params["proj.bias"] = ggml_new_tensor_1d(ctx, bias_wtype, dim_out * 2);
|
||||
}
|
||||
@@ -216,23 +216,57 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class GELU : public UnaryBlock {
|
||||
public:
|
||||
GELU(int64_t dim_in, int64_t dim_out, bool bias = true) {
|
||||
blocks["proj"] = std::shared_ptr<GGMLBlock>(new Linear(dim_in, dim_out, bias));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
// x: [ne3, ne2, ne1, dim_in]
|
||||
// return: [ne3, ne2, ne1, dim_out]
|
||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["proj"]);
|
||||
|
||||
x = proj->forward(ctx, x);
|
||||
x = ggml_gelu_inplace(ctx, x);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class FeedForward : public GGMLBlock {
|
||||
public:
|
||||
enum class Activation {
|
||||
GEGLU,
|
||||
GELU
|
||||
};
|
||||
FeedForward(int64_t dim,
|
||||
int64_t dim_out,
|
||||
int64_t mult = 4) {
|
||||
int64_t mult = 4,
|
||||
Activation activation = Activation::GEGLU,
|
||||
bool precision_fix = false) {
|
||||
int64_t inner_dim = dim * mult;
|
||||
if (activation == Activation::GELU) {
|
||||
blocks["net.0"] = std::shared_ptr<GGMLBlock>(new GELU(dim, inner_dim));
|
||||
} else {
|
||||
blocks["net.0"] = std::shared_ptr<GGMLBlock>(new GEGLU(dim, inner_dim));
|
||||
}
|
||||
|
||||
blocks["net.0"] = std::shared_ptr<GGMLBlock>(new GEGLU(dim, inner_dim));
|
||||
// net_1 is nn.Dropout(), skip for inference
|
||||
blocks["net.2"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, dim_out));
|
||||
float scale = 1.f;
|
||||
if (precision_fix) {
|
||||
scale = 1.f / 128.f;
|
||||
}
|
||||
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
||||
// For example, when using Vulkan without enabling force_prec_f32,
|
||||
// or when using CUDA but the weights are k-quants.
|
||||
blocks["net.2"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, dim_out, true, false, false, scale));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
// x: [ne3, ne2, ne1, dim]
|
||||
// return: [ne3, ne2, ne1, dim_out]
|
||||
|
||||
auto net_0 = std::dynamic_pointer_cast<GEGLU>(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"]);
|
||||
|
||||
x = net_0->forward(ctx, x); // [ne3, ne2, ne1, inner_dim]
|
||||
@@ -270,7 +304,10 @@ public:
|
||||
// to_out_1 is nn.Dropout(), skip for inference
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, struct ggml_tensor* context) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context) {
|
||||
// x: [N, n_token, query_dim]
|
||||
// context: [N, n_context, context_dim]
|
||||
// return: [N, n_token, query_dim]
|
||||
@@ -288,7 +325,7 @@ public:
|
||||
auto k = to_k->forward(ctx, context); // [N, n_context, inner_dim]
|
||||
auto v = to_v->forward(ctx, context); // [N, n_context, inner_dim]
|
||||
|
||||
x = ggml_nn_attention_ext(ctx, q, k, v, n_head, NULL, false, false, flash_attn); // [N, n_token, inner_dim]
|
||||
x = ggml_nn_attention_ext(ctx, backend, q, k, v, n_head, NULL, false, false, flash_attn); // [N, n_token, inner_dim]
|
||||
|
||||
x = to_out_0->forward(ctx, x); // [N, n_token, query_dim]
|
||||
return x;
|
||||
@@ -327,7 +364,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, struct ggml_tensor* context) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context) {
|
||||
// x: [N, n_token, query_dim]
|
||||
// context: [N, n_context, context_dim]
|
||||
// return: [N, n_token, query_dim]
|
||||
@@ -352,11 +392,11 @@ public:
|
||||
|
||||
auto r = x;
|
||||
x = norm1->forward(ctx, x);
|
||||
x = attn1->forward(ctx, x, x); // self-attention
|
||||
x = attn1->forward(ctx, backend, x, x); // self-attention
|
||||
x = ggml_add(ctx, x, r);
|
||||
r = x;
|
||||
x = norm2->forward(ctx, x);
|
||||
x = attn2->forward(ctx, x, context); // cross-attention
|
||||
x = attn2->forward(ctx, backend, x, context); // cross-attention
|
||||
x = ggml_add(ctx, x, r);
|
||||
r = x;
|
||||
x = norm3->forward(ctx, x);
|
||||
@@ -401,7 +441,10 @@ public:
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Conv2d(inner_dim, in_channels, {1, 1}));
|
||||
}
|
||||
|
||||
virtual struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, struct ggml_tensor* context) {
|
||||
virtual struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context) {
|
||||
// x: [N, in_channels, h, w]
|
||||
// context: [N, max_position(aka n_token), hidden_size(aka context_dim)]
|
||||
auto norm = std::dynamic_pointer_cast<GroupNorm32>(blocks["norm"]);
|
||||
@@ -424,7 +467,7 @@ public:
|
||||
std::string name = "transformer_blocks." + std::to_string(i);
|
||||
auto transformer_block = std::dynamic_pointer_cast<BasicTransformerBlock>(blocks[name]);
|
||||
|
||||
x = transformer_block->forward(ctx, x, context);
|
||||
x = transformer_block->forward(ctx, backend, x, context);
|
||||
}
|
||||
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 1, 0, 2, 3)); // [N, inner_dim, h * w]
|
||||
@@ -440,9 +483,9 @@ public:
|
||||
|
||||
class AlphaBlender : public GGMLBlock {
|
||||
protected:
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, std::string prefix = "") {
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, std::string prefix = "") {
|
||||
// Get the type of the "mix_factor" tensor from the input tensors map with the specified prefix
|
||||
enum ggml_type wtype = GGML_TYPE_F32; //(tensor_types.ypes.find(prefix + "mix_factor") != tensor_types.end()) ? tensor_types[prefix + "mix_factor"] : GGML_TYPE_F32;
|
||||
enum ggml_type wtype = GGML_TYPE_F32;
|
||||
params["mix_factor"] = ggml_new_tensor_1d(ctx, wtype, 1);
|
||||
}
|
||||
|
||||
|
||||
+555
-171
@@ -2,6 +2,7 @@
|
||||
#define __CONDITIONER_HPP__
|
||||
|
||||
#include "clip.hpp"
|
||||
#include "qwenvl.hpp"
|
||||
#include "t5.hpp"
|
||||
|
||||
struct SDCondition {
|
||||
@@ -14,30 +15,34 @@ struct SDCondition {
|
||||
: c_crossattn(c_crossattn), c_vector(c_vector), c_concat(c_concat) {}
|
||||
};
|
||||
|
||||
struct ConditionerParams {
|
||||
std::string text;
|
||||
int clip_skip = -1;
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
int adm_in_channels = -1;
|
||||
bool zero_out_masked = false;
|
||||
int num_input_imgs = 0; // for photomaker
|
||||
std::vector<sd_image_t*> ref_images = {}; // for qwen image edit
|
||||
};
|
||||
|
||||
struct Conditioner {
|
||||
virtual SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) = 0;
|
||||
virtual void alloc_params_buffer() = 0;
|
||||
virtual void free_params_buffer() = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) = 0;
|
||||
virtual size_t get_params_buffer_size() = 0;
|
||||
const ConditionerParams& conditioner_params) = 0;
|
||||
virtual void alloc_params_buffer() = 0;
|
||||
virtual void free_params_buffer() = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) = 0;
|
||||
virtual size_t get_params_buffer_size() = 0;
|
||||
virtual std::tuple<SDCondition, std::vector<bool>> get_learned_condition_with_trigger(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int num_input_imgs,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) = 0;
|
||||
const ConditionerParams& conditioner_params) {
|
||||
GGML_ABORT("Not implemented yet!");
|
||||
}
|
||||
virtual std::string remove_trigger_from_prompt(ggml_context* work_ctx,
|
||||
const std::string& prompt) = 0;
|
||||
const std::string& prompt) {
|
||||
GGML_ABORT("Not implemented yet!");
|
||||
}
|
||||
};
|
||||
|
||||
// ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
@@ -51,37 +56,26 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
|
||||
std::string trigger_word = "img"; // should be user settable
|
||||
std::string embd_dir;
|
||||
int32_t num_custom_embeddings = 0;
|
||||
int32_t num_custom_embeddings = 0;
|
||||
int32_t num_custom_embeddings_2 = 0;
|
||||
std::vector<uint8_t> token_embed_custom;
|
||||
std::vector<std::string> readed_embeddings;
|
||||
|
||||
FrozenCLIPEmbedderWithCustomWords(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string& embd_dir,
|
||||
SDVersion version = VERSION_SD1,
|
||||
PMVersion pv = PM_VERSION_1,
|
||||
int clip_skip = -1)
|
||||
PMVersion pv = PM_VERSION_1)
|
||||
: version(version), pm_version(pv), tokenizer(sd_version_is_sd2(version) ? 0 : 49407), embd_dir(embd_dir) {
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = 1;
|
||||
if (sd_version_is_sd2(version) || sd_version_is_sdxl(version)) {
|
||||
clip_skip = 2;
|
||||
}
|
||||
}
|
||||
bool force_clip_f32 = embd_dir.size() > 0;
|
||||
if (sd_version_is_sd1(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, clip_skip);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, force_clip_f32);
|
||||
} else if (sd_version_is_sd2(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "cond_stage_model.transformer.text_model", OPEN_CLIP_VIT_H_14, clip_skip);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "cond_stage_model.transformer.text_model", OPEN_CLIP_VIT_H_14, true, force_clip_f32);
|
||||
} else if (sd_version_is_sdxl(version)) {
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, clip_skip, false);
|
||||
text_model2 = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "cond_stage_model.1.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, clip_skip, false);
|
||||
}
|
||||
}
|
||||
|
||||
void set_clip_skip(int clip_skip) {
|
||||
text_model->set_clip_skip(clip_skip);
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->set_clip_skip(clip_skip);
|
||||
text_model = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, force_clip_f32);
|
||||
text_model2 = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "cond_stage_model.1.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, force_clip_f32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,33 +120,60 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
return true;
|
||||
}
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = 10 * 1024 * 1024; // max for custom embeddings 10 MB
|
||||
params.mem_size = 100 * 1024 * 1024; // max for custom embeddings 100 MB
|
||||
params.mem_buffer = NULL;
|
||||
params.no_alloc = false;
|
||||
struct ggml_context* embd_ctx = ggml_init(params);
|
||||
struct ggml_tensor* embd = NULL;
|
||||
int64_t hidden_size = text_model->model.hidden_size;
|
||||
struct ggml_tensor* embd2 = NULL;
|
||||
auto on_load = [&](const TensorStorage& tensor_storage, ggml_tensor** dst_tensor) {
|
||||
if (tensor_storage.ne[0] != hidden_size) {
|
||||
LOG_DEBUG("embedding wrong hidden size, got %i, expected %i", tensor_storage.ne[0], hidden_size);
|
||||
return false;
|
||||
if (tensor_storage.ne[0] != text_model->model.hidden_size) {
|
||||
if (text_model2) {
|
||||
if (tensor_storage.ne[0] == text_model2->model.hidden_size) {
|
||||
embd2 = ggml_new_tensor_2d(embd_ctx, tensor_storage.type, text_model2->model.hidden_size, tensor_storage.n_dims > 1 ? tensor_storage.ne[1] : 1);
|
||||
*dst_tensor = embd2;
|
||||
} else {
|
||||
LOG_DEBUG("embedding wrong hidden size, got %i, expected %i or %i", tensor_storage.ne[0], text_model->model.hidden_size, text_model2->model.hidden_size);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG("embedding wrong hidden size, got %i, expected %i", tensor_storage.ne[0], text_model->model.hidden_size);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
embd = ggml_new_tensor_2d(embd_ctx, tensor_storage.type, text_model->model.hidden_size, tensor_storage.n_dims > 1 ? tensor_storage.ne[1] : 1);
|
||||
*dst_tensor = embd;
|
||||
}
|
||||
embd = ggml_new_tensor_2d(embd_ctx, tensor_storage.type, hidden_size, tensor_storage.n_dims > 1 ? tensor_storage.ne[1] : 1);
|
||||
*dst_tensor = embd;
|
||||
return true;
|
||||
};
|
||||
model_loader.load_tensors(on_load, NULL);
|
||||
model_loader.load_tensors(on_load, 1);
|
||||
readed_embeddings.push_back(embd_name);
|
||||
token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd));
|
||||
memcpy((void*)(token_embed_custom.data() + num_custom_embeddings * hidden_size * ggml_type_size(embd->type)),
|
||||
embd->data,
|
||||
ggml_nbytes(embd));
|
||||
for (int i = 0; i < embd->ne[1]; i++) {
|
||||
bpe_tokens.push_back(text_model->model.vocab_size + num_custom_embeddings);
|
||||
// LOG_DEBUG("new custom token: %i", text_model.vocab_size + num_custom_embeddings);
|
||||
num_custom_embeddings++;
|
||||
if (embd) {
|
||||
int64_t hidden_size = text_model->model.hidden_size;
|
||||
token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd));
|
||||
memcpy((void*)(token_embed_custom.data() + num_custom_embeddings * hidden_size * ggml_type_size(embd->type)),
|
||||
embd->data,
|
||||
ggml_nbytes(embd));
|
||||
for (int i = 0; i < embd->ne[1]; i++) {
|
||||
bpe_tokens.push_back(text_model->model.vocab_size + num_custom_embeddings);
|
||||
// LOG_DEBUG("new custom token: %i", text_model.vocab_size + num_custom_embeddings);
|
||||
num_custom_embeddings++;
|
||||
}
|
||||
LOG_DEBUG("embedding '%s' applied, custom embeddings: %i", embd_name.c_str(), num_custom_embeddings);
|
||||
}
|
||||
if (embd2) {
|
||||
int64_t hidden_size = text_model2->model.hidden_size;
|
||||
token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd2));
|
||||
memcpy((void*)(token_embed_custom.data() + num_custom_embeddings_2 * hidden_size * ggml_type_size(embd2->type)),
|
||||
embd2->data,
|
||||
ggml_nbytes(embd2));
|
||||
for (int i = 0; i < embd2->ne[1]; i++) {
|
||||
bpe_tokens.push_back(text_model2->model.vocab_size + num_custom_embeddings_2);
|
||||
// LOG_DEBUG("new custom token: %i", text_model.vocab_size + num_custom_embeddings);
|
||||
num_custom_embeddings_2++;
|
||||
}
|
||||
LOG_DEBUG("embedding '%s' applied, custom embeddings: %i (text model 2)", embd_name.c_str(), num_custom_embeddings_2);
|
||||
}
|
||||
LOG_DEBUG("embedding '%s' applied, custom embeddings: %i", embd_name.c_str(), num_custom_embeddings);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -380,9 +401,8 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
set_clip_skip(clip_skip);
|
||||
int adm_in_channels = -1,
|
||||
bool zero_out_masked = false) {
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = NULL; // [N, n_token, hidden_size]
|
||||
struct ggml_tensor* chunk_hidden_states = NULL; // [n_token, hidden_size] or [n_token, hidden_size + hidden_size2]
|
||||
@@ -391,6 +411,10 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
struct ggml_tensor* pooled = NULL;
|
||||
std::vector<float> hidden_states_vec;
|
||||
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = (sd_version_is_sd2(version) || sd_version_is_sdxl(version)) ? 2 : 1;
|
||||
}
|
||||
|
||||
size_t chunk_len = 77;
|
||||
size_t chunk_count = tokens.size() / chunk_len;
|
||||
for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
|
||||
@@ -425,15 +449,17 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip,
|
||||
&chunk_hidden_states1,
|
||||
work_ctx);
|
||||
if (sd_version_is_sdxl(version)) {
|
||||
text_model2->compute(n_threads,
|
||||
input_ids2,
|
||||
0,
|
||||
NULL,
|
||||
num_custom_embeddings,
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip,
|
||||
&chunk_hidden_states2, work_ctx);
|
||||
// concat
|
||||
chunk_hidden_states = ggml_tensor_concat(work_ctx, chunk_hidden_states1, chunk_hidden_states2, 0);
|
||||
@@ -441,10 +467,11 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
if (chunk_idx == 0) {
|
||||
text_model2->compute(n_threads,
|
||||
input_ids2,
|
||||
0,
|
||||
NULL,
|
||||
num_custom_embeddings,
|
||||
token_embed_custom.data(),
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip,
|
||||
&pooled,
|
||||
work_ctx);
|
||||
}
|
||||
@@ -470,7 +497,7 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
float new_mean = ggml_tensor_mean(result);
|
||||
ggml_tensor_scale(result, (original_mean / new_mean));
|
||||
}
|
||||
if (force_zero_embeddings) {
|
||||
if (zero_out_masked) {
|
||||
float* vec = (float*)result->data;
|
||||
for (int i = 0; i < ggml_nelements(result); i++) {
|
||||
vec[i] = 0;
|
||||
@@ -528,20 +555,14 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
std::tuple<SDCondition, std::vector<bool>>
|
||||
get_learned_condition_with_trigger(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int num_input_imgs,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
const ConditionerParams& conditioner_params) {
|
||||
auto image_tokens = convert_token_to_id(trigger_word);
|
||||
// if(image_tokens.size() == 1){
|
||||
// printf(" image token id is: %d \n", image_tokens[0]);
|
||||
// }
|
||||
GGML_ASSERT(image_tokens.size() == 1);
|
||||
auto tokens_and_weights = tokenize_with_trigger_token(text,
|
||||
num_input_imgs,
|
||||
auto tokens_and_weights = tokenize_with_trigger_token(conditioner_params.text,
|
||||
conditioner_params.num_input_imgs,
|
||||
image_tokens[0],
|
||||
true);
|
||||
std::vector<int>& tokens = std::get<0>(tokens_and_weights);
|
||||
@@ -555,7 +576,15 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
// for(int i = 0; i < clsm.size(); ++i)
|
||||
// printf("%d ", clsm[i]?1:0);
|
||||
// printf("\n");
|
||||
auto cond = get_learned_condition_common(work_ctx, n_threads, tokens, weights, clip_skip, width, height, adm_in_channels, force_zero_embeddings);
|
||||
auto cond = get_learned_condition_common(work_ctx,
|
||||
n_threads,
|
||||
tokens,
|
||||
weights,
|
||||
conditioner_params.clip_skip,
|
||||
conditioner_params.width,
|
||||
conditioner_params.height,
|
||||
conditioner_params.adm_in_channels,
|
||||
conditioner_params.zero_out_masked);
|
||||
return std::make_tuple(cond, clsm);
|
||||
}
|
||||
|
||||
@@ -573,24 +602,29 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
|
||||
|
||||
SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
auto tokens_and_weights = tokenize(text, true);
|
||||
const ConditionerParams& conditioner_params) {
|
||||
auto tokens_and_weights = tokenize(conditioner_params.text, true);
|
||||
std::vector<int>& tokens = tokens_and_weights.first;
|
||||
std::vector<float>& weights = tokens_and_weights.second;
|
||||
return get_learned_condition_common(work_ctx, n_threads, tokens, weights, clip_skip, width, height, adm_in_channels, force_zero_embeddings);
|
||||
return get_learned_condition_common(work_ctx,
|
||||
n_threads,
|
||||
tokens,
|
||||
weights,
|
||||
conditioner_params.clip_skip,
|
||||
conditioner_params.width,
|
||||
conditioner_params.height,
|
||||
conditioner_params.adm_in_channels,
|
||||
conditioner_params.zero_out_masked);
|
||||
}
|
||||
};
|
||||
|
||||
struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
CLIPVisionModelProjection vision_model;
|
||||
|
||||
FrozenCLIPVisionEmbedder(ggml_backend_t backend, std::map<std::string, enum ggml_type>& tensor_types)
|
||||
: vision_model(OPEN_CLIP_VIT_H_14, true), GGMLRunner(backend) {
|
||||
FrozenCLIPVisionEmbedder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {})
|
||||
: vision_model(OPEN_CLIP_VIT_H_14), GGMLRunner(backend, offload_params_to_cpu) {
|
||||
vision_model.init(params_ctx, tensor_types, "cond_stage_model.transformer");
|
||||
}
|
||||
|
||||
@@ -602,12 +636,12 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
vision_model.get_param_tensors(tensors, "cond_stage_model.transformer");
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* pixel_values) {
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* pixel_values, bool return_pooled, int clip_skip) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
|
||||
pixel_values = to_backend(pixel_values);
|
||||
|
||||
struct ggml_tensor* hidden_states = vision_model.forward(compute_ctx, pixel_values);
|
||||
struct ggml_tensor* hidden_states = vision_model.forward(compute_ctx, runtime_backend, pixel_values, return_pooled, clip_skip);
|
||||
|
||||
ggml_build_forward_expand(gf, hidden_states);
|
||||
|
||||
@@ -616,10 +650,12 @@ struct FrozenCLIPVisionEmbedder : public GGMLRunner {
|
||||
|
||||
void compute(const int n_threads,
|
||||
ggml_tensor* pixel_values,
|
||||
bool return_pooled,
|
||||
int clip_skip,
|
||||
ggml_tensor** output,
|
||||
ggml_context* output_ctx) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(pixel_values);
|
||||
return build_graph(pixel_values, return_pooled, clip_skip);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
@@ -634,20 +670,12 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
std::shared_ptr<T5Runner> t5;
|
||||
|
||||
SD3CLIPEmbedder(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
int clip_skip = -1)
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {})
|
||||
: clip_g_tokenizer(0) {
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = 2;
|
||||
}
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, clip_skip, false);
|
||||
clip_g = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "text_encoders.clip_g.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, clip_skip, false);
|
||||
t5 = std::make_shared<T5Runner>(backend, tensor_types, "text_encoders.t5xxl.transformer");
|
||||
}
|
||||
|
||||
void set_clip_skip(int clip_skip) {
|
||||
clip_l->set_clip_skip(clip_skip);
|
||||
clip_g->set_clip_skip(clip_skip);
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, false);
|
||||
clip_g = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.clip_g.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false);
|
||||
t5 = std::make_shared<T5Runner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.t5xxl.transformer");
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
@@ -719,7 +747,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
|
||||
clip_l_tokenizer.pad_tokens(clip_l_tokens, clip_l_weights, max_length, padding);
|
||||
clip_g_tokenizer.pad_tokens(clip_g_tokens, clip_g_weights, max_length, padding);
|
||||
t5_tokenizer.pad_tokens(t5_tokens, t5_weights, max_length, padding);
|
||||
t5_tokenizer.pad_tokens(t5_tokens, t5_weights, NULL, max_length, padding);
|
||||
|
||||
// for (int i = 0; i < clip_l_tokens.size(); i++) {
|
||||
// std::cout << clip_l_tokens[i] << ":" << clip_l_weights[i] << ", ";
|
||||
@@ -743,8 +771,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
int n_threads,
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> token_and_weights,
|
||||
int clip_skip,
|
||||
bool force_zero_embeddings = false) {
|
||||
set_clip_skip(clip_skip);
|
||||
bool zero_out_masked = false) {
|
||||
auto& clip_l_tokens = token_and_weights[0].first;
|
||||
auto& clip_l_weights = token_and_weights[0].second;
|
||||
auto& clip_g_tokens = token_and_weights[1].first;
|
||||
@@ -752,6 +779,10 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
auto& t5_tokens = token_and_weights[2].first;
|
||||
auto& t5_weights = token_and_weights[2].second;
|
||||
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = 2;
|
||||
}
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = NULL; // [N, n_token*2, 4096]
|
||||
struct ggml_tensor* chunk_hidden_states = NULL; // [n_token*2, 4096]
|
||||
@@ -782,6 +813,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
NULL,
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip,
|
||||
&chunk_hidden_states_l,
|
||||
work_ctx);
|
||||
{
|
||||
@@ -809,6 +841,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
NULL,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip,
|
||||
&pooled_l,
|
||||
work_ctx);
|
||||
}
|
||||
@@ -830,6 +863,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
NULL,
|
||||
max_token_idx,
|
||||
false,
|
||||
clip_skip,
|
||||
&chunk_hidden_states_g,
|
||||
work_ctx);
|
||||
|
||||
@@ -858,6 +892,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
NULL,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip,
|
||||
&pooled_g,
|
||||
work_ctx);
|
||||
}
|
||||
@@ -874,6 +909,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
|
||||
t5->compute(n_threads,
|
||||
input_ids,
|
||||
NULL,
|
||||
&chunk_hidden_states_t5,
|
||||
work_ctx);
|
||||
{
|
||||
@@ -921,7 +957,7 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0);
|
||||
if (force_zero_embeddings) {
|
||||
if (zero_out_masked) {
|
||||
float* vec = (float*)chunk_hidden_states->data;
|
||||
for (int i = 0; i < ggml_nelements(chunk_hidden_states); i++) {
|
||||
vec[i] = 0;
|
||||
@@ -943,31 +979,13 @@ struct SD3CLIPEmbedder : public Conditioner {
|
||||
|
||||
SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
auto tokens_and_weights = tokenize(text, 77, true);
|
||||
return get_learned_condition_common(work_ctx, n_threads, tokens_and_weights, clip_skip, force_zero_embeddings);
|
||||
}
|
||||
|
||||
std::tuple<SDCondition, std::vector<bool>> get_learned_condition_with_trigger(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int num_input_imgs,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
GGML_ASSERT(0 && "Not implemented yet!");
|
||||
}
|
||||
|
||||
std::string remove_trigger_from_prompt(ggml_context* work_ctx,
|
||||
const std::string& prompt) {
|
||||
GGML_ASSERT(0 && "Not implemented yet!");
|
||||
const ConditionerParams& conditioner_params) {
|
||||
auto tokens_and_weights = tokenize(conditioner_params.text, 77, true);
|
||||
return get_learned_condition_common(work_ctx,
|
||||
n_threads,
|
||||
tokens_and_weights,
|
||||
conditioner_params.clip_skip,
|
||||
conditioner_params.zero_out_masked);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -976,19 +994,13 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
T5UniGramTokenizer t5_tokenizer;
|
||||
std::shared_ptr<CLIPTextModelRunner> clip_l;
|
||||
std::shared_ptr<T5Runner> t5;
|
||||
size_t chunk_len = 256;
|
||||
|
||||
FluxCLIPEmbedder(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
int clip_skip = -1) {
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = 2;
|
||||
}
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, tensor_types, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, clip_skip, true);
|
||||
t5 = std::make_shared<T5Runner>(backend, tensor_types, "text_encoders.t5xxl.transformer");
|
||||
}
|
||||
|
||||
void set_clip_skip(int clip_skip) {
|
||||
clip_l->set_clip_skip(clip_skip);
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {}) {
|
||||
clip_l = std::make_shared<CLIPTextModelRunner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, true);
|
||||
t5 = std::make_shared<T5Runner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.t5xxl.transformer");
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
@@ -1049,7 +1061,7 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
}
|
||||
|
||||
clip_l_tokenizer.pad_tokens(clip_l_tokens, clip_l_weights, 77, padding);
|
||||
t5_tokenizer.pad_tokens(t5_tokens, t5_weights, max_length, padding);
|
||||
t5_tokenizer.pad_tokens(t5_tokens, t5_weights, NULL, max_length, padding);
|
||||
|
||||
// for (int i = 0; i < clip_l_tokens.size(); i++) {
|
||||
// std::cout << clip_l_tokens[i] << ":" << clip_l_weights[i] << ", ";
|
||||
@@ -1068,20 +1080,22 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
int n_threads,
|
||||
std::vector<std::pair<std::vector<int>, std::vector<float>>> token_and_weights,
|
||||
int clip_skip,
|
||||
bool force_zero_embeddings = false) {
|
||||
set_clip_skip(clip_skip);
|
||||
bool zero_out_masked = false) {
|
||||
auto& clip_l_tokens = token_and_weights[0].first;
|
||||
auto& clip_l_weights = token_and_weights[0].second;
|
||||
auto& t5_tokens = token_and_weights[1].first;
|
||||
auto& t5_weights = token_and_weights[1].second;
|
||||
|
||||
if (clip_skip <= 0) {
|
||||
clip_skip = 2;
|
||||
}
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = NULL; // [N, n_token, 4096]
|
||||
struct ggml_tensor* chunk_hidden_states = NULL; // [n_token, 4096]
|
||||
struct ggml_tensor* pooled = NULL; // [768,]
|
||||
std::vector<float> hidden_states_vec;
|
||||
|
||||
size_t chunk_len = 256;
|
||||
size_t chunk_count = t5_tokens.size() / chunk_len;
|
||||
for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
|
||||
// clip_l
|
||||
@@ -1104,6 +1118,7 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
NULL,
|
||||
max_token_idx,
|
||||
true,
|
||||
clip_skip,
|
||||
&pooled,
|
||||
work_ctx);
|
||||
}
|
||||
@@ -1119,6 +1134,7 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
|
||||
t5->compute(n_threads,
|
||||
input_ids,
|
||||
NULL,
|
||||
&chunk_hidden_states,
|
||||
work_ctx);
|
||||
{
|
||||
@@ -1140,7 +1156,7 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0);
|
||||
if (force_zero_embeddings) {
|
||||
if (zero_out_masked) {
|
||||
float* vec = (float*)chunk_hidden_states->data;
|
||||
for (int i = 0; i < ggml_nelements(chunk_hidden_states); i++) {
|
||||
vec[i] = 0;
|
||||
@@ -1162,32 +1178,400 @@ struct FluxCLIPEmbedder : public Conditioner {
|
||||
|
||||
SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
auto tokens_and_weights = tokenize(text, 256, true);
|
||||
return get_learned_condition_common(work_ctx, n_threads, tokens_and_weights, clip_skip, force_zero_embeddings);
|
||||
}
|
||||
|
||||
std::tuple<SDCondition, std::vector<bool>> get_learned_condition_with_trigger(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const std::string& text,
|
||||
int clip_skip,
|
||||
int width,
|
||||
int height,
|
||||
int num_input_imgs,
|
||||
int adm_in_channels = -1,
|
||||
bool force_zero_embeddings = false) {
|
||||
GGML_ASSERT(0 && "Not implemented yet!");
|
||||
}
|
||||
|
||||
std::string remove_trigger_from_prompt(ggml_context* work_ctx,
|
||||
const std::string& prompt) {
|
||||
GGML_ASSERT(0 && "Not implemented yet!");
|
||||
const ConditionerParams& conditioner_params) {
|
||||
auto tokens_and_weights = tokenize(conditioner_params.text, chunk_len, true);
|
||||
return get_learned_condition_common(work_ctx,
|
||||
n_threads,
|
||||
tokens_and_weights,
|
||||
conditioner_params.clip_skip,
|
||||
conditioner_params.zero_out_masked);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
struct T5CLIPEmbedder : public Conditioner {
|
||||
T5UniGramTokenizer t5_tokenizer;
|
||||
std::shared_ptr<T5Runner> t5;
|
||||
size_t chunk_len = 512;
|
||||
bool use_mask = false;
|
||||
int mask_pad = 1;
|
||||
bool is_umt5 = false;
|
||||
|
||||
T5CLIPEmbedder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
bool use_mask = false,
|
||||
int mask_pad = 1,
|
||||
bool is_umt5 = false)
|
||||
: use_mask(use_mask), mask_pad(mask_pad), t5_tokenizer(is_umt5) {
|
||||
t5 = std::make_shared<T5Runner>(backend, offload_params_to_cpu, tensor_types, "text_encoders.t5xxl.transformer", is_umt5);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer");
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
t5->alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() {
|
||||
t5->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() {
|
||||
size_t buffer_size = 0;
|
||||
|
||||
buffer_size += t5->get_params_buffer_size();
|
||||
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
auto parsed_attention = parse_prompt_attention(text);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (const auto& item : parsed_attention) {
|
||||
ss << "['" << item.first << "', " << item.second << "], ";
|
||||
}
|
||||
ss << "]";
|
||||
LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str());
|
||||
}
|
||||
|
||||
auto on_new_token_cb = [&](std::string& str, std::vector<int32_t>& bpe_tokens) -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
std::vector<int> t5_tokens;
|
||||
std::vector<float> t5_weights;
|
||||
std::vector<float> t5_mask;
|
||||
for (const auto& item : parsed_attention) {
|
||||
const std::string& curr_text = item.first;
|
||||
float curr_weight = item.second;
|
||||
|
||||
std::vector<int> curr_tokens = t5_tokenizer.Encode(curr_text, true);
|
||||
t5_tokens.insert(t5_tokens.end(), curr_tokens.begin(), curr_tokens.end());
|
||||
t5_weights.insert(t5_weights.end(), curr_tokens.size(), curr_weight);
|
||||
}
|
||||
|
||||
t5_tokenizer.pad_tokens(t5_tokens, t5_weights, &t5_mask, max_length, padding);
|
||||
|
||||
return {t5_tokens, t5_weights, t5_mask};
|
||||
}
|
||||
|
||||
void modify_mask_to_attend_padding(struct ggml_tensor* mask, int max_seq_length, int num_extra_padding = 8) {
|
||||
float* mask_data = (float*)mask->data;
|
||||
int num_pad = 0;
|
||||
for (int64_t i = 0; i < max_seq_length; i++) {
|
||||
if (num_pad >= num_extra_padding) {
|
||||
break;
|
||||
}
|
||||
if (std::isinf(mask_data[i])) {
|
||||
mask_data[i] = 0;
|
||||
++num_pad;
|
||||
}
|
||||
}
|
||||
// LOG_DEBUG("PAD: %d", num_pad);
|
||||
}
|
||||
|
||||
SDCondition get_learned_condition_common(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> token_and_weights,
|
||||
int clip_skip,
|
||||
bool zero_out_masked = false) {
|
||||
auto& t5_tokens = std::get<0>(token_and_weights);
|
||||
auto& t5_weights = std::get<1>(token_and_weights);
|
||||
auto& t5_attn_mask_vec = std::get<2>(token_and_weights);
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = NULL; // [N, n_token, 4096]
|
||||
struct ggml_tensor* chunk_hidden_states = NULL; // [n_token, 4096]
|
||||
struct ggml_tensor* pooled = NULL;
|
||||
struct ggml_tensor* t5_attn_mask = vector_to_ggml_tensor(work_ctx, t5_attn_mask_vec); // [n_token]
|
||||
|
||||
std::vector<float> hidden_states_vec;
|
||||
|
||||
size_t chunk_count = t5_tokens.size() / chunk_len;
|
||||
|
||||
for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) {
|
||||
// t5
|
||||
std::vector<int> chunk_tokens(t5_tokens.begin() + chunk_idx * chunk_len,
|
||||
t5_tokens.begin() + (chunk_idx + 1) * chunk_len);
|
||||
std::vector<float> chunk_weights(t5_weights.begin() + chunk_idx * chunk_len,
|
||||
t5_weights.begin() + (chunk_idx + 1) * chunk_len);
|
||||
std::vector<float> chunk_mask(t5_attn_mask_vec.begin() + chunk_idx * chunk_len,
|
||||
t5_attn_mask_vec.begin() + (chunk_idx + 1) * chunk_len);
|
||||
|
||||
auto input_ids = vector_to_ggml_tensor_i32(work_ctx, chunk_tokens);
|
||||
auto t5_attn_mask_chunk = use_mask ? vector_to_ggml_tensor(work_ctx, chunk_mask) : NULL;
|
||||
|
||||
t5->compute(n_threads,
|
||||
input_ids,
|
||||
t5_attn_mask_chunk,
|
||||
&chunk_hidden_states,
|
||||
work_ctx);
|
||||
{
|
||||
auto tensor = chunk_hidden_states;
|
||||
float original_mean = ggml_tensor_mean(tensor);
|
||||
for (int i2 = 0; i2 < tensor->ne[2]; i2++) {
|
||||
for (int i1 = 0; i1 < tensor->ne[1]; i1++) {
|
||||
for (int i0 = 0; i0 < tensor->ne[0]; i0++) {
|
||||
float value = ggml_tensor_get_f32(tensor, i0, i1, i2);
|
||||
value *= chunk_weights[i1];
|
||||
ggml_tensor_set_f32(tensor, value, i0, i1, i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
float new_mean = ggml_tensor_mean(tensor);
|
||||
ggml_tensor_scale(tensor, (original_mean / new_mean));
|
||||
}
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0);
|
||||
if (zero_out_masked) {
|
||||
auto tensor = chunk_hidden_states;
|
||||
for (int i2 = 0; i2 < tensor->ne[2]; i2++) {
|
||||
for (int i1 = 0; i1 < tensor->ne[1]; i1++) {
|
||||
for (int i0 = 0; i0 < tensor->ne[0]; i0++) {
|
||||
if (chunk_mask[i1] < 0.f) {
|
||||
ggml_tensor_set_f32(tensor, 0.f, i0, i1, i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hidden_states_vec.insert(hidden_states_vec.end(),
|
||||
(float*)chunk_hidden_states->data,
|
||||
((float*)chunk_hidden_states->data) + ggml_nelements(chunk_hidden_states));
|
||||
}
|
||||
|
||||
GGML_ASSERT(hidden_states_vec.size() > 0);
|
||||
hidden_states = vector_to_ggml_tensor(work_ctx, hidden_states_vec);
|
||||
hidden_states = ggml_reshape_2d(work_ctx,
|
||||
hidden_states,
|
||||
chunk_hidden_states->ne[0],
|
||||
ggml_nelements(hidden_states) / chunk_hidden_states->ne[0]);
|
||||
|
||||
modify_mask_to_attend_padding(t5_attn_mask, ggml_nelements(t5_attn_mask), mask_pad);
|
||||
|
||||
return SDCondition(hidden_states, t5_attn_mask, NULL);
|
||||
}
|
||||
|
||||
SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const ConditionerParams& conditioner_params) {
|
||||
auto tokens_and_weights = tokenize(conditioner_params.text, chunk_len, true);
|
||||
return get_learned_condition_common(work_ctx,
|
||||
n_threads,
|
||||
tokens_and_weights,
|
||||
conditioner_params.clip_skip,
|
||||
conditioner_params.zero_out_masked);
|
||||
}
|
||||
};
|
||||
|
||||
struct Qwen2_5_VLCLIPEmbedder : public Conditioner {
|
||||
Qwen::Qwen2Tokenizer tokenizer;
|
||||
std::shared_ptr<Qwen::Qwen2_5_VLRunner> qwenvl;
|
||||
|
||||
Qwen2_5_VLCLIPEmbedder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "",
|
||||
bool enable_vision = false) {
|
||||
qwenvl = std::make_shared<Qwen::Qwen2_5_VLRunner>(backend,
|
||||
offload_params_to_cpu,
|
||||
tensor_types,
|
||||
"text_encoders.qwen2vl",
|
||||
enable_vision);
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
qwenvl->get_param_tensors(tensors, "text_encoders.qwen2vl");
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
qwenvl->alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() {
|
||||
qwenvl->free_params_buffer();
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() {
|
||||
size_t buffer_size = 0;
|
||||
buffer_size += qwenvl->get_params_buffer_size();
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
std::tuple<std::vector<int>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
size_t system_prompt_length = 0,
|
||||
bool padding = false) {
|
||||
std::vector<std::pair<std::string, float>> parsed_attention;
|
||||
if (system_prompt_length > 0) {
|
||||
parsed_attention.emplace_back(text.substr(0, system_prompt_length), 1.f);
|
||||
auto new_parsed_attention = parse_prompt_attention(text.substr(system_prompt_length, text.size() - system_prompt_length));
|
||||
parsed_attention.insert(parsed_attention.end(),
|
||||
new_parsed_attention.begin(),
|
||||
new_parsed_attention.end());
|
||||
} else {
|
||||
parsed_attention = parse_prompt_attention(text);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (const auto& item : parsed_attention) {
|
||||
ss << "['" << item.first << "', " << item.second << "], ";
|
||||
}
|
||||
ss << "]";
|
||||
LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str());
|
||||
}
|
||||
|
||||
std::vector<int> tokens;
|
||||
std::vector<float> weights;
|
||||
for (const auto& item : parsed_attention) {
|
||||
const std::string& curr_text = item.first;
|
||||
float curr_weight = item.second;
|
||||
std::vector<int> curr_tokens = tokenizer.tokenize(curr_text, nullptr);
|
||||
tokens.insert(tokens.end(), curr_tokens.begin(), curr_tokens.end());
|
||||
weights.insert(weights.end(), curr_tokens.size(), curr_weight);
|
||||
}
|
||||
|
||||
tokenizer.pad_tokens(tokens, weights, max_length, padding);
|
||||
|
||||
// for (int i = 0; i < tokens.size(); i++) {
|
||||
// std::cout << tokens[i] << ":" << weights[i] << ", " << i << std::endl;
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
|
||||
return {tokens, weights};
|
||||
}
|
||||
|
||||
SDCondition get_learned_condition(ggml_context* work_ctx,
|
||||
int n_threads,
|
||||
const ConditionerParams& conditioner_params) {
|
||||
std::string prompt;
|
||||
std::vector<std::pair<int, ggml_tensor*>> image_embeds;
|
||||
size_t system_prompt_length = 0;
|
||||
int prompt_template_encode_start_idx = 34;
|
||||
if (qwenvl->enable_vision && conditioner_params.ref_images.size() > 0) {
|
||||
LOG_INFO("QwenImageEditPlusPipeline");
|
||||
prompt_template_encode_start_idx = 64;
|
||||
int image_embed_idx = 64 + 6;
|
||||
|
||||
int min_pixels = 384 * 384;
|
||||
int max_pixels = 560 * 560;
|
||||
std::string placeholder = "<|image_pad|>";
|
||||
std::string img_prompt;
|
||||
|
||||
for (int i = 0; i < conditioner_params.ref_images.size(); i++) {
|
||||
sd_image_f32_t image = sd_image_t_to_sd_image_f32_t(*conditioner_params.ref_images[i]);
|
||||
double factor = qwenvl->params.vision.patch_size * qwenvl->params.vision.spatial_merge_size;
|
||||
int height = image.height;
|
||||
int width = image.width;
|
||||
int h_bar = static_cast<int>(std::round(height / factor)) * factor;
|
||||
int w_bar = static_cast<int>(std::round(width / factor)) * factor;
|
||||
|
||||
if (static_cast<double>(h_bar) * w_bar > max_pixels) {
|
||||
double beta = std::sqrt((height * width) / static_cast<double>(max_pixels));
|
||||
h_bar = std::max(static_cast<int>(factor),
|
||||
static_cast<int>(std::floor(height / beta / factor)) * static_cast<int>(factor));
|
||||
w_bar = std::max(static_cast<int>(factor),
|
||||
static_cast<int>(std::floor(width / beta / factor)) * static_cast<int>(factor));
|
||||
} else if (static_cast<double>(h_bar) * w_bar < min_pixels) {
|
||||
double beta = std::sqrt(static_cast<double>(min_pixels) / (height * width));
|
||||
h_bar = static_cast<int>(std::ceil(height * beta / factor)) * static_cast<int>(factor);
|
||||
w_bar = static_cast<int>(std::ceil(width * beta / factor)) * static_cast<int>(factor);
|
||||
}
|
||||
|
||||
LOG_DEBUG("resize conditioner ref image %d from %dx%d to %dx%d", i, image.height, image.width, h_bar, w_bar);
|
||||
|
||||
sd_image_f32_t resized_image = clip_preprocess(image, w_bar, h_bar);
|
||||
free(image.data);
|
||||
image.data = nullptr;
|
||||
|
||||
ggml_tensor* image_tensor = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, resized_image.width, resized_image.height, 3, 1);
|
||||
sd_image_f32_to_tensor(resized_image, image_tensor, false);
|
||||
free(resized_image.data);
|
||||
resized_image.data = nullptr;
|
||||
|
||||
ggml_tensor* image_embed = nullptr;
|
||||
qwenvl->encode_image(n_threads, image_tensor, &image_embed, work_ctx);
|
||||
image_embeds.emplace_back(image_embed_idx, image_embed);
|
||||
image_embed_idx += 1 + image_embed->ne[1] + 6;
|
||||
|
||||
img_prompt += "Picture " + std::to_string(i + 1) + ": <|vision_start|>"; // [24669, 220, index, 25, 220, 151652]
|
||||
int64_t num_image_tokens = image_embed->ne[1];
|
||||
img_prompt.reserve(num_image_tokens * placeholder.size());
|
||||
for (int j = 0; j < num_image_tokens; j++) {
|
||||
img_prompt += placeholder;
|
||||
}
|
||||
img_prompt += "<|vision_end|>";
|
||||
}
|
||||
|
||||
prompt = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n";
|
||||
|
||||
system_prompt_length = prompt.size();
|
||||
|
||||
prompt += img_prompt;
|
||||
prompt += conditioner_params.text;
|
||||
prompt += "<|im_end|>\n<|im_start|>assistant\n";
|
||||
} else {
|
||||
prompt = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n" + conditioner_params.text + "<|im_end|>\n<|im_start|>assistant\n";
|
||||
}
|
||||
|
||||
auto tokens_and_weights = tokenize(prompt, 0, system_prompt_length, false);
|
||||
auto& tokens = std::get<0>(tokens_and_weights);
|
||||
auto& weights = std::get<1>(tokens_and_weights);
|
||||
|
||||
int64_t t0 = ggml_time_ms();
|
||||
struct ggml_tensor* hidden_states = NULL; // [N, n_token, 3584]
|
||||
|
||||
auto input_ids = vector_to_ggml_tensor_i32(work_ctx, tokens);
|
||||
|
||||
qwenvl->compute(n_threads,
|
||||
input_ids,
|
||||
image_embeds,
|
||||
&hidden_states,
|
||||
work_ctx);
|
||||
{
|
||||
auto tensor = hidden_states;
|
||||
float original_mean = ggml_tensor_mean(tensor);
|
||||
for (int i2 = 0; i2 < tensor->ne[2]; i2++) {
|
||||
for (int i1 = 0; i1 < tensor->ne[1]; i1++) {
|
||||
for (int i0 = 0; i0 < tensor->ne[0]; i0++) {
|
||||
float value = ggml_tensor_get_f32(tensor, i0, i1, i2);
|
||||
value *= weights[i1];
|
||||
ggml_tensor_set_f32(tensor, value, i0, i1, i2);
|
||||
}
|
||||
}
|
||||
}
|
||||
float new_mean = ggml_tensor_mean(tensor);
|
||||
ggml_tensor_scale(tensor, (original_mean / new_mean));
|
||||
}
|
||||
|
||||
GGML_ASSERT(hidden_states->ne[1] > prompt_template_encode_start_idx);
|
||||
|
||||
ggml_tensor* new_hidden_states = ggml_new_tensor_3d(work_ctx,
|
||||
GGML_TYPE_F32,
|
||||
hidden_states->ne[0],
|
||||
hidden_states->ne[1] - prompt_template_encode_start_idx,
|
||||
hidden_states->ne[2]);
|
||||
|
||||
ggml_tensor_iter(new_hidden_states, [&](ggml_tensor* new_hidden_states, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
|
||||
float value = ggml_tensor_get_f32(hidden_states, i0, i1 + prompt_template_encode_start_idx, i2, i3);
|
||||
ggml_tensor_set_f32(new_hidden_states, value, i0, i1, i2, i3);
|
||||
});
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0);
|
||||
return SDCondition(new_hidden_states, nullptr, nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+26
-11
@@ -174,10 +174,11 @@ public:
|
||||
|
||||
struct ggml_tensor* attention_layer_forward(std::string name,
|
||||
struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context) {
|
||||
auto block = std::dynamic_pointer_cast<SpatialTransformer>(blocks[name]);
|
||||
return block->forward(ctx, x, context);
|
||||
return block->forward(ctx, backend, x, context);
|
||||
}
|
||||
|
||||
struct ggml_tensor* input_hint_block_forward(struct ggml_context* ctx,
|
||||
@@ -199,6 +200,7 @@ public:
|
||||
}
|
||||
|
||||
std::vector<struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* hint,
|
||||
struct ggml_tensor* guided_hint,
|
||||
@@ -272,7 +274,7 @@ public:
|
||||
h = resblock_forward(name, ctx, h, emb); // [N, mult*model_channels, h, w]
|
||||
if (std::find(attention_resolutions.begin(), attention_resolutions.end(), ds) != attention_resolutions.end()) {
|
||||
std::string name = "input_blocks." + std::to_string(input_block_idx) + ".1";
|
||||
h = attention_layer_forward(name, ctx, h, context); // [N, mult*model_channels, h, w]
|
||||
h = attention_layer_forward(name, ctx, backend, h, context); // [N, mult*model_channels, h, w]
|
||||
}
|
||||
|
||||
auto zero_conv = std::dynamic_pointer_cast<Conv2d>(blocks["zero_convs." + std::to_string(input_block_idx) + ".0"]);
|
||||
@@ -296,9 +298,9 @@ public:
|
||||
// [N, 4*model_channels, h/8, w/8]
|
||||
|
||||
// middle_block
|
||||
h = resblock_forward("middle_block.0", ctx, h, emb); // [N, 4*model_channels, h/8, w/8]
|
||||
h = attention_layer_forward("middle_block.1", ctx, h, context); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.2", ctx, h, emb); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.0", ctx, h, emb); // [N, 4*model_channels, h/8, w/8]
|
||||
h = attention_layer_forward("middle_block.1", ctx, backend, h, context); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.2", ctx, h, emb); // [N, 4*model_channels, h/8, w/8]
|
||||
|
||||
// out
|
||||
outs.push_back(middle_block_out->forward(ctx, h));
|
||||
@@ -317,12 +319,24 @@ struct ControlNet : public GGMLRunner {
|
||||
bool guided_hint_cached = false;
|
||||
|
||||
ControlNet(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: GGMLRunner(backend), control_net(version) {
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
SDVersion version = VERSION_SD1)
|
||||
: GGMLRunner(backend, offload_params_to_cpu), control_net(version) {
|
||||
control_net.init(params_ctx, tensor_types, "");
|
||||
}
|
||||
|
||||
void enable_conv2d_direct() {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
control_net.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->enable_direct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ControlNet() {
|
||||
free_control_ctx();
|
||||
}
|
||||
@@ -346,7 +360,7 @@ struct ControlNet : public GGMLRunner {
|
||||
control_buffer_size += ggml_nbytes(controls[i]);
|
||||
}
|
||||
|
||||
control_buffer = ggml_backend_alloc_ctx_tensors(control_ctx, backend);
|
||||
control_buffer = ggml_backend_alloc_ctx_tensors(control_ctx, runtime_backend);
|
||||
|
||||
LOG_DEBUG("control buffer size %.2fMB", control_buffer_size * 1.f / 1024.f / 1024.f);
|
||||
}
|
||||
@@ -391,6 +405,7 @@ struct ControlNet : public GGMLRunner {
|
||||
timesteps = to_backend(timesteps);
|
||||
|
||||
auto outs = control_net.forward(compute_ctx,
|
||||
runtime_backend,
|
||||
x,
|
||||
hint,
|
||||
guided_hint_cached ? guided_hint : NULL,
|
||||
@@ -430,7 +445,7 @@ struct ControlNet : public GGMLRunner {
|
||||
guided_hint_cached = true;
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path) {
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading control net from '%s'", file_path.c_str());
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
@@ -443,7 +458,7 @@ struct ControlNet : public GGMLRunner {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors, backend, ignore_tensors);
|
||||
bool success = model_loader.load_tensors(tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load control net tensors from model loader failed");
|
||||
|
||||
+494
-37
@@ -168,24 +168,21 @@ struct AYSSchedule : SigmaSchedule {
|
||||
std::vector<float> inputs;
|
||||
std::vector<float> results(n + 1);
|
||||
|
||||
switch (version) {
|
||||
case VERSION_SD2: /* fallthrough */
|
||||
LOG_WARN("AYS not designed for SD2.X models");
|
||||
case VERSION_SD1:
|
||||
LOG_INFO("AYS using SD1.5 noise levels");
|
||||
inputs = noise_levels[0];
|
||||
break;
|
||||
case VERSION_SDXL:
|
||||
LOG_INFO("AYS using SDXL noise levels");
|
||||
inputs = noise_levels[1];
|
||||
break;
|
||||
case VERSION_SVD:
|
||||
LOG_INFO("AYS using SVD noise levels");
|
||||
inputs = noise_levels[2];
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Version not compatable with AYS scheduler");
|
||||
return results;
|
||||
if (sd_version_is_sd2((SDVersion)version)) {
|
||||
LOG_WARN("AYS not designed for SD2.X models");
|
||||
} /* fallthrough */
|
||||
else if (sd_version_is_sd1((SDVersion)version)) {
|
||||
LOG_INFO("AYS using SD1.5 noise levels");
|
||||
inputs = noise_levels[0];
|
||||
} else if (sd_version_is_sdxl((SDVersion)version)) {
|
||||
LOG_INFO("AYS using SDXL noise levels");
|
||||
inputs = noise_levels[1];
|
||||
} else if (version == VERSION_SVD) {
|
||||
LOG_INFO("AYS using SVD noise levels");
|
||||
inputs = noise_levels[2];
|
||||
} else {
|
||||
LOG_ERROR("Version not compatible with AYS scheduler");
|
||||
return results;
|
||||
}
|
||||
|
||||
/* Stretches those pre-calculated reference levels out to the desired
|
||||
@@ -235,6 +232,25 @@ struct GITSSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct SGMUniformSchedule : SigmaSchedule {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min_in, float sigma_max_in, t_to_sigma_t t_to_sigma_func) override {
|
||||
std::vector<float> result;
|
||||
if (n == 0) {
|
||||
result.push_back(0.0f);
|
||||
return result;
|
||||
}
|
||||
result.reserve(n + 1);
|
||||
int t_max = TIMESTEPS - 1;
|
||||
int t_min = 0;
|
||||
std::vector<float> timesteps = linear_space(static_cast<float>(t_max), static_cast<float>(t_min), n + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
result.push_back(t_to_sigma_func(timesteps[i]));
|
||||
}
|
||||
result.push_back(0.0f);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct KarrasSchedule : SigmaSchedule {
|
||||
std::vector<float> get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) {
|
||||
// These *COULD* be function arguments here,
|
||||
@@ -254,8 +270,66 @@ struct KarrasSchedule : SigmaSchedule {
|
||||
}
|
||||
};
|
||||
|
||||
struct SimpleSchedule : SigmaSchedule {
|
||||
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> result_sigmas;
|
||||
|
||||
if (n == 0) {
|
||||
return result_sigmas;
|
||||
}
|
||||
|
||||
result_sigmas.reserve(n + 1);
|
||||
|
||||
int model_sigmas_len = TIMESTEPS;
|
||||
|
||||
float step_factor = static_cast<float>(model_sigmas_len) / static_cast<float>(n);
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
int offset_from_start_of_py_array = static_cast<int>(static_cast<float>(i) * step_factor);
|
||||
int timestep_index = model_sigmas_len - 1 - offset_from_start_of_py_array;
|
||||
|
||||
if (timestep_index < 0) {
|
||||
timestep_index = 0;
|
||||
}
|
||||
|
||||
result_sigmas.push_back(t_to_sigma(static_cast<float>(timestep_index)));
|
||||
}
|
||||
result_sigmas.push_back(0.0f);
|
||||
return result_sigmas;
|
||||
}
|
||||
};
|
||||
|
||||
// Close to Beta Schedule, but increadably simple in code.
|
||||
struct SmoothStepSchedule : SigmaSchedule {
|
||||
static constexpr float smoothstep(float x) {
|
||||
return x * x * (3.0f - 2.0f * x);
|
||||
}
|
||||
|
||||
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> result;
|
||||
result.reserve(n + 1);
|
||||
|
||||
const int t_max = TIMESTEPS - 1;
|
||||
if (n == 0) {
|
||||
return result;
|
||||
} else if (n == 1) {
|
||||
result.push_back(t_to_sigma((float)t_max));
|
||||
result.push_back(0.f);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
float u = 1.f - float(i) / float(n);
|
||||
result.push_back(t_to_sigma(std::round(smoothstep(u) * t_max)));
|
||||
}
|
||||
|
||||
result.push_back(0.f);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct Denoiser {
|
||||
std::shared_ptr<SigmaSchedule> schedule = std::make_shared<DiscreteSchedule>();
|
||||
std::shared_ptr<SigmaSchedule> scheduler = std::make_shared<DiscreteSchedule>();
|
||||
virtual float sigma_min() = 0;
|
||||
virtual float sigma_max() = 0;
|
||||
virtual float sigma_to_t(float sigma) = 0;
|
||||
@@ -266,7 +340,7 @@ struct Denoiser {
|
||||
|
||||
virtual std::vector<float> get_sigmas(uint32_t n) {
|
||||
auto bound_t_to_sigma = std::bind(&Denoiser::t_to_sigma, this, std::placeholders::_1);
|
||||
return schedule->get_sigmas(n, sigma_min(), sigma_max(), bound_t_to_sigma);
|
||||
return scheduler->get_sigmas(n, sigma_min(), sigma_max(), bound_t_to_sigma);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -346,6 +420,32 @@ struct CompVisVDenoiser : public CompVisDenoiser {
|
||||
}
|
||||
};
|
||||
|
||||
struct EDMVDenoiser : public CompVisVDenoiser {
|
||||
float min_sigma = 0.002;
|
||||
float max_sigma = 120.0;
|
||||
|
||||
EDMVDenoiser(float min_sigma = 0.002, float max_sigma = 120.0)
|
||||
: min_sigma(min_sigma), max_sigma(max_sigma) {
|
||||
scheduler = std::make_shared<ExponentialSchedule>();
|
||||
}
|
||||
|
||||
float t_to_sigma(float t) {
|
||||
return std::exp(t * 4 / (float)TIMESTEPS);
|
||||
}
|
||||
|
||||
float sigma_to_t(float s) {
|
||||
return 0.25 * std::log(s);
|
||||
}
|
||||
|
||||
float sigma_min() {
|
||||
return min_sigma;
|
||||
}
|
||||
|
||||
float sigma_max() {
|
||||
return max_sigma;
|
||||
}
|
||||
};
|
||||
|
||||
float time_snr_shift(float alpha, float t) {
|
||||
if (alpha == 1.0f) {
|
||||
return t;
|
||||
@@ -359,7 +459,8 @@ struct DiscreteFlowDenoiser : public Denoiser {
|
||||
|
||||
float sigma_data = 1.0f;
|
||||
|
||||
DiscreteFlowDenoiser() {
|
||||
DiscreteFlowDenoiser(float shift = 3.0f)
|
||||
: shift(shift) {
|
||||
set_parameters();
|
||||
}
|
||||
|
||||
@@ -474,7 +575,8 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
ggml_context* work_ctx,
|
||||
ggml_tensor* x,
|
||||
std::vector<float> sigmas,
|
||||
std::shared_ptr<RNG> rng) {
|
||||
std::shared_ptr<RNG> rng,
|
||||
float eta) {
|
||||
size_t steps = sigmas.size() - 1;
|
||||
// sample_euler_ancestral
|
||||
switch (method) {
|
||||
@@ -668,7 +770,6 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
} break;
|
||||
case DPMPP2S_A: {
|
||||
struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* d = ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
@@ -683,22 +784,15 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
auto sigma_fn = [](float t) -> float { return exp(-t); };
|
||||
|
||||
if (sigma_down == 0) {
|
||||
// Euler step
|
||||
float* vec_d = (float*)d->data;
|
||||
// d = (x - denoised) / sigmas[i];
|
||||
// dt = sigma_down - sigmas[i];
|
||||
// x += d * dt;
|
||||
// => x = denoised
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
|
||||
for (int j = 0; j < ggml_nelements(d); j++) {
|
||||
vec_d[j] = (vec_x[j] - vec_denoised[j]) / sigmas[i];
|
||||
}
|
||||
|
||||
// TODO: If sigma_down == 0, isn't this wrong?
|
||||
// But
|
||||
// https://github.com/crowsonkb/k-diffusion/blob/master/k_diffusion/sampling.py#L525
|
||||
// has this exactly the same way.
|
||||
float dt = sigma_down - sigmas[i];
|
||||
for (int j = 0; j < ggml_nelements(d); j++) {
|
||||
vec_x[j] = vec_x[j] + vec_d[j] * dt;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] = vec_denoised[j];
|
||||
}
|
||||
} else {
|
||||
// DPM-Solver++(2S)
|
||||
@@ -707,7 +801,6 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
float h = t_next - t;
|
||||
float s = t + 0.5f * h;
|
||||
|
||||
float* vec_d = (float*)d->data;
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_x2 = (float*)x2->data;
|
||||
float* vec_denoised = (float*)denoised->data;
|
||||
@@ -1005,6 +1098,370 @@ static void sample_k_diffusion(sample_method_t method,
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DDIM_TRAILING: // Denoising Diffusion Implicit Models
|
||||
// with the "trailing" timestep spacing
|
||||
{
|
||||
// See J. Song et al., "Denoising Diffusion Implicit
|
||||
// Models", arXiv:2010.02502 [cs.LG]
|
||||
//
|
||||
// DDIM itself needs alphas_cumprod (DDPM, J. Ho et al.,
|
||||
// arXiv:2006.11239 [cs.LG] with k-diffusion's start and
|
||||
// end beta) (which unfortunately k-diffusion's data
|
||||
// structure hides from the denoiser), and the sigmas are
|
||||
// also needed to invert the behavior of CompVisDenoiser
|
||||
// (k-diffusion's LMSDiscreteScheduler)
|
||||
float beta_start = 0.00085f;
|
||||
float beta_end = 0.0120f;
|
||||
std::vector<double> alphas_cumprod;
|
||||
std::vector<double> compvis_sigmas;
|
||||
|
||||
alphas_cumprod.reserve(TIMESTEPS);
|
||||
compvis_sigmas.reserve(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]);
|
||||
}
|
||||
|
||||
struct ggml_tensor* pred_original_sample =
|
||||
ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* variance_noise =
|
||||
ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// The "trailing" DDIM timestep, see S. Lin et al.,
|
||||
// "Common Diffusion Noise Schedules and Sample Steps
|
||||
// are Flawed", arXiv:2305.08891 [cs], p. 4, Table
|
||||
// 2. Most variables below follow Diffusers naming
|
||||
//
|
||||
// Diffuser naming vs. Song et al. (2010), p. 5, (12)
|
||||
// and p. 16, (16) (<variable name> -> <name in
|
||||
// paper>):
|
||||
//
|
||||
// - pred_noise_t -> epsilon_theta^(t)(x_t)
|
||||
// - pred_original_sample -> f_theta^(t)(x_t) or x_0
|
||||
// - std_dev_t -> sigma_t (not the LMS sigma)
|
||||
// - eta -> eta (set to 0 at the moment)
|
||||
// - pred_sample_direction -> "direction pointing to
|
||||
// x_t"
|
||||
// - pred_prev_sample -> "x_t-1"
|
||||
int timestep =
|
||||
roundf(TIMESTEPS -
|
||||
i * ((float)TIMESTEPS / steps)) -
|
||||
1;
|
||||
// 1. get previous step value (=t-1)
|
||||
int prev_timestep = timestep - TIMESTEPS / steps;
|
||||
// The sigma here is chosen to cause the
|
||||
// CompVisDenoiser to produce t = timestep
|
||||
float sigma = compvis_sigmas[timestep];
|
||||
if (i == 0) {
|
||||
// The function add_noise intializes x to
|
||||
// Diffusers' latents * sigma (as in Diffusers'
|
||||
// pipeline) or sample * sigma (Diffusers'
|
||||
// scheduler), where this sigma = init_noise_sigma
|
||||
// in Diffusers. For DDPM and DDIM however,
|
||||
// init_noise_sigma = 1. But the k-diffusion
|
||||
// model() also evaluates F_theta(c_in(sigma) x;
|
||||
// ...) instead of the bare U-net F_theta, with
|
||||
// c_in = 1 / sqrt(sigma^2 + 1), as defined in
|
||||
// T. Karras et al., "Elucidating the Design Space
|
||||
// of Diffusion-Based Generative Models",
|
||||
// arXiv:2206.00364 [cs.CV], p. 3, Table 1. Hence
|
||||
// the first call has to be prescaled as x <- x /
|
||||
// (c_in * sigma) with the k-diffusion pipeline
|
||||
// and CompVisDenoiser.
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] *= std::sqrt(sigma * sigma + 1) /
|
||||
sigma;
|
||||
}
|
||||
} else {
|
||||
// For the subsequent steps after the first one,
|
||||
// at this point x = latents or x = sample, and
|
||||
// needs to be prescaled with x <- sample / c_in
|
||||
// to compensate for model() applying the scale
|
||||
// c_in before the U-net F_theta
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] *= std::sqrt(sigma * sigma + 1);
|
||||
}
|
||||
}
|
||||
// Note (also noise_pred in Diffuser's pipeline)
|
||||
// model_output = model() is the D(x, sigma) as
|
||||
// defined in Karras et al. (2022), p. 3, Table 1 and
|
||||
// p. 8 (7), compare also p. 38 (226) therein.
|
||||
struct ggml_tensor* model_output =
|
||||
model(x, sigma, i + 1);
|
||||
// Here model_output is still the k-diffusion denoiser
|
||||
// output, not the U-net output F_theta(c_in(sigma) x;
|
||||
// ...) in Karras et al. (2022), whereas Diffusers'
|
||||
// model_output is F_theta(...). Recover the actual
|
||||
// model_output, which is also referred to as the
|
||||
// "Karras ODE derivative" d or d_cur in several
|
||||
// samplers above.
|
||||
{
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_model_output =
|
||||
(float*)model_output->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_model_output[j] =
|
||||
(vec_x[j] - vec_model_output[j]) *
|
||||
(1 / sigma);
|
||||
}
|
||||
}
|
||||
// 2. compute alphas, betas
|
||||
float alpha_prod_t = alphas_cumprod[timestep];
|
||||
// Note final_alpha_cumprod = alphas_cumprod[0] due to
|
||||
// trailing timestep spacing
|
||||
float alpha_prod_t_prev = prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0];
|
||||
float beta_prod_t = 1 - alpha_prod_t;
|
||||
// 3. compute predicted original sample from predicted
|
||||
// noise also called "predicted x_0" of formula (12)
|
||||
// from https://arxiv.org/pdf/2010.02502.pdf
|
||||
{
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_model_output =
|
||||
(float*)model_output->data;
|
||||
float* vec_pred_original_sample =
|
||||
(float*)pred_original_sample->data;
|
||||
// Note the substitution of latents or sample = x
|
||||
// * c_in = x / sqrt(sigma^2 + 1)
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_pred_original_sample[j] =
|
||||
(vec_x[j] / std::sqrt(sigma * sigma + 1) -
|
||||
std::sqrt(beta_prod_t) *
|
||||
vec_model_output[j]) *
|
||||
(1 / std::sqrt(alpha_prod_t));
|
||||
}
|
||||
}
|
||||
// Assuming the "epsilon" prediction type, where below
|
||||
// pred_epsilon = model_output is inserted, and is not
|
||||
// defined/copied explicitly.
|
||||
//
|
||||
// 5. compute variance: "sigma_t(eta)" -> see formula
|
||||
// (16)
|
||||
//
|
||||
// sigma_t = sqrt((1 - alpha_t-1)/(1 - alpha_t)) *
|
||||
// sqrt(1 - alpha_t/alpha_t-1)
|
||||
float beta_prod_t_prev = 1 - alpha_prod_t_prev;
|
||||
float variance = (beta_prod_t_prev / beta_prod_t) *
|
||||
(1 - alpha_prod_t / alpha_prod_t_prev);
|
||||
float std_dev_t = eta * std::sqrt(variance);
|
||||
// 6. compute "direction pointing to x_t" of formula
|
||||
// (12) from https://arxiv.org/pdf/2010.02502.pdf
|
||||
// 7. compute x_t without "random noise" of formula
|
||||
// (12) from https://arxiv.org/pdf/2010.02502.pdf
|
||||
{
|
||||
float* vec_model_output = (float*)model_output->data;
|
||||
float* vec_pred_original_sample =
|
||||
(float*)pred_original_sample->data;
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
// Two step inner loop without an explicit
|
||||
// tensor
|
||||
float pred_sample_direction =
|
||||
std::sqrt(1 - alpha_prod_t_prev -
|
||||
std::pow(std_dev_t, 2)) *
|
||||
vec_model_output[j];
|
||||
vec_x[j] = std::sqrt(alpha_prod_t_prev) *
|
||||
vec_pred_original_sample[j] +
|
||||
pred_sample_direction;
|
||||
}
|
||||
}
|
||||
if (eta > 0) {
|
||||
ggml_tensor_set_f32_randn(variance_noise, rng);
|
||||
float* vec_variance_noise =
|
||||
(float*)variance_noise->data;
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] += std_dev_t * vec_variance_noise[j];
|
||||
}
|
||||
}
|
||||
// See the note above: x = latents or sample here, and
|
||||
// is not scaled by the c_in. For the final output
|
||||
// this is correct, but for subsequent iterations, x
|
||||
// needs to be prescaled again, since k-diffusion's
|
||||
// model() differes from the bare U-net F_theta by the
|
||||
// factor c_in.
|
||||
}
|
||||
} break;
|
||||
case TCD: // Strategic Stochastic Sampling (Algorithm 4) in
|
||||
// Trajectory Consistency Distillation
|
||||
{
|
||||
// See J. Zheng et al., "Trajectory Consistency
|
||||
// Distillation: Improved Latent Consistency Distillation
|
||||
// by Semi-Linear Consistency Function with Trajectory
|
||||
// Mapping", arXiv:2402.19159 [cs.CV]
|
||||
float beta_start = 0.00085f;
|
||||
float beta_end = 0.0120f;
|
||||
std::vector<double> alphas_cumprod;
|
||||
std::vector<double> compvis_sigmas;
|
||||
|
||||
alphas_cumprod.reserve(TIMESTEPS);
|
||||
compvis_sigmas.reserve(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 original_steps = 50;
|
||||
|
||||
struct ggml_tensor* pred_original_sample =
|
||||
ggml_dup_tensor(work_ctx, x);
|
||||
struct ggml_tensor* noise =
|
||||
ggml_dup_tensor(work_ctx, x);
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
// Analytic form for TCD timesteps
|
||||
int timestep = TIMESTEPS - 1 -
|
||||
(TIMESTEPS / original_steps) *
|
||||
(int)floor(i * ((float)original_steps / steps));
|
||||
// 1. get previous step value
|
||||
int prev_timestep = i >= steps - 1 ? 0 : TIMESTEPS - 1 - (TIMESTEPS / original_steps) * (int)floor((i + 1) * ((float)original_steps / steps));
|
||||
// Here timestep_s is tau_n' in Algorithm 4. The _s
|
||||
// notation appears to be that from C. Lu,
|
||||
// "DPM-Solver: A Fast ODE Solver for Diffusion
|
||||
// Probabilistic Model Sampling in Around 10 Steps",
|
||||
// arXiv:2206.00927 [cs.LG], but this notation is not
|
||||
// continued in Algorithm 4, where _n' is used.
|
||||
int timestep_s =
|
||||
(int)floor((1 - eta) * prev_timestep);
|
||||
// Begin k-diffusion specific workaround for
|
||||
// evaluating F_theta(x; ...) from D(x, sigma), same
|
||||
// as in DDIM (and see there for detailed comments)
|
||||
float sigma = compvis_sigmas[timestep];
|
||||
if (i == 0) {
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] *= std::sqrt(sigma * sigma + 1) /
|
||||
sigma;
|
||||
}
|
||||
} else {
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_x[j] *= std::sqrt(sigma * sigma + 1);
|
||||
}
|
||||
}
|
||||
struct ggml_tensor* model_output =
|
||||
model(x, sigma, i + 1);
|
||||
{
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_model_output =
|
||||
(float*)model_output->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_model_output[j] =
|
||||
(vec_x[j] - vec_model_output[j]) *
|
||||
(1 / sigma);
|
||||
}
|
||||
}
|
||||
// 2. compute alphas, betas
|
||||
//
|
||||
// When comparing TCD with DDPM/DDIM note that Zheng
|
||||
// et al. (2024) follows the DPM-Solver notation for
|
||||
// alpha. One can find the following comment in the
|
||||
// original DPM-Solver code
|
||||
// (https://github.com/LuChengTHU/dpm-solver/):
|
||||
// "**Important**: Please pay special attention for
|
||||
// the args for `alphas_cumprod`: The `alphas_cumprod`
|
||||
// is the \hat{alpha_n} arrays in the notations of
|
||||
// DDPM. [...] Therefore, the notation \hat{alpha_n}
|
||||
// is different from the notation alpha_t in
|
||||
// DPM-Solver. In fact, we have alpha_{t_n} =
|
||||
// \sqrt{\hat{alpha_n}}, [...]"
|
||||
float alpha_prod_t = alphas_cumprod[timestep];
|
||||
float beta_prod_t = 1 - alpha_prod_t;
|
||||
// Note final_alpha_cumprod = alphas_cumprod[0] since
|
||||
// TCD is always "trailing"
|
||||
float alpha_prod_t_prev = prev_timestep >= 0 ? alphas_cumprod[prev_timestep] : alphas_cumprod[0];
|
||||
// The subscript _s are the only portion in this
|
||||
// section (2) unique to TCD
|
||||
float alpha_prod_s = alphas_cumprod[timestep_s];
|
||||
float beta_prod_s = 1 - alpha_prod_s;
|
||||
// 3. Compute the predicted noised sample x_s based on
|
||||
// the model parameterization
|
||||
//
|
||||
// This section is also exactly the same as DDIM
|
||||
{
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_model_output =
|
||||
(float*)model_output->data;
|
||||
float* vec_pred_original_sample =
|
||||
(float*)pred_original_sample->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
vec_pred_original_sample[j] =
|
||||
(vec_x[j] / std::sqrt(sigma * sigma + 1) -
|
||||
std::sqrt(beta_prod_t) *
|
||||
vec_model_output[j]) *
|
||||
(1 / std::sqrt(alpha_prod_t));
|
||||
}
|
||||
}
|
||||
// This consistency function step can be difficult to
|
||||
// decipher from Algorithm 4, as it is simply stated
|
||||
// using a consistency function. This step is the
|
||||
// modified DDIM, i.e. p. 8 (32) in Zheng et
|
||||
// al. (2024), with eta set to 0 (see the paragraph
|
||||
// immediately thereafter that states this somewhat
|
||||
// obliquely).
|
||||
{
|
||||
float* vec_pred_original_sample =
|
||||
(float*)pred_original_sample->data;
|
||||
float* vec_model_output =
|
||||
(float*)model_output->data;
|
||||
float* vec_x = (float*)x->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
// Substituting x = pred_noised_sample and
|
||||
// pred_epsilon = model_output
|
||||
vec_x[j] =
|
||||
std::sqrt(alpha_prod_s) *
|
||||
vec_pred_original_sample[j] +
|
||||
std::sqrt(beta_prod_s) *
|
||||
vec_model_output[j];
|
||||
}
|
||||
}
|
||||
// 4. Sample and inject noise z ~ N(0, I) for
|
||||
// MultiStep Inference Noise is not used on the final
|
||||
// timestep of the timestep schedule. This also means
|
||||
// that noise is not used for one-step sampling. Eta
|
||||
// (referred to as "gamma" in the paper) was
|
||||
// introduced to control the stochasticity in every
|
||||
// step. When eta = 0, it represents deterministic
|
||||
// sampling, whereas eta = 1 indicates full stochastic
|
||||
// sampling.
|
||||
if (eta > 0 && i != steps - 1) {
|
||||
// In this case, x is still pred_noised_sample,
|
||||
// continue in-place
|
||||
ggml_tensor_set_f32_randn(noise, rng);
|
||||
float* vec_x = (float*)x->data;
|
||||
float* vec_noise = (float*)noise->data;
|
||||
for (int j = 0; j < ggml_nelements(x); j++) {
|
||||
// Corresponding to (35) in Zheng et
|
||||
// al. (2024), substituting x =
|
||||
// pred_noised_sample
|
||||
vec_x[j] =
|
||||
std::sqrt(alpha_prod_t_prev /
|
||||
alpha_prod_s) *
|
||||
vec_x[j] +
|
||||
std::sqrt(1 - alpha_prod_t_prev /
|
||||
alpha_prod_s) *
|
||||
vec_noise[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_ERROR("Attempting to sample with nonexisting sample method %i", method);
|
||||
|
||||
+203
-62
@@ -3,22 +3,33 @@
|
||||
|
||||
#include "flux.hpp"
|
||||
#include "mmdit.hpp"
|
||||
#include "qwen_image.hpp"
|
||||
#include "unet.hpp"
|
||||
#include "wan.hpp"
|
||||
|
||||
struct DiffusionParams {
|
||||
struct ggml_tensor* x = NULL;
|
||||
struct ggml_tensor* timesteps = NULL;
|
||||
struct ggml_tensor* context = NULL;
|
||||
struct ggml_tensor* c_concat = NULL;
|
||||
struct ggml_tensor* y = NULL;
|
||||
struct ggml_tensor* guidance = NULL;
|
||||
std::vector<ggml_tensor*> ref_latents = {};
|
||||
bool increase_ref_index = false;
|
||||
int num_video_frames = -1;
|
||||
std::vector<struct ggml_tensor*> controls = {};
|
||||
float control_strength = 0.f;
|
||||
struct ggml_tensor* vace_context = NULL;
|
||||
float vace_strength = 1.f;
|
||||
std::vector<int> skip_layers = {};
|
||||
};
|
||||
|
||||
struct DiffusionModel {
|
||||
virtual std::string get_desc() = 0;
|
||||
virtual void compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
int num_video_frames = -1,
|
||||
std::vector<struct ggml_tensor*> controls = {},
|
||||
float control_strength = 0.f,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) = 0;
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) = 0;
|
||||
virtual void alloc_params_buffer() = 0;
|
||||
virtual void free_params_buffer() = 0;
|
||||
virtual void free_compute_buffer() = 0;
|
||||
@@ -31,10 +42,15 @@ struct UNetModel : public DiffusionModel {
|
||||
UNetModelRunner unet;
|
||||
|
||||
UNetModel(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
SDVersion version = VERSION_SD1,
|
||||
bool flash_attn = false)
|
||||
: unet(backend, tensor_types, "model.diffusion_model", version, flash_attn) {
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
SDVersion version = VERSION_SD1,
|
||||
bool flash_attn = false)
|
||||
: unet(backend, offload_params_to_cpu, tensor_types, "model.diffusion_model", version, flash_attn) {
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return unet.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
@@ -62,20 +78,18 @@ struct UNetModel : public DiffusionModel {
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
int num_video_frames = -1,
|
||||
std::vector<struct ggml_tensor*> controls = {},
|
||||
float control_strength = 0.f,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
(void)skip_layers; // SLG doesn't work with UNet models
|
||||
return unet.compute(n_threads, x, timesteps, context, c_concat, y, num_video_frames, controls, control_strength, output, output_ctx);
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
return unet.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.c_concat,
|
||||
diffusion_params.y,
|
||||
diffusion_params.num_video_frames,
|
||||
diffusion_params.controls,
|
||||
diffusion_params.control_strength, output, output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,8 +97,14 @@ struct MMDiTModel : public DiffusionModel {
|
||||
MMDiTRunner mmdit;
|
||||
|
||||
MMDiTModel(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types)
|
||||
: mmdit(backend, tensor_types, "model.diffusion_model") {
|
||||
bool offload_params_to_cpu,
|
||||
bool flash_attn = false,
|
||||
const String2GGMLType& tensor_types = {})
|
||||
: mmdit(backend, offload_params_to_cpu, flash_attn, tensor_types, "model.diffusion_model") {
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return mmdit.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
@@ -112,19 +132,17 @@ struct MMDiTModel : public DiffusionModel {
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
int num_video_frames = -1,
|
||||
std::vector<struct ggml_tensor*> controls = {},
|
||||
float control_strength = 0.f,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
return mmdit.compute(n_threads, x, timesteps, context, y, output, output_ctx, skip_layers);
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
return mmdit.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.y,
|
||||
output,
|
||||
output_ctx,
|
||||
diffusion_params.skip_layers);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,10 +150,16 @@ struct FluxModel : public DiffusionModel {
|
||||
Flux::FluxRunner flux;
|
||||
|
||||
FluxModel(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool flash_attn = false)
|
||||
: flux(backend, tensor_types, "model.diffusion_model", version, flash_attn) {
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool flash_attn = false,
|
||||
bool use_mask = false)
|
||||
: flux(backend, offload_params_to_cpu, tensor_types, "model.diffusion_model", version, flash_attn, use_mask) {
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return flux.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
@@ -163,19 +187,136 @@ struct FluxModel : public DiffusionModel {
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
int num_video_frames = -1,
|
||||
std::vector<struct ggml_tensor*> controls = {},
|
||||
float control_strength = 0.f,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
return flux.compute(n_threads, x, timesteps, context, c_concat, y, guidance, output, output_ctx, skip_layers);
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
return flux.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.c_concat,
|
||||
diffusion_params.y,
|
||||
diffusion_params.guidance,
|
||||
diffusion_params.ref_latents,
|
||||
diffusion_params.increase_ref_index,
|
||||
output,
|
||||
output_ctx,
|
||||
diffusion_params.skip_layers);
|
||||
}
|
||||
};
|
||||
|
||||
struct WanModel : public DiffusionModel {
|
||||
std::string prefix;
|
||||
WAN::WanRunner wan;
|
||||
|
||||
WanModel(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
SDVersion version = VERSION_WAN2,
|
||||
bool flash_attn = false)
|
||||
: prefix(prefix), wan(backend, offload_params_to_cpu, tensor_types, prefix, version, flash_attn) {
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return wan.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
wan.alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() {
|
||||
wan.free_params_buffer();
|
||||
}
|
||||
|
||||
void free_compute_buffer() {
|
||||
wan.free_compute_buffer();
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
wan.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() {
|
||||
return wan.get_params_buffer_size();
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
return wan.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.y,
|
||||
diffusion_params.c_concat,
|
||||
NULL,
|
||||
diffusion_params.vace_context,
|
||||
diffusion_params.vace_strength,
|
||||
output,
|
||||
output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct QwenImageModel : public DiffusionModel {
|
||||
std::string prefix;
|
||||
Qwen::QwenImageRunner qwen_image;
|
||||
|
||||
QwenImageModel(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "model.diffusion_model",
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool flash_attn = false)
|
||||
: prefix(prefix), qwen_image(backend, offload_params_to_cpu, tensor_types, prefix, version, flash_attn) {
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return qwen_image.get_desc();
|
||||
}
|
||||
|
||||
void alloc_params_buffer() {
|
||||
qwen_image.alloc_params_buffer();
|
||||
}
|
||||
|
||||
void free_params_buffer() {
|
||||
qwen_image.free_params_buffer();
|
||||
}
|
||||
|
||||
void free_compute_buffer() {
|
||||
qwen_image.free_compute_buffer();
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors) {
|
||||
qwen_image.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
size_t get_params_buffer_size() {
|
||||
return qwen_image.get_params_buffer_size();
|
||||
}
|
||||
|
||||
int64_t get_adm_in_channels() {
|
||||
return 768;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
DiffusionParams diffusion_params,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
return qwen_image.compute(n_threads,
|
||||
diffusion_params.x,
|
||||
diffusion_params.timesteps,
|
||||
diffusion_params.context,
|
||||
diffusion_params.ref_latents,
|
||||
true, // increase_ref_index
|
||||
output,
|
||||
output_ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
# Build from scratch
|
||||
|
||||
## Get the Code
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/leejet/stable-diffusion.cpp
|
||||
cd stable-diffusion.cpp
|
||||
```
|
||||
|
||||
- If you have already cloned the repository, you can use the following command to update the repository to the latest code.
|
||||
|
||||
```
|
||||
cd stable-diffusion.cpp
|
||||
git pull origin master
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
## Build (CPU only)
|
||||
|
||||
If you don't have a GPU or CUDA installed, you can build a CPU-only version.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with OpenBLAS
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake .. -DGGML_OPENBLAS=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with CUDA
|
||||
|
||||
This provides GPU acceleration using NVIDIA GPU. Make sure to have the CUDA toolkit installed. You can download it from your Linux distro's package manager (e.g. `apt install nvidia-cuda-toolkit`) or from here: [CUDA Toolkit](https://developer.nvidia.com/cuda-downloads). Recommended to have at least 4 GB of VRAM.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake .. -DSD_CUDA=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with HipBLAS
|
||||
|
||||
This provides GPU acceleration using AMD GPU. Make sure to have the ROCm toolkit installed.
|
||||
To build for another GPU architecture than installed in your system, set `$GFX_NAME` manually to the desired architecture (replace first command). This is also necessary if your GPU is not officially supported by ROCm, for example you have to set `$GFX_NAME` manually to `gfx1030` for consumer RDNA2 cards.
|
||||
|
||||
Windows User Refer to [docs/hipBLAS_on_Windows.md](docs%2FhipBLAS_on_Windows.md) for a comprehensive guide.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
if command -v rocminfo; then export GFX_NAME=$(rocminfo | awk '/ *Name: +gfx[1-9]/ {print $2; exit}'); else echo "rocminfo missing!"; fi
|
||||
if [ -z "${GFX_NAME}" ]; then echo "Error: Couldn't detect GPU!"; else echo "Building for GPU: ${GFX_NAME}"; fi
|
||||
cmake .. -G "Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DGPU_TARGETS=$GFX_NAME -DAMDGPU_TARGETS=$GFX_NAME -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with MUSA
|
||||
|
||||
This provides GPU acceleration using Moore Threads GPU. Make sure to have the MUSA toolkit installed.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_C_COMPILER=/usr/local/musa/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/musa/bin/clang++ -DSD_MUSA=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with Metal
|
||||
|
||||
Using Metal makes the computation run on the GPU. Currently, there are some issues with Metal when performing operations on very large matrices, making it highly inefficient at the moment. Performance improvements are expected in the near future.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake .. -DSD_METAL=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with Vulkan
|
||||
|
||||
Install Vulkan SDK from https://www.lunarg.com/vulkan-sdk/.
|
||||
|
||||
```shell
|
||||
mkdir build && cd build
|
||||
cmake .. -DSD_VULKAN=ON
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
## Build with OpenCL (for Adreno GPU)
|
||||
|
||||
Currently, it supports only Adreno GPUs and is primarily optimized for Q4_0 type
|
||||
|
||||
To build for Windows ARM please refers to [Windows 11 Arm64](https://github.com/ggml-org/llama.cpp/blob/master/docs/backend/OPENCL.md#windows-11-arm64)
|
||||
|
||||
Building for Android:
|
||||
|
||||
Android NDK:
|
||||
Download and install the Android NDK from the [official Android developer site](https://developer.android.com/ndk/downloads).
|
||||
|
||||
Setup OpenCL Dependencies for NDK:
|
||||
|
||||
You need to provide OpenCL headers and the ICD loader library to your NDK sysroot.
|
||||
|
||||
* OpenCL Headers:
|
||||
```bash
|
||||
# In a temporary working directory
|
||||
git clone https://github.com/KhronosGroup/OpenCL-Headers
|
||||
cd OpenCL-Headers
|
||||
# Replace <YOUR_NDK_PATH> with your actual NDK installation path
|
||||
# e.g., cp -r CL /path/to/android-ndk-r26c/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include
|
||||
sudo cp -r CL <YOUR_NDK_PATH>/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include
|
||||
cd ..
|
||||
```
|
||||
|
||||
* OpenCL ICD Loader:
|
||||
```shell
|
||||
# In the same temporary working directory
|
||||
git clone https://github.com/KhronosGroup/OpenCL-ICD-Loader
|
||||
cd OpenCL-ICD-Loader
|
||||
mkdir build_ndk && cd build_ndk
|
||||
|
||||
# Replace <YOUR_NDK_PATH> in the CMAKE_TOOLCHAIN_FILE and OPENCL_ICD_LOADER_HEADERS_DIR
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE=<YOUR_NDK_PATH>/build/cmake/android.toolchain.cmake \
|
||||
-DOPENCL_ICD_LOADER_HEADERS_DIR=<YOUR_NDK_PATH>/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include \
|
||||
-DANDROID_ABI=arm64-v8a \
|
||||
-DANDROID_PLATFORM=24 \
|
||||
-DANDROID_STL=c++_shared
|
||||
|
||||
ninja
|
||||
# Replace <YOUR_NDK_PATH>
|
||||
# e.g., cp libOpenCL.so /path/to/android-ndk-r26c/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android
|
||||
sudo cp libOpenCL.so <YOUR_NDK_PATH>/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android
|
||||
cd ../..
|
||||
```
|
||||
|
||||
Build `stable-diffusion.cpp` for Android with OpenCL:
|
||||
|
||||
```shell
|
||||
mkdir build-android && cd build-android
|
||||
|
||||
# Replace <YOUR_NDK_PATH> with your actual NDK installation path
|
||||
# e.g., -DCMAKE_TOOLCHAIN_FILE=/path/to/android-ndk-r26c/build/cmake/android.toolchain.cmake
|
||||
cmake .. -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=<YOUR_NDK_PATH>/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_ABI=arm64-v8a \
|
||||
-DANDROID_PLATFORM=android-28 \
|
||||
-DGGML_OPENMP=OFF \
|
||||
-DSD_OPENCL=ON
|
||||
|
||||
ninja
|
||||
```
|
||||
*(Note: Don't forget to include `LD_LIBRARY_PATH=/vendor/lib64` in your command line before running the binary)*
|
||||
|
||||
## Build with SYCL
|
||||
|
||||
Using SYCL makes the computation run on the Intel GPU. Please make sure you have installed the related driver and [Intel® oneAPI Base toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) before start. More details and steps can refer to [llama.cpp SYCL backend](https://github.com/ggerganov/llama.cpp/blob/master/docs/backend/SYCL.md#linux).
|
||||
|
||||
```shell
|
||||
# Export relevant ENV variables
|
||||
source /opt/intel/oneapi/setvars.sh
|
||||
|
||||
# Option 1: Use FP32 (recommended for better performance in most cases)
|
||||
cmake .. -DSD_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx
|
||||
|
||||
# Option 2: Use FP16
|
||||
cmake .. -DSD_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_SYCL_F16=ON
|
||||
|
||||
cmake --build . --config Release
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
# How to Use
|
||||
|
||||
You can run Chroma using stable-diffusion.cpp with a GPU that has 6GB or even 4GB of VRAM, without needing to offload to RAM.
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Chroma
|
||||
- If you don't want to do the conversion yourself, download the preconverted gguf model from [silveroxides/Chroma-GGUF](https://huggingface.co/silveroxides/Chroma-GGUF)
|
||||
- Otherwise, download chroma's safetensors from [lodestones/Chroma](https://huggingface.co/lodestones/Chroma)
|
||||
- Download vae from https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/ae.safetensors
|
||||
- Download t5xxl from https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp16.safetensors
|
||||
|
||||
## Convert Chroma weights
|
||||
|
||||
You can download the preconverted gguf weights from [silveroxides/Chroma-GGUF](https://huggingface.co/silveroxides/Chroma-GGUF), this way you don't have to do the conversion yourself.
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M convert -m ..\..\ComfyUI\models\unet\chroma-unlocked-v40.safetensors -o ..\models\chroma-unlocked-v40-q8_0.gguf -v --type q8_0
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
### Example
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\chroma-unlocked-v40-q8_0.gguf --vae ..\models\ae.sft --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'chroma.cpp'" --cfg-scale 4.0 --sampling-method euler -v --chroma-disable-dit-mask --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ Using fp16 will lead to overflow, but ggml's support for bf16 is not yet fully d
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
Using formats of different precisions will yield results of varying quality.
|
||||
@@ -44,7 +44,7 @@ Using formats of different precisions will yield results of varying quality.
|
||||
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-schnell-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --steps 4
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-schnell-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --steps 4 --clip-on-cpu
|
||||
```
|
||||
|
||||
| q8_0 |
|
||||
@@ -60,7 +60,7 @@ Since many flux LoRA training libraries have used various LoRA naming formats, i
|
||||
- LoRA model from https://huggingface.co/XLabs-AI/flux-lora-collection/tree/main (using comfy converted version!!!)
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ...\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'<lora:realism_lora_comfy_converted:1>" --cfg-scale 1.0 --sampling-method euler -v --lora-model-dir ../models
|
||||
.\bin\Release\sd.exe --diffusion-model ..\models\flux1-dev-q8_0.gguf --vae ...\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'<lora:realism_lora_comfy_converted:1>" --cfg-scale 1.0 --sampling-method euler -v --lora-model-dir ../models --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# How to Use
|
||||
|
||||
You can run Kontext using stable-diffusion.cpp with a GPU that has 6GB or even 4GB of VRAM, without needing to offload to RAM.
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Kontext
|
||||
- If you don't want to do the conversion yourself, download the preconverted gguf model from [FLUX.1-Kontext-dev-GGUF](https://huggingface.co/QuantStack/FLUX.1-Kontext-dev-GGUF)
|
||||
- Otherwise, download FLUX.1-Kontext-dev from https://huggingface.co/black-forest-labs/FLUX.1-Kontext-dev/blob/main/flux1-kontext-dev.safetensors
|
||||
- Download vae from https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/ae.safetensors
|
||||
- Download clip_l from https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/clip_l.safetensors
|
||||
- Download t5xxl from https://huggingface.co/comfyanonymous/flux_text_encoders/blob/main/t5xxl_fp16.safetensors
|
||||
|
||||
## Convert Kontext weights
|
||||
|
||||
You can download the preconverted gguf weights from [FLUX.1-Kontext-dev-GGUF](https://huggingface.co/QuantStack/FLUX.1-Kontext-dev-GGUF), this way you don't have to do the conversion yourself.
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M convert -m ..\..\ComfyUI\models\unet\flux1-kontext-dev.safetensors -o ..\models\flux1-kontext-dev-q8_0.gguf -v --type q8_0
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
- `--cfg-scale` is recommended to be set to 1.
|
||||
|
||||
### Example
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -r .\flux1-dev-q8_0.png --diffusion-model ..\models\flux1-kontext-dev-q8_0.gguf --vae ..\models\ae.sft --clip_l ..\models\clip_l.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -p "change 'flux.cpp' to 'kontext.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
|
||||
| ref_image | prompt | output |
|
||||
| ---- | ---- |---- |
|
||||
|  | change 'flux.cpp' to 'kontext.cpp' | |
|
||||
|
||||
|
||||
|
||||
+37
-1
@@ -10,4 +10,40 @@ Here's a simple example:
|
||||
./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat<lora:marblesh:1>" --lora-model-dir ../models
|
||||
```
|
||||
|
||||
`../models/marblesh.safetensors` or `../models/marblesh.ckpt` will be applied to the model
|
||||
`../models/marblesh.safetensors` or `../models/marblesh.ckpt` will be applied to the model
|
||||
|
||||
# Support matrix
|
||||
|
||||
> ℹ️ CUDA `get_rows` support is defined here:
|
||||
> [ggml-org/ggml/src/ggml-cuda/getrows.cu#L156](https://github.com/ggml-org/ggml/blob/7dee1d6a1e7611f238d09be96738388da97c88ed/src/ggml-cuda/getrows.cu#L156)
|
||||
> Currently only the basic types + Q4/Q5/Q8 are implemented. K-quants are **not** supported.
|
||||
|
||||
NOTE: The other backends may have different support.
|
||||
|
||||
| Quant / Type | CUDA | Vulkan |
|
||||
|--------------|------|--------|
|
||||
| F32 | ✔️ | ✔️ |
|
||||
| F16 | ✔️ | ✔️ |
|
||||
| BF16 | ✔️ | ✔️ |
|
||||
| I32 | ✔️ | ❌ |
|
||||
| Q4_0 | ✔️ | ✔️ |
|
||||
| Q4_1 | ✔️ | ✔️ |
|
||||
| Q5_0 | ✔️ | ✔️ |
|
||||
| Q5_1 | ✔️ | ✔️ |
|
||||
| Q8_0 | ✔️ | ✔️ |
|
||||
| Q2_K | ❌ | ❌ |
|
||||
| Q3_K | ❌ | ❌ |
|
||||
| Q4_K | ❌ | ❌ |
|
||||
| Q5_K | ❌ | ❌ |
|
||||
| Q6_K | ❌ | ❌ |
|
||||
| Q8_K | ❌ | ❌ |
|
||||
| IQ1_S | ❌ | ✔️ |
|
||||
| IQ1_M | ❌ | ✔️ |
|
||||
| IQ2_XXS | ❌ | ✔️ |
|
||||
| IQ2_XS | ❌ | ✔️ |
|
||||
| IQ2_S | ❌ | ✔️ |
|
||||
| IQ3_XXS | ❌ | ✔️ |
|
||||
| IQ3_S | ❌ | ✔️ |
|
||||
| IQ4_XS | ❌ | ✔️ |
|
||||
| IQ4_NL | ❌ | ✔️ |
|
||||
| MXFP4 | ❌ | ✔️ |
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
## Use Flash Attention to save memory and improve speed.
|
||||
|
||||
Enabling flash attention for the diffusion model reduces memory usage by varying amounts of MB.
|
||||
eg.:
|
||||
- flux 768x768 ~600mb
|
||||
- SD2 768x768 ~1400mb
|
||||
|
||||
For most backends, it slows things down, but for cuda it generally speeds it up too.
|
||||
At the moment, it is only supported for some models and some backends (like cpu, cuda/rocm, metal).
|
||||
|
||||
Run by adding `--diffusion-fa` to the arguments and watch for:
|
||||
```
|
||||
[INFO ] stable-diffusion.cpp:312 - Using flash attention in the diffusion model
|
||||
```
|
||||
and the compute buffer shrink in the debug log:
|
||||
```
|
||||
[DEBUG] ggml_extend.hpp:1004 - flux compute buffer size: 650.00 MB(VRAM)
|
||||
```
|
||||
|
||||
## Offload weights to the CPU to save VRAM without reducing generation speed.
|
||||
|
||||
Using `--offload-to-cpu` allows you to offload weights to the CPU, saving VRAM without reducing generation speed.
|
||||
|
||||
## Use quantization to reduce memory usage.
|
||||
|
||||
[quantization](./quantization_and_gguf.md)
|
||||
+4
-5
@@ -6,16 +6,15 @@ You can use [PhotoMaker](https://github.com/TencentARC/PhotoMaker) to personaliz
|
||||
|
||||
Download PhotoMaker model file (in safetensor format) [here](https://huggingface.co/bssrdf/PhotoMaker). The official release of the model file (in .bin format) does not work with ```stablediffusion.cpp```.
|
||||
|
||||
- Specify the PhotoMaker model path using the `--stacked-id-embd-dir PATH` parameter.
|
||||
- Specify the input images path using the `--input-id-images-dir PATH` parameter.
|
||||
- input images **must** have the same width and height for preprocessing (to be improved)
|
||||
- Specify the PhotoMaker model path using the `--photo-maker PATH` parameter.
|
||||
- Specify the input images path using the `--pm-id-images-dir PATH` parameter.
|
||||
|
||||
In prompt, make sure you have a class word followed by the trigger word ```"img"``` (hard-coded for now). The class word could be one of ```"man, woman, girl, boy"```. If input ID images contain asian faces, add ```Asian``` before the class
|
||||
word.
|
||||
|
||||
Another PhotoMaker specific parameter:
|
||||
|
||||
- ```--style-ratio (0-100)%```: default is 20 and 10-20 typically gets good results. Lower ratio means more faithfully following input ID (not necessarily better quality).
|
||||
- ```--pm-style-strength (0-100)%```: default is 20 and 10-20 typically gets good results. Lower ratio means more faithfully following input ID (not necessarily better quality).
|
||||
|
||||
Other parameters recommended for running Photomaker:
|
||||
|
||||
@@ -28,7 +27,7 @@ If on low memory GPUs (<= 8GB), recommend running with ```--vae-on-cpu``` option
|
||||
Example:
|
||||
|
||||
```bash
|
||||
bin/sd -m ../models/sdxlUnstableDiffusers_v11.safetensors --vae ../models/sdxl_vae.safetensors --stacked-id-embd-dir ../models/photomaker-v1.safetensors --input-id-images-dir ../assets/photomaker_examples/scarletthead_woman -p "a girl img, retro futurism, retro game art style but extremely beautiful, intricate details, masterpiece, best quality, space-themed, cosmic, celestial, stars, galaxies, nebulas, planets, science fiction, highly detailed" -n "realistic, photo-realistic, worst quality, greyscale, bad anatomy, bad hands, error, text" --cfg-scale 5.0 --sampling-method euler -H 1024 -W 1024 --style-ratio 10 --vae-on-cpu -o output.png
|
||||
bin/sd -m ../models/sdxlUnstableDiffusers_v11.safetensors --vae ../models/sdxl_vae.safetensors --photo-maker ../models/photomaker-v1.safetensors --pm-id-images-dir ../assets/photomaker_examples/scarletthead_woman -p "a girl img, retro futurism, retro game art style but extremely beautiful, intricate details, masterpiece, best quality, space-themed, cosmic, celestial, stars, galaxies, nebulas, planets, science fiction, highly detailed" -n "realistic, photo-realistic, worst quality, greyscale, bad anatomy, bad hands, error, text" --cfg-scale 5.0 --sampling-method euler -H 1024 -W 1024 --pm-style-strength 10 --vae-on-cpu --steps 50
|
||||
```
|
||||
|
||||
## PhotoMaker Version 2
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# How to Use
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Qwen Image
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Qwen-Image-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main/split_files/vae
|
||||
- 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
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\qwen-image-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf -p '一个穿着"QWEN"标志的T恤的中国美女正拿着黑色的马克笔面相镜头微笑。她身后的玻璃板上手写体写着 “一、Qwen-Image的技术路线: 探索视觉生成基础模型的极限,开创理解与生成一体化的未来。二、Qwen-Image的模型特色:1、复杂文字渲染。支持中英渲染、自动布局; 2、精准图像编辑。支持文字编辑、物体增减、风格变换。三、Qwen-Image的未来愿景:赋能专业内容创作、助力生成式AI发展。”' --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu -H 1024 -W 1024 --diffusion-fa --flow-shift 3
|
||||
```
|
||||
|
||||
<img alt="qwen example" src="../assets/qwen/example.png" />
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# How to Use
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Qwen Image
|
||||
- Qwen Image Edit
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Qwen-Image-Edit-GGUF/tree/main
|
||||
- Qwen Image Edit 2509
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image-Edit_ComfyUI/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF/tree/main
|
||||
- Download vae
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/tree/main/split_files/vae
|
||||
- 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
|
||||
|
||||
## Examples
|
||||
|
||||
### Qwen Image Edit
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen_Image_Edit-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\qwen_2.5_vl_7b.safetensors --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'edit.cpp'" --seed 1118877715456453
|
||||
```
|
||||
|
||||
<img alt="qwen_image_edit" src="../assets/qwen/qwen_image_edit.png" />
|
||||
|
||||
|
||||
### Qwen Image Edit 2509
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe --diffusion-model ..\..\ComfyUI\models\diffusion_models\Qwen-Image-Edit-2509-Q4_K_S.gguf --vae ..\..\ComfyUI\models\vae\qwen_image_vae.safetensors --qwen2vl ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct-Q8_0.gguf --qwen2vl_vision ..\..\ComfyUI\models\text_encoders\Qwen2.5-VL-7B-Instruct.mmproj-Q8_0.gguf --cfg-scale 2.5 --sampling-method euler -v --offload-to-cpu --diffusion-fa --flow-shift 3 -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'Qwen Image Edit 2509'"
|
||||
```
|
||||
|
||||
<img alt="qwen_image_edit_2509" src="../assets/qwen/qwen_image_edit_2509.png" />
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
## Download weights
|
||||
|
||||
- download original weights(.ckpt or .safetensors). For example
|
||||
- Stable Diffusion v1.4 from https://huggingface.co/CompVis/stable-diffusion-v-1-4-original
|
||||
- Stable Diffusion v1.5 from https://huggingface.co/runwayml/stable-diffusion-v1-5
|
||||
- Stable Diffuison v2.1 from https://huggingface.co/stabilityai/stable-diffusion-2-1
|
||||
- Stable Diffusion 3 2B from https://huggingface.co/stabilityai/stable-diffusion-3-medium
|
||||
|
||||
### txt2img example
|
||||
|
||||
```sh
|
||||
./bin/sd -m ../models/sd-v1-4.ckpt -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/v1-5-pruned-emaonly.safetensors -p "a lovely cat"
|
||||
# ./bin/sd -m ../models/sd_xl_base_1.0.safetensors --vae ../models/sdxl_vae-fp16-fix.safetensors -H 1024 -W 1024 -p "a lovely cat" -v
|
||||
# ./bin/sd -m ../models/sd3_medium_incl_clips_t5xxlfp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable Diffusion CPP\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd --diffusion-model ../models/flux1-dev-q3_k.gguf --vae ../models/ae.sft --clip_l ../models/clip_l.safetensors --t5xxl ../models/t5xxl_fp16.safetensors -p "a lovely cat holding a sign says 'flux.cpp'" --cfg-scale 1.0 --sampling-method euler -v --clip-on-cpu
|
||||
# ./bin/sd -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||
Using formats of different precisions will yield results of varying quality.
|
||||
|
||||
| f32 | f16 |q8_0 |q5_0 |q5_1 |q4_0 |q4_1 |
|
||||
| ---- |---- |---- |---- |---- |---- |---- |
|
||||
|  | | | | | | |
|
||||
|
||||
### img2img example
|
||||
|
||||
- `./output.png` is the image generated from the above txt2img pipeline
|
||||
|
||||
|
||||
```
|
||||
./bin/sd -m ../models/sd-v1-4.ckpt -p "cat with blue eyes" -i ./output.png -o ./img2img_output.png --strength 0.4
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="../assets/img2img_output.png" width="256x">
|
||||
</p>
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
For example:
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v
|
||||
.\bin\Release\sd.exe -m ..\models\sd3.5_large.safetensors --clip_l ..\models\clip_l.safetensors --clip_g ..\models\clip_g.safetensors --t5xxl ..\models\t5xxl_fp16.safetensors -H 1024 -W 1024 -p 'a lovely cat holding a sign says \"Stable diffusion 3.5 Large\"' --cfg-scale 4.5 --sampling-method euler -v --clip-on-cpu
|
||||
```
|
||||
|
||||

|
||||
+204
@@ -0,0 +1,204 @@
|
||||
# How to Use
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download Wan
|
||||
- Wan2.1
|
||||
- Wan2.1 T2V 1.3B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- Wan2.1 T2V 14B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/city96/Wan2.1-T2V-14B-gguf/tree/main
|
||||
- Wan2.1 I2V 14B 480P
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/city96/Wan2.1-I2V-14B-480P-gguf/tree/main
|
||||
- Wan2.1 I2V 14B 720P
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/city96/Wan2.1-I2V-14B-720P-gguf/tree/main
|
||||
- Wan2.1 FLF2V 14B 720P
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/city96/Wan2.1-FLF2V-14B-720P-gguf/tree/main
|
||||
- Wan2.1 VACE 1.3B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/calcuis/wan-1.3b-gguf/tree/main
|
||||
- Wan2.1 VACE 14B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.1_14B_VACE-GGUF/tree/main
|
||||
- Wan2.2
|
||||
- Wan2.2 TI2V 5B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.2-TI2V-5B-GGUF/tree/main
|
||||
- Wan2.2 T2V A14B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/tree/main
|
||||
- Wan2.2 I2V A14B
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.2-I2V-A14B-GGUF/tree/main
|
||||
- Download vae
|
||||
- wan_2.1_vae (for all the wan model except Wan2.2 TI2V 5B)
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/vae/wan_2.1_vae.safetensors
|
||||
- wan_2.2_vae (for Wan2.2 TI2V 5B only)
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/blob/main/split_files/vae/wan2.2_vae.safetensors
|
||||
- Download umt5_xxl
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/text_encoders/umt5_xxl_fp16.safetensors
|
||||
- gguf: https://huggingface.co/city96/umt5-xxl-encoder-gguf/tree/main
|
||||
|
||||
- Download clip_vison_h (for Wan2.1 I2V/FLF2V only)
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/clip_vision/clip_vision_h.safetensors
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Wan2.1 T2V 1.3B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1_t2v_1.3B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.1 T2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-t2v-14b-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
|
||||
### Wan2.1 I2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-i2v-14b-480p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.2 T2V A14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.2 I2V A14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --video-frames 33 --offload-to-cpu -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.2 T2V A14B T2I
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --flow-shift 3.0
|
||||
```
|
||||
|
||||
<img width="832" height="480" alt="Wan2 2_14B_t2i" src="../assets/wan/Wan2.2_14B_t2i.png" />
|
||||
|
||||
### Wan2.2 T2V 14B with Lora
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-T2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat<lora:wan2.2_t2v_lightx2v_4steps_lora_v1.1_low_noise:1><lora:|high_noise|wan2.2_t2v_lightx2v_4steps_lora_v1.1_high_noise:1>" --cfg-scale 3.5 --sampling-method euler --steps 4 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 4 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --offload-to-cpu --lora-model-dir ..\..\ComfyUI\models\loras --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_t2v_lora.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
|
||||
### Wan2.2 TI2V 5B
|
||||
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_5B_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
#### I2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.2_ti2v_5B_fp16.safetensors --vae ..\..\ComfyUI\models\vae\wan2.2_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --offload-to-cpu --video-frames 33 -i ..\assets\cat_with_sd_cpp_42.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_5B_i2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.1 FLF2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-flf2v-14b-720p-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --clip_vision ..\..\ComfyUI\models\clip_vision\clip_vision_h.safetensors -p "glass flower blossom" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_flf2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.2 FLF2V 14B
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-LowNoise-Q8_0.gguf --high-noise-diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.2-I2V-A14B-HighNoise-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -p "glass flower blossom" -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa --video-frames 33 --offload-to-cpu --init-img ..\..\ComfyUI\input\start_image.png --end-img ..\..\ComfyUI\input\end_image.png --flow-shift 3.0
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_14B_flf2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.1 VACE 1.3B
|
||||
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 1 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
#### R2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
#### V2V
|
||||
|
||||
```
|
||||
mkdir post+depth
|
||||
ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\frame_%04d.jpg
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\wan2.1-vace-1.3b-q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_1.3B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.1 VACE 14B
|
||||
|
||||
#### T2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_t2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
#### R2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
|
||||
|
||||
#### V2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd.exe -M vid_gen --diffusion-model ..\..\ComfyUI\models\diffusion_models\Wan2.1_14B_VACE-Q8_0.gguf --vae ..\..\ComfyUI\models\vae\wan_2.1_vae.safetensors --t5xxl ..\..\ComfyUI\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "The girl is dancing in a sea of flowers, slowly moving her hands. There is a close - up shot of her upper body. The character is surrounded by other transparent glass flowers in the style of Nicoletta Ceccoli, creating a beautiful, surreal, and emotionally expressive movie scene with a white. transparent feel and a dreamyl atmosphere." --cfg-scale 6.0 --sampling-method euler -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 480 -H 832 --diffusion-fa -i ..\..\ComfyUI\input\dance_girl.jpg --control-video ./post+depth --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
+208
-27
@@ -83,39 +83,44 @@ public:
|
||||
|
||||
class RRDBNet : public GGMLBlock {
|
||||
protected:
|
||||
int scale = 4; // default RealESRGAN_x4plus_anime_6B
|
||||
int num_block = 6; // default RealESRGAN_x4plus_anime_6B
|
||||
int scale = 4;
|
||||
int num_block = 23;
|
||||
int num_in_ch = 3;
|
||||
int num_out_ch = 3;
|
||||
int num_feat = 64; // default RealESRGAN_x4plus_anime_6B
|
||||
int num_grow_ch = 32; // default RealESRGAN_x4plus_anime_6B
|
||||
int num_feat = 64;
|
||||
int num_grow_ch = 32;
|
||||
|
||||
public:
|
||||
RRDBNet() {
|
||||
RRDBNet(int scale, int num_block, int num_in_ch, int num_out_ch, int num_feat, int num_grow_ch)
|
||||
: scale(scale), num_block(num_block), num_in_ch(num_in_ch), num_out_ch(num_out_ch), num_feat(num_feat), num_grow_ch(num_grow_ch) {
|
||||
blocks["conv_first"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_in_ch, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
for (int i = 0; i < num_block; i++) {
|
||||
std::string name = "body." + std::to_string(i);
|
||||
blocks[name] = std::shared_ptr<GGMLBlock>(new RRDB(num_feat, num_grow_ch));
|
||||
}
|
||||
blocks["conv_body"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
// upsample
|
||||
blocks["conv_up1"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_up2"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
if (scale >= 2) {
|
||||
blocks["conv_up1"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
if (scale == 4) {
|
||||
blocks["conv_up2"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
blocks["conv_hr"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_feat, {3, 3}, {1, 1}, {1, 1}));
|
||||
blocks["conv_last"] = std::shared_ptr<GGMLBlock>(new Conv2d(num_feat, num_out_ch, {3, 3}, {1, 1}, {1, 1}));
|
||||
}
|
||||
|
||||
int get_scale() { return scale; }
|
||||
int get_num_block() { return num_block; }
|
||||
|
||||
struct ggml_tensor* lrelu(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
return ggml_leaky_relu(ctx, x, 0.2f, true);
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
// x: [n, num_in_ch, h, w]
|
||||
// return: [n, num_out_ch, h*4, w*4]
|
||||
// return: [n, num_out_ch, h*scale, w*scale]
|
||||
auto conv_first = std::dynamic_pointer_cast<Conv2d>(blocks["conv_first"]);
|
||||
auto conv_body = std::dynamic_pointer_cast<Conv2d>(blocks["conv_body"]);
|
||||
auto conv_up1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up1"]);
|
||||
auto conv_up2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up2"]);
|
||||
auto conv_hr = std::dynamic_pointer_cast<Conv2d>(blocks["conv_hr"]);
|
||||
auto conv_last = std::dynamic_pointer_cast<Conv2d>(blocks["conv_last"]);
|
||||
|
||||
@@ -130,55 +135,231 @@ public:
|
||||
body_feat = conv_body->forward(ctx, body_feat);
|
||||
feat = ggml_add(ctx, feat, body_feat);
|
||||
// upsample
|
||||
feat = lrelu(ctx, conv_up1->forward(ctx, ggml_upscale(ctx, feat, 2)));
|
||||
feat = lrelu(ctx, conv_up2->forward(ctx, ggml_upscale(ctx, feat, 2)));
|
||||
if (scale >= 2) {
|
||||
auto conv_up1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up1"]);
|
||||
feat = lrelu(ctx, conv_up1->forward(ctx, ggml_upscale(ctx, feat, 2, GGML_SCALE_MODE_NEAREST)));
|
||||
if (scale == 4) {
|
||||
auto conv_up2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv_up2"]);
|
||||
feat = lrelu(ctx, conv_up2->forward(ctx, ggml_upscale(ctx, feat, 2, GGML_SCALE_MODE_NEAREST)));
|
||||
}
|
||||
}
|
||||
// for all scales
|
||||
auto out = conv_last->forward(ctx, lrelu(ctx, conv_hr->forward(ctx, feat)));
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
struct ESRGAN : public GGMLRunner {
|
||||
RRDBNet rrdb_net;
|
||||
std::unique_ptr<RRDBNet> rrdb_net;
|
||||
int scale = 4;
|
||||
int tile_size = 128; // avoid cuda OOM for 4gb VRAM
|
||||
|
||||
ESRGAN(ggml_backend_t backend, std::map<std::string, enum ggml_type>& tensor_types)
|
||||
: GGMLRunner(backend) {
|
||||
rrdb_net.init(params_ctx, tensor_types, "");
|
||||
ESRGAN(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {})
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||
// rrdb_net will be created in load_from_file
|
||||
}
|
||||
|
||||
void enable_conv2d_direct() {
|
||||
if (!rrdb_net)
|
||||
return;
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
rrdb_net->get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->enable_direct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return "esrgan";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path) {
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading esrgan from '%s'", file_path.c_str());
|
||||
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> esrgan_tensors;
|
||||
rrdb_net.get_param_tensors(esrgan_tensors);
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init esrgan model loader from file failed: '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(esrgan_tensors, backend);
|
||||
// Get tensor names
|
||||
auto tensor_names = model_loader.get_tensor_names();
|
||||
|
||||
// Detect if it's ESRGAN format
|
||||
bool is_ESRGAN = std::find(tensor_names.begin(), tensor_names.end(), "model.0.weight") != tensor_names.end();
|
||||
|
||||
// Detect parameters from tensor names
|
||||
int detected_num_block = 0;
|
||||
if (is_ESRGAN) {
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("model.1.sub.") == 0) {
|
||||
size_t first_dot = name.find('.', 12);
|
||||
if (first_dot != std::string::npos) {
|
||||
size_t second_dot = name.find('.', first_dot + 1);
|
||||
if (second_dot != std::string::npos && name.substr(first_dot + 1, 3) == "RDB") {
|
||||
try {
|
||||
int idx = std::stoi(name.substr(12, first_dot - 12));
|
||||
detected_num_block = std::max(detected_num_block, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Original format
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("body.") == 0) {
|
||||
size_t pos = name.find('.', 5);
|
||||
if (pos != std::string::npos) {
|
||||
try {
|
||||
int idx = std::stoi(name.substr(5, pos - 5));
|
||||
detected_num_block = std::max(detected_num_block, idx + 1);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int detected_scale = 4; // default
|
||||
if (is_ESRGAN) {
|
||||
// For ESRGAN format, detect scale by highest model number
|
||||
int max_model_num = 0;
|
||||
for (const auto& name : tensor_names) {
|
||||
if (name.find("model.") == 0) {
|
||||
size_t dot_pos = name.find('.', 6);
|
||||
if (dot_pos != std::string::npos) {
|
||||
try {
|
||||
int num = std::stoi(name.substr(6, dot_pos - 6));
|
||||
max_model_num = std::max(max_model_num, num);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_model_num <= 4) {
|
||||
detected_scale = 1;
|
||||
} else if (max_model_num <= 7) {
|
||||
detected_scale = 2;
|
||||
} else {
|
||||
detected_scale = 4;
|
||||
}
|
||||
} else {
|
||||
// Original format
|
||||
bool has_conv_up2 = std::any_of(tensor_names.begin(), tensor_names.end(), [](const std::string& name) {
|
||||
return name == "conv_up2.weight";
|
||||
});
|
||||
bool has_conv_up1 = std::any_of(tensor_names.begin(), tensor_names.end(), [](const std::string& name) {
|
||||
return name == "conv_up1.weight";
|
||||
});
|
||||
if (has_conv_up2) {
|
||||
detected_scale = 4;
|
||||
} else if (has_conv_up1) {
|
||||
detected_scale = 2;
|
||||
} else {
|
||||
detected_scale = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int detected_num_in_ch = 3;
|
||||
int detected_num_out_ch = 3;
|
||||
int detected_num_feat = 64;
|
||||
int detected_num_grow_ch = 32;
|
||||
|
||||
// Create RRDBNet with detected parameters
|
||||
rrdb_net = std::make_unique<RRDBNet>(detected_scale, detected_num_block, detected_num_in_ch, detected_num_out_ch, detected_num_feat, detected_num_grow_ch);
|
||||
rrdb_net->init(params_ctx, {}, "");
|
||||
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> esrgan_tensors;
|
||||
rrdb_net->get_param_tensors(esrgan_tensors);
|
||||
|
||||
bool success;
|
||||
if (is_ESRGAN) {
|
||||
// Build name mapping for ESRGAN format
|
||||
std::map<std::string, std::string> expected_to_model;
|
||||
expected_to_model["conv_first.weight"] = "model.0.weight";
|
||||
expected_to_model["conv_first.bias"] = "model.0.bias";
|
||||
|
||||
for (int i = 0; i < detected_num_block; i++) {
|
||||
for (int j = 1; j <= 3; j++) {
|
||||
for (int k = 1; k <= 5; k++) {
|
||||
std::string expected_weight = "body." + std::to_string(i) + ".rdb" + std::to_string(j) + ".conv" + std::to_string(k) + ".weight";
|
||||
std::string model_weight = "model.1.sub." + std::to_string(i) + ".RDB" + std::to_string(j) + ".conv" + std::to_string(k) + ".0.weight";
|
||||
expected_to_model[expected_weight] = model_weight;
|
||||
|
||||
std::string expected_bias = "body." + std::to_string(i) + ".rdb" + std::to_string(j) + ".conv" + std::to_string(k) + ".bias";
|
||||
std::string model_bias = "model.1.sub." + std::to_string(i) + ".RDB" + std::to_string(j) + ".conv" + std::to_string(k) + ".0.bias";
|
||||
expected_to_model[expected_bias] = model_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (detected_scale == 1) {
|
||||
expected_to_model["conv_body.weight"] = "model.1.sub." + std::to_string(detected_num_block) + ".weight";
|
||||
expected_to_model["conv_body.bias"] = "model.1.sub." + std::to_string(detected_num_block) + ".bias";
|
||||
expected_to_model["conv_hr.weight"] = "model.2.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.2.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.4.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.4.bias";
|
||||
} else {
|
||||
expected_to_model["conv_body.weight"] = "model.1.sub." + std::to_string(detected_num_block) + ".weight";
|
||||
expected_to_model["conv_body.bias"] = "model.1.sub." + std::to_string(detected_num_block) + ".bias";
|
||||
if (detected_scale >= 2) {
|
||||
expected_to_model["conv_up1.weight"] = "model.3.weight";
|
||||
expected_to_model["conv_up1.bias"] = "model.3.bias";
|
||||
}
|
||||
if (detected_scale == 4) {
|
||||
expected_to_model["conv_up2.weight"] = "model.6.weight";
|
||||
expected_to_model["conv_up2.bias"] = "model.6.bias";
|
||||
expected_to_model["conv_hr.weight"] = "model.8.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.8.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.10.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.10.bias";
|
||||
} else if (detected_scale == 2) {
|
||||
expected_to_model["conv_hr.weight"] = "model.5.weight";
|
||||
expected_to_model["conv_hr.bias"] = "model.5.bias";
|
||||
expected_to_model["conv_last.weight"] = "model.7.weight";
|
||||
expected_to_model["conv_last.bias"] = "model.7.bias";
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, ggml_tensor*> model_tensors;
|
||||
for (auto& p : esrgan_tensors) {
|
||||
auto it = expected_to_model.find(p.first);
|
||||
if (it != expected_to_model.end()) {
|
||||
model_tensors[it->second] = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
success = model_loader.load_tensors(model_tensors, {}, n_threads);
|
||||
} else {
|
||||
success = model_loader.load_tensors(esrgan_tensors, {}, n_threads);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load esrgan tensors from model loader failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("esrgan model loaded");
|
||||
scale = rrdb_net->get_scale();
|
||||
LOG_INFO("esrgan model loaded with scale=%d, num_block=%d", scale, detected_num_block);
|
||||
return success;
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* x) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
x = to_backend(x);
|
||||
struct ggml_tensor* out = rrdb_net.forward(compute_ctx, x);
|
||||
if (!rrdb_net)
|
||||
return nullptr;
|
||||
constexpr int kGraphNodes = 1 << 16; // 65k
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, kGraphNodes, /*grads*/ false);
|
||||
x = to_backend(x);
|
||||
struct ggml_tensor* out = rrdb_net->forward(compute_ctx, x);
|
||||
ggml_build_forward_expand(gf, out);
|
||||
return gf;
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ set(TARGET sd)
|
||||
add_executable(${TARGET} main.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_11)
|
||||
target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17)
|
||||
@@ -0,0 +1,110 @@
|
||||
# Run
|
||||
|
||||
```
|
||||
usage: ./bin/sd [options]
|
||||
|
||||
Options:
|
||||
-m, --model <string> path to full model
|
||||
--clip_l <string> path to the clip-l text encoder
|
||||
--clip_g <string> path to the clip-g text encoder
|
||||
--clip_vision <string> path to the clip-vision encoder
|
||||
--t5xxl <string> path to the t5xxl text encoder
|
||||
--qwen2vl <string> path to the qwen2vl text encoder
|
||||
--qwen2vl_vision <string> path to the qwen2vl vit
|
||||
--diffusion-model <string> path to the standalone diffusion model
|
||||
--high-noise-diffusion-model <string> path to the standalone high noise diffusion model
|
||||
--vae <string> path to standalone vae model
|
||||
--taesd <string> path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
|
||||
--control-net <string> path to control net model
|
||||
--embd-dir <string> embeddings directory
|
||||
--lora-model-dir <string> lora model directory
|
||||
-i, --init-img <string> path to the init image
|
||||
--end-img <string> path to the end image, required by flf2v
|
||||
--tensor-type-rules <string> weight type per tensor pattern (example: "^vae\.=f16,model\.=q8_0")
|
||||
--photo-maker <string> path to PHOTOMAKER model
|
||||
--pm-id-images-dir <string> path to PHOTOMAKER input id images dir
|
||||
--pm-id-embed-path <string> path to PHOTOMAKER v2 id embed
|
||||
--mask <string> path to the mask image
|
||||
--control-image <string> path to control image, control net
|
||||
--control-video <string> path to control video frames, It must be a directory path. The video frames inside should be stored as images in
|
||||
lexicographical (character) order. For example, if the control video path is
|
||||
`frames`, the directory contain images such as 00.png, 01.png, ... etc.
|
||||
-o, --output <string> path to write result image to (default: ./output.png)
|
||||
-p, --prompt <string> the prompt to render
|
||||
-n, --negative-prompt <string> the negative prompt (default: "")
|
||||
--upscale-model <string> path to esrgan model.
|
||||
-t, --threads <int> number of threads to use during computation (default: -1). If threads <= 0, then threads will be set to the number of
|
||||
CPU physical cores
|
||||
--upscale-repeats <int> Run the ESRGAN upscaler this many times (default: 1)
|
||||
-H, --height <int> image height, in pixel space (default: 512)
|
||||
-W, --width <int> image width, in pixel space (default: 512)
|
||||
--steps <int> number of sample steps (default: 20)
|
||||
--high-noise-steps <int> (high noise) number of sample steps (default: -1 = auto)
|
||||
--clip-skip <int> ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1). <= 0 represents unspecified,
|
||||
will be 1 for SD1.x, 2 for SD2.x
|
||||
-b, --batch-count <int> batch count
|
||||
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
|
||||
--video-frames <int> video frames (default: 1)
|
||||
--fps <int> fps (default: 24)
|
||||
--timestep-shift <int> shift timestep for NitroFusion models (default: 0). recommended N for NitroSD-Realism around 250 and 500 for
|
||||
NitroSD-Vibrant
|
||||
--cfg-scale <float> unconditional guidance scale: (default: 7.0)
|
||||
--img-cfg-scale <float> image guidance scale for inpaint or instruct-pix2pix models: (default: same as --cfg-scale)
|
||||
--guidance <float> distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--slg-scale <float> skip layer guidance (SLG) scale, only for DiT models: (default: 0). 0 means disabled, a value of 2.5 is nice for sd3.5
|
||||
medium
|
||||
--skip-layer-start <float> SLG enabling point (default: 0.01)
|
||||
--skip-layer-end <float> SLG disabling point (default: 0.2)
|
||||
--eta <float> eta in DDIM, only for DDIM and TCD (default: 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 instruct-pix2pix models (default: same as --cfg-scale)
|
||||
--high-noise-guidance <float> (high noise) distilled guidance scale for models with guidance input (default: 3.5)
|
||||
--high-noise-slg-scale <float> (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0)
|
||||
--high-noise-skip-layer-start <float> (high noise) SLG enabling point (default: 0.01)
|
||||
--high-noise-skip-layer-end <float> (high noise) SLG disabling point (default: 0.2)
|
||||
--high-noise-eta <float> (high noise) eta in DDIM, only for DDIM and TCD (default: 0)
|
||||
--strength <float> strength for noising/unnoising (default: 0.75)
|
||||
--pm-style-strength <float>
|
||||
--control-strength <float> strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image
|
||||
--moe-boundary <float> timestep boundary for Wan2.2 MoE model. (default: 0.875). Only enabled if `--high-noise-steps` is set to -1
|
||||
--flow-shift <float> shift value for Flow models like SD3.x or WAN (default: auto)
|
||||
--vace-strength <float> wan vace strength
|
||||
--vae-tile-overlap <float> tile overlap for vae tiling, in fraction of tile size (default: 0.5)
|
||||
--vae-tiling process vae in tiles to reduce memory usage
|
||||
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
|
||||
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM when needed
|
||||
--control-net-cpu keep controlnet in cpu (for low vram)
|
||||
--clip-on-cpu keep clip in cpu (for low vram)
|
||||
--vae-on-cpu keep vae in cpu (for low vram)
|
||||
--diffusion-fa use flash attention in the diffusion model
|
||||
--diffusion-conv-direct use ggml_conv2d_direct in the diffusion model
|
||||
--vae-conv-direct use ggml_conv2d_direct in the vae model
|
||||
--canny apply canny preprocessor (edge detection)
|
||||
-v, --verbose print extra info
|
||||
--color colors the logging tags according to level
|
||||
--chroma-disable-dit-mask disable dit mask for chroma
|
||||
--chroma-enable-t5-mask enable t5 mask for chroma
|
||||
--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
|
||||
-M, --mode run mode, one of [img_gen, vid_gen, upscale, convert], default: img_gen
|
||||
--type weight type (examples: f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K). If not specified, the default is the
|
||||
type of the weight file
|
||||
--rng RNG, one of [std_default, cuda], default: cuda
|
||||
-s, --seed RNG seed (default: 42, use random seed for < 0)
|
||||
--sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing,
|
||||
tcd] (default: euler for Flux/SD3/Wan, euler_a otherwise)
|
||||
--prediction prediction type override, one of [eps, v, edm_v, sd3_flow, flux_flow]
|
||||
--scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple], default:
|
||||
discrete
|
||||
--skip-layers layers to skip for SLG steps (default: [7,8,9])
|
||||
--high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm,
|
||||
ddim_trailing, tcd] default: euler for Flux/SD3/Wan, euler_a otherwise
|
||||
--high-noise-scheduler (high noise) denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform,
|
||||
simple], default: discrete
|
||||
--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)
|
||||
-h, --help show this help message and exit
|
||||
--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)
|
||||
```
|
||||
@@ -0,0 +1,217 @@
|
||||
#ifndef __AVI_WRITER_H__
|
||||
#define __AVI_WRITER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stable-diffusion.h"
|
||||
|
||||
#ifndef INCLUDE_STB_IMAGE_WRITE_H
|
||||
#include "stb_image_write.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} avi_index_entry;
|
||||
|
||||
// Write 32-bit little-endian integer
|
||||
void write_u32_le(FILE* f, uint32_t val) {
|
||||
fwrite(&val, 4, 1, f);
|
||||
}
|
||||
|
||||
// Write 16-bit little-endian integer
|
||||
void write_u16_le(FILE* f, uint16_t val) {
|
||||
fwrite(&val, 2, 1, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MJPG AVI file from an array of sd_image_t images.
|
||||
* Images are encoded to JPEG using stb_image_write.
|
||||
*
|
||||
* @param filename Output AVI file name.
|
||||
* @param images Array of input images.
|
||||
* @param num_images Number of images in the array.
|
||||
* @param fps Frames per second for the video.
|
||||
* @param quality JPEG quality (0-100).
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int create_mjpg_avi_from_sd_images(const char* filename, sd_image_t* images, int num_images, int fps, int quality = 90) {
|
||||
if (num_images == 0) {
|
||||
fprintf(stderr, "Error: Image array is empty.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE* f = fopen(filename, "wb");
|
||||
if (!f) {
|
||||
perror("Error opening file for writing");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t width = images[0].width;
|
||||
uint32_t height = images[0].height;
|
||||
uint32_t channels = images[0].channel;
|
||||
if (channels != 3 && channels != 4) {
|
||||
fprintf(stderr, "Error: Unsupported channel count: %u\n", channels);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- RIFF AVI Header ---
|
||||
fwrite("RIFF", 4, 1, f);
|
||||
long riff_size_pos = ftell(f);
|
||||
write_u32_le(f, 0); // Placeholder for file size
|
||||
fwrite("AVI ", 4, 1, f);
|
||||
|
||||
// 'hdrl' LIST (header list)
|
||||
fwrite("LIST", 4, 1, f);
|
||||
write_u32_le(f, 4 + 8 + 56 + 8 + 4 + 8 + 56 + 8 + 40);
|
||||
fwrite("hdrl", 4, 1, f);
|
||||
|
||||
// 'avih' chunk (AVI main header)
|
||||
fwrite("avih", 4, 1, f);
|
||||
write_u32_le(f, 56);
|
||||
write_u32_le(f, 1000000 / fps); // Microseconds per frame
|
||||
write_u32_le(f, 0); // Max bytes per second
|
||||
write_u32_le(f, 0); // Padding granularity
|
||||
write_u32_le(f, 0x110); // Flags (HASINDEX | ISINTERLEAVED)
|
||||
write_u32_le(f, num_images); // Total frames
|
||||
write_u32_le(f, 0); // Initial frames
|
||||
write_u32_le(f, 1); // Number of streams
|
||||
write_u32_le(f, width * height * 3); // Suggested buffer size
|
||||
write_u32_le(f, width);
|
||||
write_u32_le(f, height);
|
||||
write_u32_le(f, 0); // Reserved
|
||||
write_u32_le(f, 0); // Reserved
|
||||
write_u32_le(f, 0); // Reserved
|
||||
write_u32_le(f, 0); // Reserved
|
||||
|
||||
// 'strl' LIST (stream list)
|
||||
fwrite("LIST", 4, 1, f);
|
||||
write_u32_le(f, 4 + 8 + 56 + 8 + 40);
|
||||
fwrite("strl", 4, 1, f);
|
||||
|
||||
// 'strh' chunk (stream header)
|
||||
fwrite("strh", 4, 1, f);
|
||||
write_u32_le(f, 56);
|
||||
fwrite("vids", 4, 1, f); // Stream type: video
|
||||
fwrite("MJPG", 4, 1, f); // Codec: Motion JPEG
|
||||
write_u32_le(f, 0); // Flags
|
||||
write_u16_le(f, 0); // Priority
|
||||
write_u16_le(f, 0); // Language
|
||||
write_u32_le(f, 0); // Initial frames
|
||||
write_u32_le(f, 1); // Scale
|
||||
write_u32_le(f, fps); // Rate
|
||||
write_u32_le(f, 0); // Start
|
||||
write_u32_le(f, num_images); // Length
|
||||
write_u32_le(f, width * height * 3); // Suggested buffer size
|
||||
write_u32_le(f, (uint32_t)-1); // Quality
|
||||
write_u32_le(f, 0); // Sample size
|
||||
write_u16_le(f, 0); // rcFrame.left
|
||||
write_u16_le(f, 0); // rcFrame.top
|
||||
write_u16_le(f, 0); // rcFrame.right
|
||||
write_u16_le(f, 0); // rcFrame.bottom
|
||||
|
||||
// 'strf' chunk (stream format: BITMAPINFOHEADER)
|
||||
fwrite("strf", 4, 1, f);
|
||||
write_u32_le(f, 40);
|
||||
write_u32_le(f, 40); // biSize
|
||||
write_u32_le(f, width);
|
||||
write_u32_le(f, height);
|
||||
write_u16_le(f, 1); // biPlanes
|
||||
write_u16_le(f, 24); // biBitCount
|
||||
fwrite("MJPG", 4, 1, f); // biCompression (FOURCC)
|
||||
write_u32_le(f, width * height * 3); // biSizeImage
|
||||
write_u32_le(f, 0); // XPelsPerMeter
|
||||
write_u32_le(f, 0); // YPelsPerMeter
|
||||
write_u32_le(f, 0); // Colors used
|
||||
write_u32_le(f, 0); // Colors important
|
||||
|
||||
// 'movi' LIST (video frames)
|
||||
long movi_list_pos = ftell(f);
|
||||
fwrite("LIST", 4, 1, f);
|
||||
long movi_size_pos = ftell(f);
|
||||
write_u32_le(f, 0); // Placeholder for movi size
|
||||
fwrite("movi", 4, 1, f);
|
||||
|
||||
avi_index_entry* index = (avi_index_entry*)malloc(sizeof(avi_index_entry) * num_images);
|
||||
if (!index) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Encode and write each frame as JPEG
|
||||
struct {
|
||||
uint8_t* buf;
|
||||
size_t size;
|
||||
} jpeg_data;
|
||||
|
||||
for (int i = 0; i < num_images; i++) {
|
||||
jpeg_data.buf = NULL;
|
||||
jpeg_data.size = 0;
|
||||
|
||||
// Callback function to collect JPEG data into memory
|
||||
auto write_to_buf = [](void* context, void* data, int size) {
|
||||
auto jd = (decltype(jpeg_data)*)context;
|
||||
jd->buf = (uint8_t*)realloc(jd->buf, jd->size + size);
|
||||
memcpy(jd->buf + jd->size, data, size);
|
||||
jd->size += size;
|
||||
};
|
||||
|
||||
// Encode to JPEG in memory
|
||||
stbi_write_jpg_to_func(
|
||||
write_to_buf,
|
||||
&jpeg_data,
|
||||
images[i].width,
|
||||
images[i].height,
|
||||
channels,
|
||||
images[i].data,
|
||||
quality);
|
||||
|
||||
// Write '00dc' chunk (video frame)
|
||||
fwrite("00dc", 4, 1, f);
|
||||
write_u32_le(f, jpeg_data.size);
|
||||
index[i].offset = ftell(f) - 8;
|
||||
index[i].size = jpeg_data.size;
|
||||
fwrite(jpeg_data.buf, 1, jpeg_data.size, f);
|
||||
|
||||
// Align to even byte size
|
||||
if (jpeg_data.size % 2)
|
||||
fputc(0, f);
|
||||
|
||||
free(jpeg_data.buf);
|
||||
}
|
||||
|
||||
// Finalize 'movi' size
|
||||
long cur_pos = ftell(f);
|
||||
long movi_size = cur_pos - movi_size_pos - 4;
|
||||
fseek(f, movi_size_pos, SEEK_SET);
|
||||
write_u32_le(f, movi_size);
|
||||
fseek(f, cur_pos, SEEK_SET);
|
||||
|
||||
// Write 'idx1' index
|
||||
fwrite("idx1", 4, 1, f);
|
||||
write_u32_le(f, num_images * 16);
|
||||
for (int i = 0; i < num_images; i++) {
|
||||
fwrite("00dc", 4, 1, f);
|
||||
write_u32_le(f, 0x10);
|
||||
write_u32_le(f, index[i].offset);
|
||||
write_u32_le(f, index[i].size);
|
||||
}
|
||||
|
||||
// Finalize RIFF size
|
||||
cur_pos = ftell(f);
|
||||
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);
|
||||
|
||||
fclose(f);
|
||||
free(index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // __AVI_WRITER_H__
|
||||
+1562
-811
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "ggml_extend.hpp"
|
||||
#include "model.h"
|
||||
#include "rope.hpp"
|
||||
|
||||
#define FLUX_GRAPH_SIZE 10240
|
||||
|
||||
@@ -35,8 +36,8 @@ namespace Flux {
|
||||
int64_t hidden_size;
|
||||
float eps;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
ggml_type wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "scale") != tensor_types.end()) ? tensor_types[prefix + "scale"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
ggml_type wtype = GGML_TYPE_F32;
|
||||
params["scale"] = ggml_new_tensor_1d(ctx, wtype, hidden_size);
|
||||
}
|
||||
|
||||
@@ -80,54 +81,6 @@ namespace Flux {
|
||||
}
|
||||
};
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* apply_rope(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* pe) {
|
||||
// x: [N, L, n_head, d_head]
|
||||
// pe: [L, d_head/2, 2, 2]
|
||||
int64_t d_head = x->ne[0];
|
||||
int64_t n_head = x->ne[1];
|
||||
int64_t L = x->ne[2];
|
||||
int64_t N = x->ne[3];
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N, n_head, L, d_head]
|
||||
x = ggml_reshape_4d(ctx, x, 2, d_head / 2, L, n_head * N); // [N * n_head, L, d_head/2, 2]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 3, 0, 1, 2)); // [2, N * n_head, L, d_head/2]
|
||||
|
||||
int64_t offset = x->nb[2] * x->ne[2];
|
||||
auto x_0 = ggml_view_3d(ctx, x, x->ne[0], x->ne[1], x->ne[2], x->nb[1], x->nb[2], offset * 0); // [N * n_head, L, d_head/2]
|
||||
auto x_1 = ggml_view_3d(ctx, x, x->ne[0], x->ne[1], x->ne[2], x->nb[1], x->nb[2], offset * 1); // [N * n_head, L, d_head/2]
|
||||
x_0 = ggml_reshape_4d(ctx, x_0, 1, x_0->ne[0], x_0->ne[1], x_0->ne[2]); // [N * n_head, L, d_head/2, 1]
|
||||
x_1 = ggml_reshape_4d(ctx, x_1, 1, x_1->ne[0], x_1->ne[1], x_1->ne[2]); // [N * n_head, L, d_head/2, 1]
|
||||
auto temp_x = ggml_new_tensor_4d(ctx, x_0->type, 2, x_0->ne[1], x_0->ne[2], x_0->ne[3]);
|
||||
x_0 = ggml_repeat(ctx, x_0, temp_x); // [N * n_head, L, d_head/2, 2]
|
||||
x_1 = ggml_repeat(ctx, x_1, temp_x); // [N * n_head, L, d_head/2, 2]
|
||||
|
||||
pe = ggml_cont(ctx, ggml_permute(ctx, pe, 3, 0, 1, 2)); // [2, L, d_head/2, 2]
|
||||
offset = pe->nb[2] * pe->ne[2];
|
||||
auto pe_0 = ggml_view_3d(ctx, pe, pe->ne[0], pe->ne[1], pe->ne[2], pe->nb[1], pe->nb[2], offset * 0); // [L, d_head/2, 2]
|
||||
auto pe_1 = ggml_view_3d(ctx, pe, pe->ne[0], pe->ne[1], pe->ne[2], pe->nb[1], pe->nb[2], offset * 1); // [L, d_head/2, 2]
|
||||
|
||||
auto x_out = ggml_add_inplace(ctx, ggml_mul(ctx, x_0, pe_0), ggml_mul(ctx, x_1, pe_1)); // [N * n_head, L, d_head/2, 2]
|
||||
x_out = ggml_reshape_3d(ctx, x_out, d_head, L, n_head * N); // [N*n_head, L, d_head]
|
||||
return x_out;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* attention(struct ggml_context* ctx,
|
||||
struct ggml_tensor* q,
|
||||
struct ggml_tensor* k,
|
||||
struct ggml_tensor* v,
|
||||
struct ggml_tensor* pe,
|
||||
bool flash_attn) {
|
||||
// q,k,v: [N, L, n_head, d_head]
|
||||
// pe: [L, d_head/2, 2, 2]
|
||||
// return: [N, L, n_head*d_head]
|
||||
q = apply_rope(ctx, q, pe); // [N*n_head, L, d_head]
|
||||
k = apply_rope(ctx, k, pe); // [N*n_head, L, d_head]
|
||||
|
||||
auto x = ggml_nn_attention_ext(ctx, q, k, v, v->ne[1], NULL, false, true, flash_attn); // [N, L, n_head*d_head]
|
||||
return x;
|
||||
}
|
||||
|
||||
struct SelfAttention : public GGMLBlock {
|
||||
public:
|
||||
int64_t num_heads;
|
||||
@@ -167,13 +120,17 @@ namespace Flux {
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, struct ggml_tensor* pe) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* mask) {
|
||||
// x: [N, n_token, dim]
|
||||
// pe: [n_token, d_head/2, 2, 2]
|
||||
// return [N, n_token, dim]
|
||||
auto qkv = pre_attention(ctx, x); // q,k,v: [N, n_token, n_head, d_head]
|
||||
x = attention(ctx, qkv[0], qkv[1], qkv[2], pe, flash_attn); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
auto qkv = pre_attention(ctx, x); // q,k,v: [N, n_token, n_head, d_head]
|
||||
x = Rope::attention(ctx, backend, qkv[0], qkv[1], qkv[2], pe, mask, flash_attn); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
return x;
|
||||
}
|
||||
};
|
||||
@@ -185,6 +142,13 @@ namespace Flux {
|
||||
|
||||
ModulationOut(ggml_tensor* shift = NULL, ggml_tensor* scale = NULL, ggml_tensor* gate = NULL)
|
||||
: shift(shift), scale(scale), gate(gate) {}
|
||||
|
||||
ModulationOut(struct ggml_context* ctx, ggml_tensor* vec, int64_t offset) {
|
||||
int64_t stride = vec->nb[1] * vec->ne[1];
|
||||
shift = ggml_view_2d(ctx, vec, vec->ne[0], vec->ne[1], vec->nb[1], stride * (offset + 0)); // [N, dim]
|
||||
scale = ggml_view_2d(ctx, vec, vec->ne[0], vec->ne[1], vec->nb[1], stride * (offset + 1)); // [N, dim]
|
||||
gate = ggml_view_2d(ctx, vec, vec->ne[0], vec->ne[1], vec->nb[1], stride * (offset + 2)); // [N, dim]
|
||||
}
|
||||
};
|
||||
|
||||
struct Modulation : public GGMLBlock {
|
||||
@@ -210,19 +174,12 @@ namespace Flux {
|
||||
auto m = ggml_reshape_3d(ctx, out, vec->ne[0], multiplier, vec->ne[1]); // [N, multiplier, dim]
|
||||
m = ggml_cont(ctx, ggml_permute(ctx, m, 0, 2, 1, 3)); // [multiplier, N, dim]
|
||||
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
auto shift_0 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, dim]
|
||||
auto scale_0 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, dim]
|
||||
auto gate_0 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 2); // [N, dim]
|
||||
|
||||
ModulationOut m_0 = ModulationOut(ctx, m, 0);
|
||||
if (is_double) {
|
||||
auto shift_1 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 3); // [N, dim]
|
||||
auto scale_1 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 4); // [N, dim]
|
||||
auto gate_1 = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 5); // [N, dim]
|
||||
return {ModulationOut(shift_0, scale_0, gate_0), ModulationOut(shift_1, scale_1, gate_1)};
|
||||
return {m_0, ModulationOut(ctx, m, 3)};
|
||||
}
|
||||
|
||||
return {ModulationOut(shift_0, scale_0, gate_0), ModulationOut()};
|
||||
return {m_0, ModulationOut()};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -242,25 +199,33 @@ namespace Flux {
|
||||
|
||||
struct DoubleStreamBlock : public GGMLBlock {
|
||||
bool flash_attn;
|
||||
bool prune_mod;
|
||||
int idx = 0;
|
||||
|
||||
public:
|
||||
DoubleStreamBlock(int64_t hidden_size,
|
||||
int64_t num_heads,
|
||||
float mlp_ratio,
|
||||
int idx = 0,
|
||||
bool qkv_bias = false,
|
||||
bool flash_attn = false)
|
||||
: flash_attn(flash_attn) {
|
||||
bool flash_attn = false,
|
||||
bool prune_mod = false)
|
||||
: idx(idx), flash_attn(flash_attn), prune_mod(prune_mod) {
|
||||
int64_t mlp_hidden_dim = hidden_size * mlp_ratio;
|
||||
blocks["img_mod"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, true));
|
||||
blocks["img_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-6f, false));
|
||||
blocks["img_attn"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qkv_bias, flash_attn));
|
||||
if (!prune_mod) {
|
||||
blocks["img_mod"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, true));
|
||||
}
|
||||
blocks["img_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-6f, false));
|
||||
blocks["img_attn"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qkv_bias, flash_attn));
|
||||
|
||||
blocks["img_norm2"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-6f, false));
|
||||
blocks["img_mlp.0"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, mlp_hidden_dim));
|
||||
// img_mlp.1 is nn.GELU(approximate="tanh")
|
||||
blocks["img_mlp.2"] = std::shared_ptr<GGMLBlock>(new Linear(mlp_hidden_dim, hidden_size));
|
||||
|
||||
blocks["txt_mod"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, true));
|
||||
if (!prune_mod) {
|
||||
blocks["txt_mod"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, true));
|
||||
}
|
||||
blocks["txt_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-6f, false));
|
||||
blocks["txt_attn"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qkv_bias, flash_attn));
|
||||
|
||||
@@ -270,17 +235,35 @@ namespace Flux {
|
||||
blocks["txt_mlp.2"] = std::shared_ptr<GGMLBlock>(new Linear(mlp_hidden_dim, hidden_size));
|
||||
}
|
||||
|
||||
std::vector<ModulationOut> get_distil_img_mod(struct ggml_context* ctx, struct ggml_tensor* vec) {
|
||||
// TODO: not hardcoded?
|
||||
const int single_blocks_count = 38;
|
||||
const int double_blocks_count = 19;
|
||||
|
||||
int64_t offset = 6 * idx + 3 * single_blocks_count;
|
||||
return {ModulationOut(ctx, vec, offset), ModulationOut(ctx, vec, offset + 3)};
|
||||
}
|
||||
|
||||
std::vector<ModulationOut> get_distil_txt_mod(struct ggml_context* ctx, struct ggml_tensor* vec) {
|
||||
// TODO: not hardcoded?
|
||||
const int single_blocks_count = 38;
|
||||
const int double_blocks_count = 19;
|
||||
|
||||
int64_t offset = 6 * idx + 6 * double_blocks_count + 3 * single_blocks_count;
|
||||
return {ModulationOut(ctx, vec, offset), ModulationOut(ctx, vec, offset + 3)};
|
||||
}
|
||||
|
||||
std::pair<struct ggml_tensor*, struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* img,
|
||||
struct ggml_tensor* txt,
|
||||
struct ggml_tensor* vec,
|
||||
struct ggml_tensor* pe) {
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* mask = NULL) {
|
||||
// img: [N, n_img_token, hidden_size]
|
||||
// txt: [N, n_txt_token, hidden_size]
|
||||
// pe: [n_img_token + n_txt_token, d_head/2, 2, 2]
|
||||
// return: ([N, n_img_token, hidden_size], [N, n_txt_token, hidden_size])
|
||||
|
||||
auto img_mod = std::dynamic_pointer_cast<Modulation>(blocks["img_mod"]);
|
||||
auto img_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["img_norm1"]);
|
||||
auto img_attn = std::dynamic_pointer_cast<SelfAttention>(blocks["img_attn"]);
|
||||
|
||||
@@ -288,7 +271,6 @@ namespace Flux {
|
||||
auto img_mlp_0 = std::dynamic_pointer_cast<Linear>(blocks["img_mlp.0"]);
|
||||
auto img_mlp_2 = std::dynamic_pointer_cast<Linear>(blocks["img_mlp.2"]);
|
||||
|
||||
auto txt_mod = std::dynamic_pointer_cast<Modulation>(blocks["txt_mod"]);
|
||||
auto txt_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["txt_norm1"]);
|
||||
auto txt_attn = std::dynamic_pointer_cast<SelfAttention>(blocks["txt_attn"]);
|
||||
|
||||
@@ -296,10 +278,22 @@ namespace Flux {
|
||||
auto txt_mlp_0 = std::dynamic_pointer_cast<Linear>(blocks["txt_mlp.0"]);
|
||||
auto txt_mlp_2 = std::dynamic_pointer_cast<Linear>(blocks["txt_mlp.2"]);
|
||||
|
||||
auto img_mods = img_mod->forward(ctx, vec);
|
||||
std::vector<ModulationOut> img_mods;
|
||||
if (prune_mod) {
|
||||
img_mods = get_distil_img_mod(ctx, vec);
|
||||
} else {
|
||||
auto img_mod = std::dynamic_pointer_cast<Modulation>(blocks["img_mod"]);
|
||||
img_mods = img_mod->forward(ctx, vec);
|
||||
}
|
||||
ModulationOut img_mod1 = img_mods[0];
|
||||
ModulationOut img_mod2 = img_mods[1];
|
||||
auto txt_mods = txt_mod->forward(ctx, vec);
|
||||
std::vector<ModulationOut> txt_mods;
|
||||
if (prune_mod) {
|
||||
txt_mods = get_distil_txt_mod(ctx, vec);
|
||||
} else {
|
||||
auto txt_mod = std::dynamic_pointer_cast<Modulation>(blocks["txt_mod"]);
|
||||
txt_mods = txt_mod->forward(ctx, vec);
|
||||
}
|
||||
ModulationOut txt_mod1 = txt_mods[0];
|
||||
ModulationOut txt_mod2 = txt_mods[1];
|
||||
|
||||
@@ -324,8 +318,8 @@ namespace Flux {
|
||||
auto k = ggml_concat(ctx, txt_k, img_k, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
auto v = ggml_concat(ctx, txt_v, img_v, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
|
||||
auto attn = attention(ctx, q, k, v, pe, flash_attn); // [N, n_txt_token + n_img_token, n_head*d_head]
|
||||
attn = ggml_cont(ctx, ggml_permute(ctx, attn, 0, 2, 1, 3)); // [n_txt_token + n_img_token, N, hidden_size]
|
||||
auto attn = Rope::attention(ctx, backend, q, k, v, pe, mask, flash_attn); // [N, n_txt_token + n_img_token, n_head*d_head]
|
||||
attn = ggml_cont(ctx, ggml_permute(ctx, attn, 0, 2, 1, 3)); // [n_txt_token + n_img_token, N, hidden_size]
|
||||
auto txt_attn_out = ggml_view_3d(ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
@@ -373,14 +367,18 @@ namespace Flux {
|
||||
int64_t hidden_size;
|
||||
int64_t mlp_hidden_dim;
|
||||
bool flash_attn;
|
||||
bool prune_mod;
|
||||
int idx = 0;
|
||||
|
||||
public:
|
||||
SingleStreamBlock(int64_t hidden_size,
|
||||
int64_t num_heads,
|
||||
float mlp_ratio = 4.0f,
|
||||
int idx = 0,
|
||||
float qk_scale = 0.f,
|
||||
bool flash_attn = false)
|
||||
: hidden_size(hidden_size), num_heads(num_heads), flash_attn(flash_attn) {
|
||||
bool flash_attn = false,
|
||||
bool prune_mod = false)
|
||||
: hidden_size(hidden_size), num_heads(num_heads), idx(idx), flash_attn(flash_attn), prune_mod(prune_mod) {
|
||||
int64_t head_dim = hidden_size / num_heads;
|
||||
float scale = qk_scale;
|
||||
if (scale <= 0.f) {
|
||||
@@ -393,26 +391,38 @@ namespace Flux {
|
||||
blocks["norm"] = std::shared_ptr<GGMLBlock>(new QKNorm(head_dim));
|
||||
blocks["pre_norm"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-6f, false));
|
||||
// mlp_act is nn.GELU(approximate="tanh")
|
||||
blocks["modulation"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, false));
|
||||
if (!prune_mod) {
|
||||
blocks["modulation"] = std::shared_ptr<GGMLBlock>(new Modulation(hidden_size, false));
|
||||
}
|
||||
}
|
||||
|
||||
ModulationOut get_distil_mod(struct ggml_context* ctx, struct ggml_tensor* vec) {
|
||||
int64_t offset = 3 * idx;
|
||||
return ModulationOut(ctx, vec, offset);
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* vec,
|
||||
struct ggml_tensor* pe) {
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* mask = NULL) {
|
||||
// x: [N, n_token, hidden_size]
|
||||
// pe: [n_token, d_head/2, 2, 2]
|
||||
// return: [N, n_token, hidden_size]
|
||||
|
||||
auto linear1 = std::dynamic_pointer_cast<Linear>(blocks["linear1"]);
|
||||
auto linear2 = std::dynamic_pointer_cast<Linear>(blocks["linear2"]);
|
||||
auto norm = std::dynamic_pointer_cast<QKNorm>(blocks["norm"]);
|
||||
auto pre_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["pre_norm"]);
|
||||
auto modulation = std::dynamic_pointer_cast<Modulation>(blocks["modulation"]);
|
||||
|
||||
auto mods = modulation->forward(ctx, vec);
|
||||
ModulationOut mod = mods[0];
|
||||
auto linear1 = std::dynamic_pointer_cast<Linear>(blocks["linear1"]);
|
||||
auto linear2 = std::dynamic_pointer_cast<Linear>(blocks["linear2"]);
|
||||
auto norm = std::dynamic_pointer_cast<QKNorm>(blocks["norm"]);
|
||||
auto pre_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["pre_norm"]);
|
||||
ModulationOut mod;
|
||||
if (prune_mod) {
|
||||
mod = get_distil_mod(ctx, vec);
|
||||
} else {
|
||||
auto modulation = std::dynamic_pointer_cast<Modulation>(blocks["modulation"]);
|
||||
|
||||
mod = modulation->forward(ctx, vec)[0];
|
||||
}
|
||||
auto x_mod = Flux::modulate(ctx, pre_norm->forward(ctx, x), mod.shift, mod.scale);
|
||||
auto qkv_mlp = linear1->forward(ctx, x_mod); // [N, n_token, hidden_size * 3 + mlp_hidden_dim]
|
||||
qkv_mlp = ggml_cont(ctx, ggml_permute(ctx, qkv_mlp, 2, 0, 1, 3)); // [hidden_size * 3 + mlp_hidden_dim, N, n_token]
|
||||
@@ -443,7 +453,7 @@ namespace Flux {
|
||||
auto v = ggml_reshape_4d(ctx, qkv_vec[2], head_dim, num_heads, qkv_vec[2]->ne[1], qkv_vec[2]->ne[2]); // [N, n_token, n_head, d_head]
|
||||
q = norm->query_norm(ctx, q);
|
||||
k = norm->key_norm(ctx, k);
|
||||
auto attn = attention(ctx, q, k, v, pe, flash_attn); // [N, n_token, hidden_size]
|
||||
auto attn = Rope::attention(ctx, backend, q, k, v, pe, mask, flash_attn); // [N, n_token, hidden_size]
|
||||
|
||||
auto attn_mlp = ggml_concat(ctx, attn, ggml_gelu_inplace(ctx, mlp), 0); // [N, n_token, hidden_size + mlp_hidden_dim]
|
||||
auto output = linear2->forward(ctx, attn_mlp); // [N, n_token, hidden_size]
|
||||
@@ -454,13 +464,28 @@ namespace Flux {
|
||||
};
|
||||
|
||||
struct LastLayer : public GGMLBlock {
|
||||
bool prune_mod;
|
||||
|
||||
public:
|
||||
LastLayer(int64_t hidden_size,
|
||||
int64_t patch_size,
|
||||
int64_t out_channels) {
|
||||
blocks["norm_final"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-06f, false));
|
||||
blocks["linear"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, patch_size * patch_size * out_channels));
|
||||
blocks["adaLN_modulation.1"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, 2 * hidden_size));
|
||||
int64_t out_channels,
|
||||
bool prune_mod = false)
|
||||
: prune_mod(prune_mod) {
|
||||
blocks["norm_final"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-06f, false));
|
||||
blocks["linear"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, patch_size * patch_size * out_channels));
|
||||
if (!prune_mod) {
|
||||
blocks["adaLN_modulation.1"] = std::shared_ptr<GGMLBlock>(new Linear(hidden_size, 2 * hidden_size));
|
||||
}
|
||||
}
|
||||
|
||||
ModulationOut get_distil_mod(struct ggml_context* ctx, struct ggml_tensor* vec) {
|
||||
int64_t offset = vec->ne[2] - 2;
|
||||
int64_t stride = vec->nb[1] * vec->ne[1];
|
||||
auto shift = ggml_view_2d(ctx, vec, vec->ne[0], vec->ne[1], vec->nb[1], stride * (offset + 0)); // [N, dim]
|
||||
auto scale = ggml_view_2d(ctx, vec, vec->ne[0], vec->ne[1], vec->nb[1], stride * (offset + 1)); // [N, dim]
|
||||
// No gate
|
||||
return ModulationOut(shift, scale, NULL);
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
@@ -469,17 +494,24 @@ namespace Flux {
|
||||
// x: [N, n_token, hidden_size]
|
||||
// c: [N, hidden_size]
|
||||
// return: [N, n_token, patch_size * patch_size * out_channels]
|
||||
auto norm_final = std::dynamic_pointer_cast<LayerNorm>(blocks["norm_final"]);
|
||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
||||
auto adaLN_modulation_1 = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.1"]);
|
||||
auto norm_final = std::dynamic_pointer_cast<LayerNorm>(blocks["norm_final"]);
|
||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
||||
struct ggml_tensor *shift, *scale;
|
||||
if (prune_mod) {
|
||||
auto mod = get_distil_mod(ctx, c);
|
||||
shift = mod.shift;
|
||||
scale = mod.scale;
|
||||
} else {
|
||||
auto adaLN_modulation_1 = std::dynamic_pointer_cast<Linear>(blocks["adaLN_modulation.1"]);
|
||||
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx, c)); // [N, 2 * hidden_size]
|
||||
m = ggml_reshape_3d(ctx, m, c->ne[0], 2, c->ne[1]); // [N, 2, hidden_size]
|
||||
m = ggml_cont(ctx, ggml_permute(ctx, m, 0, 2, 1, 3)); // [2, N, hidden_size]
|
||||
auto m = adaLN_modulation_1->forward(ctx, ggml_silu(ctx, c)); // [N, 2 * hidden_size]
|
||||
m = ggml_reshape_3d(ctx, m, c->ne[0], 2, c->ne[1]); // [N, 2, hidden_size]
|
||||
m = ggml_cont(ctx, ggml_permute(ctx, m, 0, 2, 1, 3)); // [2, N, hidden_size]
|
||||
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
auto shift = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, hidden_size]
|
||||
auto scale = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, hidden_size]
|
||||
int64_t offset = m->nb[1] * m->ne[1];
|
||||
shift = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 0); // [N, hidden_size]
|
||||
scale = ggml_view_2d(ctx, m, m->ne[0], m->ne[1], m->nb[1], offset * 1); // [N, hidden_size]
|
||||
}
|
||||
|
||||
x = Flux::modulate(ctx, norm_final->forward(ctx, x), shift, scale);
|
||||
x = linear->forward(ctx, x);
|
||||
@@ -488,6 +520,34 @@ namespace Flux {
|
||||
}
|
||||
};
|
||||
|
||||
struct ChromaApproximator : public GGMLBlock {
|
||||
int64_t inner_size = 5120;
|
||||
int64_t n_layers = 5;
|
||||
ChromaApproximator(int64_t in_channels = 64, int64_t hidden_size = 3072) {
|
||||
blocks["in_proj"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, inner_size, true));
|
||||
for (int i = 0; i < n_layers; i++) {
|
||||
blocks["norms." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new RMSNorm(inner_size));
|
||||
blocks["layers." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(inner_size, inner_size));
|
||||
}
|
||||
blocks["out_proj"] = std::shared_ptr<GGMLBlock>(new Linear(inner_size, hidden_size, true));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
auto in_proj = std::dynamic_pointer_cast<Linear>(blocks["in_proj"]);
|
||||
auto out_proj = std::dynamic_pointer_cast<Linear>(blocks["out_proj"]);
|
||||
|
||||
x = in_proj->forward(ctx, x);
|
||||
for (int i = 0; i < n_layers; i++) {
|
||||
auto norm = std::dynamic_pointer_cast<RMSNorm>(blocks["norms." + std::to_string(i)]);
|
||||
auto embed = std::dynamic_pointer_cast<MLPEmbedder>(blocks["layers." + std::to_string(i)]);
|
||||
x = ggml_add_inplace(ctx, x, embed->forward(ctx, norm->forward(ctx, x)));
|
||||
}
|
||||
x = out_proj->forward(ctx, x);
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
struct FluxParams {
|
||||
int64_t in_channels = 64;
|
||||
int64_t out_channels = 64;
|
||||
@@ -504,152 +564,25 @@ namespace Flux {
|
||||
bool qkv_bias = true;
|
||||
bool guidance_embed = true;
|
||||
bool flash_attn = true;
|
||||
bool is_chroma = false;
|
||||
SDVersion version = VERSION_FLUX;
|
||||
};
|
||||
|
||||
struct Flux : public GGMLBlock {
|
||||
public:
|
||||
std::vector<float> linspace(float start, float end, int num) {
|
||||
std::vector<float> result(num);
|
||||
float step = (end - start) / (num - 1);
|
||||
for (int i = 0; i < num; ++i) {
|
||||
result[i] = start + i * step;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> transpose(const std::vector<std::vector<float>>& mat) {
|
||||
int rows = mat.size();
|
||||
int cols = mat[0].size();
|
||||
std::vector<std::vector<float>> transposed(cols, std::vector<float>(rows));
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
transposed[j][i] = mat[i][j];
|
||||
}
|
||||
}
|
||||
return transposed;
|
||||
}
|
||||
|
||||
std::vector<float> flatten(const std::vector<std::vector<float>>& vec) {
|
||||
std::vector<float> flat_vec;
|
||||
for (const auto& sub_vec : vec) {
|
||||
flat_vec.insert(flat_vec.end(), sub_vec.begin(), sub_vec.end());
|
||||
}
|
||||
return flat_vec;
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> rope(const std::vector<float>& pos, int dim, int theta) {
|
||||
assert(dim % 2 == 0);
|
||||
int half_dim = dim / 2;
|
||||
|
||||
std::vector<float> scale = linspace(0, (dim * 1.0f - 2) / dim, half_dim);
|
||||
|
||||
std::vector<float> omega(half_dim);
|
||||
for (int i = 0; i < half_dim; ++i) {
|
||||
omega[i] = 1.0 / std::pow(theta, scale[i]);
|
||||
}
|
||||
|
||||
int pos_size = pos.size();
|
||||
std::vector<std::vector<float>> out(pos_size, std::vector<float>(half_dim));
|
||||
for (int i = 0; i < pos_size; ++i) {
|
||||
for (int j = 0; j < half_dim; ++j) {
|
||||
out[i][j] = pos[i] * omega[j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> result(pos_size, std::vector<float>(half_dim * 4));
|
||||
for (int i = 0; i < pos_size; ++i) {
|
||||
for (int j = 0; j < half_dim; ++j) {
|
||||
result[i][4 * j] = std::cos(out[i][j]);
|
||||
result[i][4 * j + 1] = -std::sin(out[i][j]);
|
||||
result[i][4 * j + 2] = std::sin(out[i][j]);
|
||||
result[i][4 * j + 3] = std::cos(out[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate IDs for image patches and text
|
||||
std::vector<std::vector<float>> gen_ids(int h, int w, int patch_size, int bs, int context_len) {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
|
||||
std::vector<std::vector<float>> img_ids(h_len * w_len, std::vector<float>(3, 0.0));
|
||||
|
||||
std::vector<float> row_ids = linspace(0, h_len - 1, h_len);
|
||||
std::vector<float> col_ids = linspace(0, w_len - 1, w_len);
|
||||
|
||||
for (int i = 0; i < h_len; ++i) {
|
||||
for (int j = 0; j < w_len; ++j) {
|
||||
img_ids[i * w_len + j][1] = row_ids[i];
|
||||
img_ids[i * w_len + j][2] = col_ids[j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> img_ids_repeated(bs * img_ids.size(), std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < img_ids.size(); ++j) {
|
||||
img_ids_repeated[i * img_ids.size() + j] = img_ids[j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> txt_ids(bs * context_len, std::vector<float>(3, 0.0));
|
||||
std::vector<std::vector<float>> ids(bs * (context_len + img_ids.size()), std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < context_len; ++j) {
|
||||
ids[i * (context_len + img_ids.size()) + j] = txt_ids[j];
|
||||
}
|
||||
for (int j = 0; j < img_ids.size(); ++j) {
|
||||
ids[i * (context_len + img_ids.size()) + context_len + j] = img_ids_repeated[i * img_ids.size() + j];
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Generate positional embeddings
|
||||
std::vector<float> gen_pe(int h, int w, int patch_size, int bs, int context_len, int theta, const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_ids(h, w, patch_size, bs, context_len);
|
||||
std::vector<std::vector<float>> trans_ids = transpose(ids);
|
||||
size_t pos_len = ids.size();
|
||||
int num_axes = axes_dim.size();
|
||||
for (int i = 0; i < pos_len; i++) {
|
||||
// std::cout << trans_ids[0][i] << " " << trans_ids[1][i] << " " << trans_ids[2][i] << std::endl;
|
||||
}
|
||||
|
||||
int emb_dim = 0;
|
||||
for (int d : axes_dim)
|
||||
emb_dim += d / 2;
|
||||
|
||||
std::vector<std::vector<float>> emb(bs * pos_len, std::vector<float>(emb_dim * 2 * 2, 0.0));
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_axes; ++i) {
|
||||
std::vector<std::vector<float>> rope_emb = rope(trans_ids[i], axes_dim[i], theta); // [bs*pos_len, axes_dim[i]/2 * 2 * 2]
|
||||
for (int b = 0; b < bs; ++b) {
|
||||
for (int j = 0; j < pos_len; ++j) {
|
||||
for (int k = 0; k < rope_emb[0].size(); ++k) {
|
||||
emb[b * pos_len + j][offset + k] = rope_emb[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += rope_emb[0].size();
|
||||
}
|
||||
|
||||
return flatten(emb);
|
||||
}
|
||||
|
||||
public:
|
||||
FluxParams params;
|
||||
Flux() {}
|
||||
Flux(FluxParams params)
|
||||
: params(params) {
|
||||
int64_t pe_dim = params.hidden_size / params.num_heads;
|
||||
|
||||
blocks["img_in"] = std::shared_ptr<GGMLBlock>(new Linear(params.in_channels, params.hidden_size, true));
|
||||
blocks["time_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(256, params.hidden_size));
|
||||
blocks["vector_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(params.vec_in_dim, params.hidden_size));
|
||||
if (params.guidance_embed) {
|
||||
blocks["guidance_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(256, params.hidden_size));
|
||||
blocks["img_in"] = std::shared_ptr<GGMLBlock>(new Linear(params.in_channels, params.hidden_size, true));
|
||||
if (params.is_chroma) {
|
||||
blocks["distilled_guidance_layer"] = std::shared_ptr<GGMLBlock>(new ChromaApproximator(params.in_channels, params.hidden_size));
|
||||
} else {
|
||||
blocks["time_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(256, params.hidden_size));
|
||||
blocks["vector_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(params.vec_in_dim, params.hidden_size));
|
||||
if (params.guidance_embed) {
|
||||
blocks["guidance_in"] = std::shared_ptr<GGMLBlock>(new MLPEmbedder(256, params.hidden_size));
|
||||
}
|
||||
}
|
||||
blocks["txt_in"] = std::shared_ptr<GGMLBlock>(new Linear(params.context_in_dim, params.hidden_size, true));
|
||||
|
||||
@@ -657,19 +590,23 @@ namespace Flux {
|
||||
blocks["double_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new DoubleStreamBlock(params.hidden_size,
|
||||
params.num_heads,
|
||||
params.mlp_ratio,
|
||||
i,
|
||||
params.qkv_bias,
|
||||
params.flash_attn));
|
||||
params.flash_attn,
|
||||
params.is_chroma));
|
||||
}
|
||||
|
||||
for (int i = 0; i < params.depth_single_blocks; i++) {
|
||||
blocks["single_blocks." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new SingleStreamBlock(params.hidden_size,
|
||||
params.num_heads,
|
||||
params.mlp_ratio,
|
||||
i,
|
||||
0.f,
|
||||
params.flash_attn));
|
||||
params.flash_attn,
|
||||
params.is_chroma));
|
||||
}
|
||||
|
||||
blocks["final_layer"] = std::shared_ptr<GGMLBlock>(new LastLayer(params.hidden_size, 1, params.out_channels));
|
||||
blocks["final_layer"] = std::shared_ptr<GGMLBlock>(new LastLayer(params.hidden_size, 1, params.out_channels, params.is_chroma));
|
||||
}
|
||||
|
||||
struct ggml_tensor* patchify(struct ggml_context* ctx,
|
||||
@@ -720,31 +657,62 @@ namespace Flux {
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward_orig(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* img,
|
||||
struct ggml_tensor* txt,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
struct ggml_tensor* pe,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
struct ggml_tensor* mod_index_arange = NULL,
|
||||
std::vector<int> skip_layers = {}) {
|
||||
auto img_in = std::dynamic_pointer_cast<Linear>(blocks["img_in"]);
|
||||
auto time_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["time_in"]);
|
||||
auto vector_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["vector_in"]);
|
||||
auto txt_in = std::dynamic_pointer_cast<Linear>(blocks["txt_in"]);
|
||||
auto final_layer = std::dynamic_pointer_cast<LastLayer>(blocks["final_layer"]);
|
||||
|
||||
img = img_in->forward(ctx, img);
|
||||
auto vec = time_in->forward(ctx, ggml_nn_timestep_embedding(ctx, timesteps, 256, 10000, 1000.f));
|
||||
img = img_in->forward(ctx, img);
|
||||
struct ggml_tensor* vec;
|
||||
struct ggml_tensor* txt_img_mask = NULL;
|
||||
if (params.is_chroma) {
|
||||
int64_t mod_index_length = 344;
|
||||
auto approx = std::dynamic_pointer_cast<ChromaApproximator>(blocks["distilled_guidance_layer"]);
|
||||
auto distill_timestep = ggml_nn_timestep_embedding(ctx, timesteps, 16, 10000, 1000.f);
|
||||
auto distill_guidance = ggml_nn_timestep_embedding(ctx, guidance, 16, 10000, 1000.f);
|
||||
|
||||
if (params.guidance_embed) {
|
||||
GGML_ASSERT(guidance != NULL);
|
||||
auto guidance_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["guidance_in"]);
|
||||
// bf16 and fp16 result is different
|
||||
auto g_in = ggml_nn_timestep_embedding(ctx, guidance, 256, 10000, 1000.f);
|
||||
vec = ggml_add(ctx, vec, guidance_in->forward(ctx, g_in));
|
||||
// auto mod_index_arange = ggml_arange(ctx, 0, (float)mod_index_length, 1);
|
||||
// ggml_arange tot working on a lot of backends, precomputing it on CPU instead
|
||||
GGML_ASSERT(arange != NULL);
|
||||
auto modulation_index = ggml_nn_timestep_embedding(ctx, mod_index_arange, 32, 10000, 1000.f); // [1, 344, 32]
|
||||
|
||||
// Batch broadcast (will it ever be useful)
|
||||
modulation_index = ggml_repeat(ctx, modulation_index, ggml_new_tensor_3d(ctx, GGML_TYPE_F32, modulation_index->ne[0], modulation_index->ne[1], img->ne[2])); // [N, 344, 32]
|
||||
|
||||
auto timestep_guidance = ggml_concat(ctx, distill_timestep, distill_guidance, 0); // [N, 1, 32]
|
||||
timestep_guidance = ggml_repeat(ctx, timestep_guidance, modulation_index); // [N, 344, 32]
|
||||
|
||||
vec = ggml_concat(ctx, timestep_guidance, modulation_index, 0); // [N, 344, 64]
|
||||
// Permute for consistency with non-distilled modulation implementation
|
||||
vec = ggml_cont(ctx, ggml_permute(ctx, vec, 0, 2, 1, 3)); // [344, N, 64]
|
||||
vec = approx->forward(ctx, vec); // [344, N, hidden_size]
|
||||
|
||||
if (y != NULL) {
|
||||
txt_img_mask = ggml_pad(ctx, y, img->ne[1], 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
auto time_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["time_in"]);
|
||||
auto vector_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["vector_in"]);
|
||||
vec = time_in->forward(ctx, ggml_nn_timestep_embedding(ctx, timesteps, 256, 10000, 1000.f));
|
||||
if (params.guidance_embed) {
|
||||
GGML_ASSERT(guidance != NULL);
|
||||
auto guidance_in = std::dynamic_pointer_cast<MLPEmbedder>(blocks["guidance_in"]);
|
||||
// bf16 and fp16 result is different
|
||||
auto g_in = ggml_nn_timestep_embedding(ctx, guidance, 256, 10000, 1000.f);
|
||||
vec = ggml_add(ctx, vec, guidance_in->forward(ctx, g_in));
|
||||
}
|
||||
|
||||
vec = ggml_add(ctx, vec, vector_in->forward(ctx, y));
|
||||
}
|
||||
|
||||
vec = ggml_add(ctx, vec, vector_in->forward(ctx, y));
|
||||
txt = txt_in->forward(ctx, txt);
|
||||
|
||||
for (int i = 0; i < params.depth; i++) {
|
||||
@@ -754,7 +722,7 @@ namespace Flux {
|
||||
|
||||
auto block = std::dynamic_pointer_cast<DoubleStreamBlock>(blocks["double_blocks." + std::to_string(i)]);
|
||||
|
||||
auto img_txt = block->forward(ctx, img, txt, vec, pe);
|
||||
auto img_txt = block->forward(ctx, backend, img, txt, vec, pe, txt_img_mask);
|
||||
img = img_txt.first; // [N, n_img_token, hidden_size]
|
||||
txt = img_txt.second; // [N, n_txt_token, hidden_size]
|
||||
}
|
||||
@@ -766,7 +734,7 @@ namespace Flux {
|
||||
}
|
||||
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 = block->forward(ctx, backend, txt_img, vec, pe, txt_img_mask);
|
||||
}
|
||||
|
||||
txt_img = ggml_cont(ctx, ggml_permute(ctx, txt_img, 0, 2, 1, 3)); // [n_txt_token + n_img_token, N, hidden_size]
|
||||
@@ -781,11 +749,25 @@ namespace Flux {
|
||||
img = ggml_cont(ctx, ggml_permute(ctx, img, 0, 2, 1, 3)); // [N, n_img_token, hidden_size]
|
||||
|
||||
img = final_layer->forward(ctx, img, vec); // (N, T, patch_size ** 2 * out_channels)
|
||||
return img;
|
||||
}
|
||||
|
||||
struct ggml_tensor* process_img(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t patch_size = 2;
|
||||
int pad_h = (patch_size - H % patch_size) % patch_size;
|
||||
int pad_w = (patch_size - W % patch_size) % patch_size;
|
||||
x = ggml_pad(ctx, x, pad_w, pad_h, 0, 0); // [N, C, H + pad_h, W + pad_w]
|
||||
|
||||
// img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
|
||||
auto img = patchify(ctx, x, patch_size); // [N, h*w, C * patch_size * patch_size]
|
||||
return img;
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timestep,
|
||||
struct ggml_tensor* context,
|
||||
@@ -793,7 +775,9 @@ namespace Flux {
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
struct ggml_tensor* pe,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
struct ggml_tensor* mod_index_arange = NULL,
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
std::vector<int> skip_layers = {}) {
|
||||
// Forward pass of DiT.
|
||||
// x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images)
|
||||
// timestep: (N,) tensor of diffusion timesteps
|
||||
@@ -812,25 +796,56 @@ namespace Flux {
|
||||
int64_t patch_size = 2;
|
||||
int pad_h = (patch_size - H % patch_size) % patch_size;
|
||||
int pad_w = (patch_size - W % patch_size) % patch_size;
|
||||
x = ggml_pad(ctx, x, pad_w, pad_h, 0, 0); // [N, C, H + pad_h, W + pad_w]
|
||||
|
||||
// img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
|
||||
auto img = patchify(ctx, x, patch_size); // [N, h*w, C * patch_size * patch_size]
|
||||
auto img = process_img(ctx, x);
|
||||
uint64_t img_tokens = img->ne[1];
|
||||
|
||||
if (c_concat != NULL) {
|
||||
if (params.version == VERSION_FLUX_FILL) {
|
||||
GGML_ASSERT(c_concat != NULL);
|
||||
ggml_tensor* masked = ggml_view_4d(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, 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);
|
||||
|
||||
masked = ggml_pad(ctx, masked, pad_w, pad_h, 0, 0);
|
||||
mask = ggml_pad(ctx, mask, pad_w, pad_h, 0, 0);
|
||||
|
||||
masked = patchify(ctx, masked, patch_size);
|
||||
mask = patchify(ctx, mask, patch_size);
|
||||
masked = process_img(ctx, masked);
|
||||
mask = process_img(ctx, mask);
|
||||
|
||||
img = ggml_concat(ctx, img, ggml_concat(ctx, masked, mask, 0), 0);
|
||||
} else if (params.version == VERSION_FLEX_2) {
|
||||
GGML_ASSERT(c_concat != NULL);
|
||||
ggml_tensor* masked = ggml_view_4d(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, 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* control = ggml_view_4d(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], c_concat->nb[2] * (C + 1));
|
||||
|
||||
masked = ggml_pad(ctx, masked, pad_w, pad_h, 0, 0);
|
||||
mask = ggml_pad(ctx, mask, pad_w, pad_h, 0, 0);
|
||||
control = ggml_pad(ctx, control, pad_w, pad_h, 0, 0);
|
||||
|
||||
masked = patchify(ctx, masked, patch_size);
|
||||
mask = patchify(ctx, mask, patch_size);
|
||||
control = patchify(ctx, control, patch_size);
|
||||
|
||||
img = ggml_concat(ctx, img, ggml_concat(ctx, ggml_concat(ctx, masked, mask, 0), control, 0), 0);
|
||||
} else if (params.version == VERSION_FLUX_CONTROLS) {
|
||||
GGML_ASSERT(c_concat != NULL);
|
||||
|
||||
ggml_tensor* control = ggml_pad(ctx, c_concat, pad_w, pad_h, 0, 0);
|
||||
control = patchify(ctx, control, patch_size);
|
||||
img = ggml_concat(ctx, img, control, 0);
|
||||
}
|
||||
|
||||
auto out = forward_orig(ctx, img, context, timestep, y, guidance, pe, skip_layers); // [N, h*w, C * patch_size * patch_size]
|
||||
if (ref_latents.size() > 0) {
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
ref = process_img(ctx, ref);
|
||||
img = ggml_concat(ctx, img, ref, 1);
|
||||
}
|
||||
}
|
||||
|
||||
auto out = forward_orig(ctx, backend, img, context, timestep, y, guidance, pe, mod_index_arange, skip_layers); // [N, num_tokens, C * patch_size * patch_size]
|
||||
|
||||
if (out->ne[1] > img_tokens) {
|
||||
out = ggml_cont(ctx, ggml_permute(ctx, out, 0, 2, 1, 3)); // [num_tokens, N, C * patch_size * patch_size]
|
||||
out = ggml_view_3d(ctx, out, out->ne[0], out->ne[1], img_tokens, out->nb[1], out->nb[2], 0);
|
||||
out = ggml_cont(ctx, ggml_permute(ctx, out, 0, 2, 1, 3)); // [N, h*w, C * patch_size * patch_size]
|
||||
}
|
||||
|
||||
// rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)
|
||||
out = unpatchify(ctx, out, (H + pad_h) / patch_size, (W + pad_w) / patch_size, patch_size); // [N, C, H + pad_h, W + pad_w]
|
||||
@@ -840,34 +855,46 @@ namespace Flux {
|
||||
};
|
||||
|
||||
struct FluxRunner : public GGMLRunner {
|
||||
static std::map<std::string, enum ggml_type> empty_tensor_types;
|
||||
|
||||
public:
|
||||
FluxParams flux_params;
|
||||
Flux flux;
|
||||
std::vector<float> pe_vec; // for cache
|
||||
std::vector<float> pe_vec;
|
||||
std::vector<float> mod_index_arange_vec; // for cache
|
||||
SDVersion version;
|
||||
bool use_mask = false;
|
||||
|
||||
FluxRunner(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types = empty_tensor_types,
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool flash_attn = false)
|
||||
: GGMLRunner(backend) {
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_FLUX,
|
||||
bool flash_attn = false,
|
||||
bool use_mask = false)
|
||||
: GGMLRunner(backend, offload_params_to_cpu), version(version), use_mask(use_mask) {
|
||||
flux_params.version = version;
|
||||
flux_params.flash_attn = flash_attn;
|
||||
flux_params.guidance_embed = false;
|
||||
flux_params.depth = 0;
|
||||
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;
|
||||
}
|
||||
for (auto pair : tensor_types) {
|
||||
std::string tensor_name = pair.first;
|
||||
if (tensor_name.find("model.diffusion_model.") == std::string::npos)
|
||||
if (!starts_with(tensor_name, prefix))
|
||||
continue;
|
||||
if (tensor_name.find("guidance_in.in_layer.weight") != std::string::npos) {
|
||||
// not schnell
|
||||
flux_params.guidance_embed = true;
|
||||
}
|
||||
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
|
||||
@@ -887,7 +914,9 @@ namespace Flux {
|
||||
}
|
||||
|
||||
LOG_INFO("Flux blocks: %d double, %d single", flux_params.depth, flux_params.depth_single_blocks);
|
||||
if (!flux_params.guidance_embed) {
|
||||
if (flux_params.is_chroma) {
|
||||
LOG_INFO("Using pruned modulation (Chroma)");
|
||||
} else if (!flux_params.guidance_embed) {
|
||||
LOG_INFO("Flux guidance is disabled (Schnell mode)");
|
||||
}
|
||||
|
||||
@@ -909,22 +938,50 @@ namespace Flux {
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
bool increase_ref_index = false,
|
||||
std::vector<int> skip_layers = {}) {
|
||||
GGML_ASSERT(x->ne[3] == 1);
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, FLUX_GRAPH_SIZE, false);
|
||||
|
||||
struct ggml_tensor* mod_index_arange = NULL;
|
||||
|
||||
x = to_backend(x);
|
||||
context = to_backend(context);
|
||||
if (c_concat != NULL) {
|
||||
c_concat = to_backend(c_concat);
|
||||
}
|
||||
y = to_backend(y);
|
||||
if (flux_params.is_chroma) {
|
||||
guidance = ggml_set_f32(guidance, 0);
|
||||
|
||||
if (!use_mask) {
|
||||
y = NULL;
|
||||
}
|
||||
|
||||
// ggml_arange is not working on some backends, precompute it
|
||||
mod_index_arange_vec = arange(0, 344);
|
||||
mod_index_arange = ggml_new_tensor_1d(compute_ctx, GGML_TYPE_F32, mod_index_arange_vec.size());
|
||||
set_backend_tensor_data(mod_index_arange, mod_index_arange_vec.data());
|
||||
}
|
||||
y = to_backend(y);
|
||||
|
||||
timesteps = to_backend(timesteps);
|
||||
if (flux_params.guidance_embed) {
|
||||
if (flux_params.guidance_embed || flux_params.is_chroma) {
|
||||
guidance = to_backend(guidance);
|
||||
}
|
||||
for (int i = 0; i < ref_latents.size(); i++) {
|
||||
ref_latents[i] = to_backend(ref_latents[i]);
|
||||
}
|
||||
|
||||
pe_vec = flux.gen_pe(x->ne[1], x->ne[0], 2, x->ne[3], context->ne[1], flux_params.theta, flux_params.axes_dim);
|
||||
pe_vec = Rope::gen_flux_pe(x->ne[1],
|
||||
x->ne[0],
|
||||
2,
|
||||
x->ne[3],
|
||||
context->ne[1],
|
||||
ref_latents,
|
||||
increase_ref_index,
|
||||
flux_params.theta,
|
||||
flux_params.axes_dim);
|
||||
int pos_len = pe_vec.size() / flux_params.axes_dim_sum / 2;
|
||||
// LOG_DEBUG("pos_len %d", pos_len);
|
||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, flux_params.axes_dim_sum / 2, pos_len);
|
||||
@@ -934,6 +991,7 @@ namespace Flux {
|
||||
set_backend_tensor_data(pe, pe_vec.data());
|
||||
|
||||
struct ggml_tensor* out = flux.forward(compute_ctx,
|
||||
runtime_backend,
|
||||
x,
|
||||
timesteps,
|
||||
context,
|
||||
@@ -941,6 +999,8 @@ namespace Flux {
|
||||
y,
|
||||
guidance,
|
||||
pe,
|
||||
mod_index_arange,
|
||||
ref_latents,
|
||||
skip_layers);
|
||||
|
||||
ggml_build_forward_expand(gf, out);
|
||||
@@ -955,16 +1015,18 @@ namespace Flux {
|
||||
struct ggml_tensor* c_concat,
|
||||
struct ggml_tensor* y,
|
||||
struct ggml_tensor* guidance,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
bool increase_ref_index = false,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL,
|
||||
std::vector<int> skip_layers = std::vector<int>()) {
|
||||
// x: [N, in_channels, h, w]
|
||||
// timesteps: [N, ]
|
||||
// context: [N, max_position, hidden_size]
|
||||
// y: [N, adm_in_channels] or [1, adm_in_channels]
|
||||
// guidance: [N, ]
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, c_concat, y, guidance, skip_layers);
|
||||
return build_graph(x, timesteps, context, c_concat, y, guidance, ref_latents, increase_ref_index, skip_layers);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
@@ -1004,7 +1066,7 @@ namespace Flux {
|
||||
struct ggml_tensor* out = NULL;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
compute(8, x, timesteps, context, NULL, y, guidance, &out, work_ctx);
|
||||
compute(8, x, timesteps, context, NULL, y, guidance, {}, false, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
@@ -1016,7 +1078,7 @@ namespace Flux {
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_Q8_0;
|
||||
std::shared_ptr<FluxRunner> flux = std::shared_ptr<FluxRunner>(new FluxRunner(backend));
|
||||
std::shared_ptr<FluxRunner> flux = std::shared_ptr<FluxRunner>(new FluxRunner(backend, false));
|
||||
{
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
|
||||
@@ -1030,7 +1092,7 @@ namespace Flux {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors, backend);
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
|
||||
+5
-2
@@ -1,2 +1,5 @@
|
||||
clang-format -style=file -i *.cpp *.h *.hpp
|
||||
clang-format -style=file -i examples/cli/*.cpp
|
||||
for f in *.cpp *.h *.hpp examples/cli/*.cpp examples/cli/*.h; do
|
||||
[[ "$f" == vocab* ]] && continue
|
||||
echo "formatting '$f'"
|
||||
clang-format -style=file -i "$f"
|
||||
done
|
||||
+1
-1
Submodule ggml updated: 6fcbd60bc7...c538174d26
+1128
-315
File diff suppressed because it is too large
Load Diff
+231
@@ -0,0 +1,231 @@
|
||||
#ifndef __GGUF_READER_HPP__
|
||||
#define __GGUF_READER_HPP__
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ggml.h"
|
||||
#include "util.h"
|
||||
|
||||
struct GGUFTensorInfo {
|
||||
std::string name;
|
||||
ggml_type type;
|
||||
std::vector<int64_t> shape;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
enum class GGUFMetadataType : uint32_t {
|
||||
UINT8 = 0,
|
||||
INT8 = 1,
|
||||
UINT16 = 2,
|
||||
INT16 = 3,
|
||||
UINT32 = 4,
|
||||
INT32 = 5,
|
||||
FLOAT32 = 6,
|
||||
BOOL = 7,
|
||||
STRING = 8,
|
||||
ARRAY = 9,
|
||||
UINT64 = 10,
|
||||
INT64 = 11,
|
||||
FLOAT64 = 12,
|
||||
};
|
||||
|
||||
class GGUFReader {
|
||||
private:
|
||||
std::vector<GGUFTensorInfo> tensors_;
|
||||
size_t data_offset_;
|
||||
size_t alignment_ = 32; // default alignment is 32
|
||||
|
||||
template <typename T>
|
||||
bool safe_read(std::ifstream& fin, T& value) {
|
||||
fin.read(reinterpret_cast<char*>(&value), sizeof(T));
|
||||
return fin.good();
|
||||
}
|
||||
|
||||
bool safe_read(std::ifstream& fin, char* buffer, size_t size) {
|
||||
fin.read(buffer, size);
|
||||
return fin.good();
|
||||
}
|
||||
|
||||
bool safe_seek(std::ifstream& fin, std::streamoff offset, std::ios::seekdir dir) {
|
||||
fin.seekg(offset, dir);
|
||||
return fin.good();
|
||||
}
|
||||
|
||||
bool read_metadata(std::ifstream& fin) {
|
||||
uint64_t key_len = 0;
|
||||
if (!safe_read(fin, key_len))
|
||||
return false;
|
||||
|
||||
std::string key(key_len, '\0');
|
||||
if (!safe_read(fin, (char*)key.data(), key_len))
|
||||
return false;
|
||||
|
||||
uint32_t type = 0;
|
||||
if (!safe_read(fin, type))
|
||||
return false;
|
||||
|
||||
if (key == "general.alignment") {
|
||||
uint32_t align_val = 0;
|
||||
if (!safe_read(fin, align_val))
|
||||
return false;
|
||||
|
||||
if (align_val != 0 && (align_val & (align_val - 1)) == 0) {
|
||||
alignment_ = align_val;
|
||||
LOG_DEBUG("Found alignment: %zu", alignment_);
|
||||
} else {
|
||||
LOG_ERROR("Invalid alignment value %u, fallback to default %zu", align_val, alignment_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (static_cast<GGUFMetadataType>(type)) {
|
||||
case GGUFMetadataType::UINT8:
|
||||
case GGUFMetadataType::INT8:
|
||||
case GGUFMetadataType::BOOL:
|
||||
return safe_seek(fin, 1, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT16:
|
||||
case GGUFMetadataType::INT16:
|
||||
return safe_seek(fin, 2, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT32:
|
||||
case GGUFMetadataType::INT32:
|
||||
case GGUFMetadataType::FLOAT32:
|
||||
return safe_seek(fin, 4, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT64:
|
||||
case GGUFMetadataType::INT64:
|
||||
case GGUFMetadataType::FLOAT64:
|
||||
return safe_seek(fin, 8, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::STRING: {
|
||||
uint64_t len = 0;
|
||||
if (!safe_read(fin, len))
|
||||
return false;
|
||||
return safe_seek(fin, len, std::ios::cur);
|
||||
}
|
||||
|
||||
case GGUFMetadataType::ARRAY: {
|
||||
uint32_t elem_type = 0;
|
||||
uint64_t len = 0;
|
||||
if (!safe_read(fin, elem_type))
|
||||
return false;
|
||||
if (!safe_read(fin, len))
|
||||
return false;
|
||||
|
||||
for (uint64_t i = 0; i < len; i++) {
|
||||
if (!read_metadata(fin))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR("Unknown metadata type=%u", type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GGUFTensorInfo read_tensor_info(std::ifstream& fin) {
|
||||
GGUFTensorInfo info;
|
||||
|
||||
uint64_t name_len;
|
||||
if (!safe_read(fin, name_len))
|
||||
throw std::runtime_error("read tensor name length failed");
|
||||
|
||||
info.name.resize(name_len);
|
||||
if (!safe_read(fin, (char*)info.name.data(), name_len))
|
||||
throw std::runtime_error("read tensor name failed");
|
||||
|
||||
uint32_t n_dims;
|
||||
if (!safe_read(fin, n_dims))
|
||||
throw std::runtime_error("read tensor dims failed");
|
||||
|
||||
info.shape.resize(n_dims);
|
||||
for (uint32_t i = 0; i < n_dims; i++) {
|
||||
if (!safe_read(fin, info.shape[i]))
|
||||
throw std::runtime_error("read tensor shape failed");
|
||||
}
|
||||
|
||||
if (n_dims > GGML_MAX_DIMS) {
|
||||
for (int i = GGML_MAX_DIMS; i < n_dims; i++) {
|
||||
info.shape[GGML_MAX_DIMS - 1] *= info.shape[i]; // stack to last dim;
|
||||
}
|
||||
info.shape.resize(GGML_MAX_DIMS);
|
||||
n_dims = GGML_MAX_DIMS;
|
||||
}
|
||||
|
||||
uint32_t type;
|
||||
if (!safe_read(fin, type))
|
||||
throw std::runtime_error("read tensor type failed");
|
||||
info.type = static_cast<ggml_type>(type);
|
||||
|
||||
if (!safe_read(fin, info.offset))
|
||||
throw std::runtime_error("read tensor offset failed");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public:
|
||||
bool load(const std::string& file_path) {
|
||||
std::ifstream fin(file_path, std::ios::binary);
|
||||
if (!fin) {
|
||||
LOG_ERROR("failed to open '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Header ---
|
||||
char magic[4];
|
||||
if (!safe_read(fin, magic, 4) || strncmp(magic, "GGUF", 4) != 0) {
|
||||
LOG_ERROR("not a valid GGUF file");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
if (!safe_read(fin, version))
|
||||
return false;
|
||||
|
||||
uint64_t tensor_count, metadata_kv_count;
|
||||
if (!safe_read(fin, tensor_count))
|
||||
return false;
|
||||
if (!safe_read(fin, metadata_kv_count))
|
||||
return false;
|
||||
|
||||
LOG_DEBUG("GGUF v%u, tensor_count=%llu, metadata_kv_count=%llu",
|
||||
version, (unsigned long long)tensor_count, (unsigned long long)metadata_kv_count);
|
||||
|
||||
// --- Read Metadata ---
|
||||
for (uint64_t i = 0; i < metadata_kv_count; i++) {
|
||||
if (!read_metadata(fin)) {
|
||||
LOG_ERROR("read meta data failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tensor Infos ---
|
||||
tensors_.clear();
|
||||
try {
|
||||
for (uint64_t i = 0; i < tensor_count; i++) {
|
||||
tensors_.push_back(read_tensor_info(fin));
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
LOG_ERROR("%s", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
data_offset_ = static_cast<size_t>(fin.tellg());
|
||||
if ((data_offset_ % alignment_) != 0) {
|
||||
data_offset_ = ((data_offset_ + alignment_ - 1) / alignment_) * alignment_;
|
||||
}
|
||||
fin.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<GGUFTensorInfo>& tensors() const { return tensors_; }
|
||||
size_t data_offset() const { return data_offset_; }
|
||||
};
|
||||
|
||||
#endif // __GGUF_READER_HPP__
|
||||
+15
-15
@@ -329,21 +329,21 @@ const std::vector<std::vector<float>> GITS_NOISE_1_50 = {
|
||||
};
|
||||
|
||||
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 }
|
||||
&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
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef __LTXV_HPP__
|
||||
#define __LTXV_HPP__
|
||||
|
||||
#include "common.hpp"
|
||||
#include "ggml_extend.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> 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));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
struct 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
|
||||
@@ -142,43 +142,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class RMSNorm : public UnaryBlock {
|
||||
protected:
|
||||
int64_t hidden_size;
|
||||
float eps;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, std::string prefix = "") {
|
||||
enum ggml_type wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "weight") != tensor_types.end()) ? tensor_types[prefix + "weight"] : GGML_TYPE_F32;
|
||||
params["weight"] = ggml_new_tensor_1d(ctx, wtype, hidden_size);
|
||||
}
|
||||
|
||||
public:
|
||||
RMSNorm(int64_t hidden_size,
|
||||
float eps = 1e-06f)
|
||||
: hidden_size(hidden_size),
|
||||
eps(eps) {}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
struct ggml_tensor* w = params["weight"];
|
||||
x = ggml_rms_norm(ctx, x, eps);
|
||||
x = ggml_mul(ctx, x, w);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class SelfAttention : public GGMLBlock {
|
||||
public:
|
||||
int64_t num_heads;
|
||||
bool pre_only;
|
||||
std::string qk_norm;
|
||||
bool flash_attn;
|
||||
|
||||
public:
|
||||
SelfAttention(int64_t dim,
|
||||
int64_t num_heads = 8,
|
||||
std::string qk_norm = "",
|
||||
bool qkv_bias = false,
|
||||
bool pre_only = false)
|
||||
: num_heads(num_heads), pre_only(pre_only), qk_norm(qk_norm) {
|
||||
bool pre_only = false,
|
||||
bool flash_attn = false)
|
||||
: num_heads(num_heads), pre_only(pre_only), qk_norm(qk_norm), flash_attn(flash_attn) {
|
||||
int64_t d_head = dim / num_heads;
|
||||
blocks["qkv"] = std::shared_ptr<GGMLBlock>(new Linear(dim, dim * 3, qkv_bias));
|
||||
if (!pre_only) {
|
||||
@@ -226,10 +204,12 @@ public:
|
||||
}
|
||||
|
||||
// x: [N, n_token, dim]
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x) {
|
||||
auto qkv = pre_attention(ctx, x);
|
||||
x = ggml_nn_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
x = ggml_nn_attention_ext(ctx, backend, qkv[0], qkv[1], qkv[2], num_heads, NULL, false, false, true); // [N, n_token, dim]
|
||||
x = post_attention(ctx, x); // [N, n_token, dim]
|
||||
return x;
|
||||
}
|
||||
};
|
||||
@@ -254,6 +234,7 @@ public:
|
||||
int64_t num_heads;
|
||||
bool pre_only;
|
||||
bool self_attn;
|
||||
bool flash_attn;
|
||||
|
||||
public:
|
||||
DismantledBlock(int64_t hidden_size,
|
||||
@@ -262,16 +243,17 @@ public:
|
||||
std::string qk_norm = "",
|
||||
bool qkv_bias = false,
|
||||
bool pre_only = false,
|
||||
bool self_attn = false)
|
||||
bool self_attn = false,
|
||||
bool flash_attn = false)
|
||||
: num_heads(num_heads), pre_only(pre_only), self_attn(self_attn) {
|
||||
// rmsnorm is always Flase
|
||||
// scale_mod_only is always Flase
|
||||
// swiglu is always Flase
|
||||
blocks["norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(hidden_size, 1e-06f, false));
|
||||
blocks["attn"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qk_norm, qkv_bias, pre_only));
|
||||
blocks["attn"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qk_norm, qkv_bias, pre_only, flash_attn));
|
||||
|
||||
if (self_attn) {
|
||||
blocks["attn2"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qk_norm, qkv_bias, false));
|
||||
blocks["attn2"] = std::shared_ptr<GGMLBlock>(new SelfAttention(hidden_size, num_heads, qk_norm, qkv_bias, false, flash_attn));
|
||||
}
|
||||
|
||||
if (!pre_only) {
|
||||
@@ -439,7 +421,10 @@ public:
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x, struct ggml_tensor* c) {
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* c) {
|
||||
// x: [N, n_token, hidden_size]
|
||||
// c: [N, hidden_size]
|
||||
// return: [N, n_token, hidden_size]
|
||||
@@ -454,8 +439,8 @@ public:
|
||||
auto qkv2 = std::get<1>(qkv_intermediates);
|
||||
auto intermediates = std::get<2>(qkv_intermediates);
|
||||
|
||||
auto attn_out = ggml_nn_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads); // [N, n_token, dim]
|
||||
auto attn2_out = ggml_nn_attention_ext(ctx, qkv2[0], qkv2[1], qkv2[2], num_heads); // [N, n_token, dim]
|
||||
auto attn_out = ggml_nn_attention_ext(ctx, backend, qkv[0], qkv[1], qkv[2], num_heads, NULL, false, false, flash_attn); // [N, n_token, dim]
|
||||
auto attn2_out = ggml_nn_attention_ext(ctx, backend, qkv2[0], qkv2[1], qkv2[2], num_heads, NULL, false, false, flash_attn); // [N, n_token, dim]
|
||||
x = post_attention_x(ctx,
|
||||
attn_out,
|
||||
attn2_out,
|
||||
@@ -471,7 +456,7 @@ public:
|
||||
auto qkv = qkv_intermediates.first;
|
||||
auto intermediates = qkv_intermediates.second;
|
||||
|
||||
auto attn_out = ggml_nn_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads); // [N, n_token, dim]
|
||||
auto attn_out = ggml_nn_attention_ext(ctx, backend, qkv[0], qkv[1], qkv[2], num_heads, NULL, false, false, flash_attn); // [N, n_token, dim]
|
||||
x = post_attention(ctx,
|
||||
attn_out,
|
||||
intermediates[0],
|
||||
@@ -486,6 +471,8 @@ public:
|
||||
|
||||
__STATIC_INLINE__ std::pair<struct ggml_tensor*, struct ggml_tensor*>
|
||||
block_mixing(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
bool flash_attn,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* c,
|
||||
@@ -515,8 +502,8 @@ block_mixing(struct ggml_context* ctx,
|
||||
qkv.push_back(ggml_concat(ctx, context_qkv[i], x_qkv[i], 1));
|
||||
}
|
||||
|
||||
auto attn = ggml_nn_attention_ext(ctx, qkv[0], qkv[1], qkv[2], x_block->num_heads); // [N, n_context + n_token, hidden_size]
|
||||
attn = ggml_cont(ctx, ggml_permute(ctx, attn, 0, 2, 1, 3)); // [n_context + n_token, N, hidden_size]
|
||||
auto attn = ggml_nn_attention_ext(ctx, backend, qkv[0], qkv[1], qkv[2], x_block->num_heads, NULL, false, false, flash_attn); // [N, n_context + n_token, hidden_size]
|
||||
attn = ggml_cont(ctx, ggml_permute(ctx, attn, 0, 2, 1, 3)); // [n_context + n_token, N, hidden_size]
|
||||
auto context_attn = ggml_view_3d(ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
@@ -549,7 +536,7 @@ block_mixing(struct ggml_context* ctx,
|
||||
}
|
||||
|
||||
if (x_block->self_attn) {
|
||||
auto attn2 = ggml_nn_attention_ext(ctx, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads); // [N, n_token, hidden_size]
|
||||
auto attn2 = ggml_nn_attention_ext(ctx, backend, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads); // [N, n_token, hidden_size]
|
||||
|
||||
x = x_block->post_attention_x(ctx,
|
||||
x_attn,
|
||||
@@ -574,6 +561,8 @@ block_mixing(struct ggml_context* ctx,
|
||||
}
|
||||
|
||||
struct JointBlock : public GGMLBlock {
|
||||
bool flash_attn;
|
||||
|
||||
public:
|
||||
JointBlock(int64_t hidden_size,
|
||||
int64_t num_heads,
|
||||
@@ -581,19 +570,22 @@ public:
|
||||
std::string qk_norm = "",
|
||||
bool qkv_bias = false,
|
||||
bool pre_only = false,
|
||||
bool self_attn_x = false) {
|
||||
blocks["context_block"] = std::shared_ptr<GGMLBlock>(new DismantledBlock(hidden_size, num_heads, mlp_ratio, qk_norm, qkv_bias, pre_only));
|
||||
blocks["x_block"] = std::shared_ptr<GGMLBlock>(new DismantledBlock(hidden_size, num_heads, mlp_ratio, qk_norm, qkv_bias, false, self_attn_x));
|
||||
bool self_attn_x = false,
|
||||
bool flash_attn = false)
|
||||
: flash_attn(flash_attn) {
|
||||
blocks["context_block"] = std::shared_ptr<GGMLBlock>(new DismantledBlock(hidden_size, num_heads, mlp_ratio, qk_norm, qkv_bias, pre_only, false, flash_attn));
|
||||
blocks["x_block"] = std::shared_ptr<GGMLBlock>(new DismantledBlock(hidden_size, num_heads, mlp_ratio, qk_norm, qkv_bias, false, self_attn_x, flash_attn));
|
||||
}
|
||||
|
||||
std::pair<struct ggml_tensor*, struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* c) {
|
||||
auto context_block = std::dynamic_pointer_cast<DismantledBlock>(blocks["context_block"]);
|
||||
auto x_block = std::dynamic_pointer_cast<DismantledBlock>(blocks["x_block"]);
|
||||
|
||||
return block_mixing(ctx, context, x, c, context_block, x_block);
|
||||
return block_mixing(ctx, backend, flash_attn, context, x, c, context_block, x_block);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -651,14 +643,16 @@ protected:
|
||||
int64_t context_embedder_out_dim = 1536;
|
||||
int64_t hidden_size;
|
||||
std::string qk_norm;
|
||||
bool flash_attn = false;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, std::string prefix = "") {
|
||||
enum ggml_type wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "pos_embed") != tensor_types.end()) ? tensor_types[prefix + "pos_embed"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, std::string prefix = "") {
|
||||
enum ggml_type wtype = GGML_TYPE_F32;
|
||||
params["pos_embed"] = ggml_new_tensor_3d(ctx, wtype, hidden_size, num_patchs, 1);
|
||||
}
|
||||
|
||||
public:
|
||||
MMDiT(std::map<std::string, enum ggml_type>& tensor_types) {
|
||||
MMDiT(bool flash_attn = false, const String2GGMLType& tensor_types = {})
|
||||
: flash_attn(flash_attn) {
|
||||
// input_size is always None
|
||||
// learn_sigma is always False
|
||||
// register_length is alwalys 0
|
||||
@@ -726,7 +720,8 @@ public:
|
||||
qk_norm,
|
||||
true,
|
||||
i == depth - 1,
|
||||
i <= d_self));
|
||||
i <= d_self,
|
||||
flash_attn));
|
||||
}
|
||||
|
||||
blocks["final_layer"] = std::shared_ptr<GGMLBlock>(new FinalLayer(hidden_size, patch_size, out_channels));
|
||||
@@ -795,6 +790,7 @@ public:
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward_core_with_concat(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* c_mod,
|
||||
struct ggml_tensor* context,
|
||||
@@ -813,7 +809,7 @@ public:
|
||||
|
||||
auto block = std::dynamic_pointer_cast<JointBlock>(blocks["joint_blocks." + std::to_string(i)]);
|
||||
|
||||
auto context_x = block->forward(ctx, context, x, c_mod);
|
||||
auto context_x = block->forward(ctx, backend, context, x, c_mod);
|
||||
context = context_x.first;
|
||||
x = context_x.second;
|
||||
}
|
||||
@@ -824,6 +820,7 @@ public:
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* t,
|
||||
struct ggml_tensor* y = NULL,
|
||||
@@ -859,7 +856,7 @@ public:
|
||||
context = context_embedder->forward(ctx, context); // [N, L, D] aka [N, L, 1536]
|
||||
}
|
||||
|
||||
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, backend, x, c, context, skip_layers); // (N, H*W, patch_size ** 2 * out_channels)
|
||||
|
||||
x = unpatchify(ctx, x, h, w); // [N, C, H, W]
|
||||
|
||||
@@ -869,12 +866,12 @@ public:
|
||||
struct MMDiTRunner : public GGMLRunner {
|
||||
MMDiT mmdit;
|
||||
|
||||
static std::map<std::string, enum ggml_type> empty_tensor_types;
|
||||
|
||||
MMDiTRunner(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types = empty_tensor_types,
|
||||
const std::string prefix = "")
|
||||
: GGMLRunner(backend), mmdit(tensor_types) {
|
||||
bool offload_params_to_cpu,
|
||||
bool flash_attn,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "")
|
||||
: GGMLRunner(backend, offload_params_to_cpu), mmdit(flash_attn, tensor_types) {
|
||||
mmdit.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
@@ -899,6 +896,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
timesteps = to_backend(timesteps);
|
||||
|
||||
struct ggml_tensor* out = mmdit.forward(compute_ctx,
|
||||
runtime_backend,
|
||||
x,
|
||||
timesteps,
|
||||
y,
|
||||
@@ -972,7 +970,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
std::shared_ptr<MMDiTRunner> mmdit = std::shared_ptr<MMDiTRunner>(new MMDiTRunner(backend));
|
||||
std::shared_ptr<MMDiTRunner> mmdit = std::shared_ptr<MMDiTRunner>(new MMDiTRunner(backend, false, false));
|
||||
{
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
|
||||
@@ -986,7 +984,7 @@ struct MMDiTRunner : public GGMLRunner {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors, backend);
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "ggml-backend.h"
|
||||
#include "ggml.h"
|
||||
#include "gguf.h"
|
||||
#include "json.hpp"
|
||||
#include "zip.h"
|
||||
|
||||
@@ -20,33 +21,27 @@
|
||||
enum SDVersion {
|
||||
VERSION_SD1,
|
||||
VERSION_SD1_INPAINT,
|
||||
VERSION_SD1_PIX2PIX,
|
||||
VERSION_SD2,
|
||||
VERSION_SD2_INPAINT,
|
||||
VERSION_SDXL,
|
||||
VERSION_SDXL_INPAINT,
|
||||
VERSION_SDXL_PIX2PIX,
|
||||
VERSION_SVD,
|
||||
VERSION_SD3,
|
||||
VERSION_FLUX,
|
||||
VERSION_FLUX_FILL,
|
||||
VERSION_FLUX_CONTROLS,
|
||||
VERSION_FLEX_2,
|
||||
VERSION_WAN2,
|
||||
VERSION_WAN2_2_I2V,
|
||||
VERSION_WAN2_2_TI2V,
|
||||
VERSION_QWEN_IMAGE,
|
||||
VERSION_COUNT,
|
||||
};
|
||||
|
||||
static inline bool sd_version_is_flux(SDVersion version) {
|
||||
if (version == VERSION_FLUX || version == VERSION_FLUX_FILL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_sd3(SDVersion version) {
|
||||
if (version == VERSION_SD3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_sd1(SDVersion version) {
|
||||
if (version == VERSION_SD1 || version == VERSION_SD1_INPAINT) {
|
||||
if (version == VERSION_SD1 || version == VERSION_SD1_INPAINT || version == VERSION_SD1_PIX2PIX) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -60,26 +55,69 @@ static inline bool sd_version_is_sd2(SDVersion version) {
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_sdxl(SDVersion version) {
|
||||
if (version == VERSION_SDXL || version == VERSION_SDXL_INPAINT) {
|
||||
if (version == VERSION_SDXL || version == VERSION_SDXL_INPAINT || version == VERSION_SDXL_PIX2PIX) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_sd3(SDVersion version) {
|
||||
if (version == VERSION_SD3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_flux(SDVersion version) {
|
||||
if (version == VERSION_FLUX || version == VERSION_FLUX_FILL || version == VERSION_FLUX_CONTROLS || version == VERSION_FLEX_2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_wan(SDVersion version) {
|
||||
if (version == VERSION_WAN2 || version == VERSION_WAN2_2_I2V || version == VERSION_WAN2_2_TI2V) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_qwen_image(SDVersion version) {
|
||||
if (version == VERSION_QWEN_IMAGE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_inpaint(SDVersion version) {
|
||||
if (version == VERSION_SD1_INPAINT || version == VERSION_SD2_INPAINT || version == VERSION_SDXL_INPAINT || version == VERSION_FLUX_FILL) {
|
||||
if (version == VERSION_SD1_INPAINT || version == VERSION_SD2_INPAINT || version == VERSION_SDXL_INPAINT || version == VERSION_FLUX_FILL || version == VERSION_FLEX_2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_dit(SDVersion version) {
|
||||
if (sd_version_is_flux(version) || sd_version_is_sd3(version)) {
|
||||
if (sd_version_is_flux(version) ||
|
||||
sd_version_is_sd3(version) ||
|
||||
sd_version_is_wan(version) ||
|
||||
sd_version_is_qwen_image(version)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_unet_edit(SDVersion version) {
|
||||
return version == VERSION_SD1_PIX2PIX || version == VERSION_SDXL_PIX2PIX;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_control(SDVersion version) {
|
||||
return version == VERSION_FLUX_CONTROLS || version == VERSION_FLEX_2;
|
||||
}
|
||||
|
||||
static bool sd_version_is_inpaint_or_unet_edit(SDVersion version) {
|
||||
return sd_version_is_unet_edit(version) || sd_version_is_inpaint(version) || sd_version_is_control(version);
|
||||
}
|
||||
|
||||
enum PMVersion {
|
||||
PM_VERSION_1,
|
||||
PM_VERSION_2,
|
||||
@@ -91,16 +129,18 @@ struct TensorStorage {
|
||||
bool is_bf16 = false;
|
||||
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
|
||||
size_t offset = 0; // offset in file
|
||||
uint64_t offset = 0; // offset in file
|
||||
|
||||
TensorStorage() = default;
|
||||
|
||||
TensorStorage(const std::string& name, ggml_type type, int64_t* ne, int n_dims, size_t file_index, size_t offset = 0)
|
||||
TensorStorage(const std::string& name, ggml_type type, const int64_t* ne, int n_dims, size_t file_index, size_t offset = 0)
|
||||
: name(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];
|
||||
@@ -122,6 +162,8 @@ struct TensorStorage {
|
||||
int64_t nbytes_to_read() const {
|
||||
if (is_bf16 || is_f8_e4m3 || is_f8_e5m2) {
|
||||
return nbytes() / 2;
|
||||
} else if (is_f64 || is_i64) {
|
||||
return nbytes() * 2;
|
||||
} else {
|
||||
return nbytes();
|
||||
}
|
||||
@@ -139,10 +181,10 @@ struct TensorStorage {
|
||||
|
||||
std::vector<TensorStorage> chunk(size_t n) {
|
||||
std::vector<TensorStorage> chunks;
|
||||
size_t chunk_size = nbytes_to_read() / n;
|
||||
uint64_t chunk_size = nbytes_to_read() / n;
|
||||
// printf("%d/%d\n", chunk_size, nbytes_to_read());
|
||||
reverse_ne();
|
||||
for (int i = 0; i < n; i++) {
|
||||
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;
|
||||
@@ -172,6 +214,10 @@ struct TensorStorage {
|
||||
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 << " [";
|
||||
@@ -188,6 +234,8 @@ struct TensorStorage {
|
||||
|
||||
typedef std::function<bool(const TensorStorage&, ggml_tensor**)> on_new_tensor_cb_t;
|
||||
|
||||
typedef std::map<std::string, enum ggml_type> String2GGMLType;
|
||||
|
||||
class ModelLoader {
|
||||
protected:
|
||||
std::vector<std::string> file_paths_;
|
||||
@@ -206,27 +254,38 @@ protected:
|
||||
bool init_from_diffusers_file(const std::string& file_path, const std::string& prefix = "");
|
||||
|
||||
public:
|
||||
std::map<std::string, enum ggml_type> tensor_storages_types;
|
||||
String2GGMLType tensor_storages_types;
|
||||
|
||||
bool init_from_file(const std::string& file_path, const std::string& prefix = "");
|
||||
bool model_is_unet();
|
||||
SDVersion get_sd_version();
|
||||
ggml_type get_sd_wtype();
|
||||
ggml_type get_conditioner_wtype();
|
||||
ggml_type get_diffusion_model_wtype();
|
||||
ggml_type get_vae_wtype();
|
||||
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();
|
||||
void set_wtype_override(ggml_type wtype, std::string prefix = "");
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, ggml_backend_t backend);
|
||||
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0);
|
||||
bool load_tensors(std::map<std::string, struct ggml_tensor*>& tensors,
|
||||
ggml_backend_t backend,
|
||||
std::set<std::string> ignore_tensors = {});
|
||||
std::set<std::string> ignore_tensors = {},
|
||||
int n_threads = 0);
|
||||
|
||||
bool save_to_gguf_file(const std::string& file_path, ggml_type type);
|
||||
std::vector<std::string> get_tensor_names() const {
|
||||
std::vector<std::string> names;
|
||||
for (const auto& ts : tensor_storages) {
|
||||
names.push_back(ts.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;
|
||||
|
||||
static std::string load_merges();
|
||||
static std::string load_qwen2_merges();
|
||||
static std::string load_t5_tokenizer_json();
|
||||
static std::string load_umt5_tokenizer_json();
|
||||
};
|
||||
|
||||
#endif // __MODEL_H__
|
||||
|
||||
@@ -42,41 +42,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
class QFormerPerceiver(nn.Module):
|
||||
def __init__(self, id_embeddings_dim, cross_attention_dim, num_tokens, embedding_dim=1024, use_residual=True, ratio=4):
|
||||
super().__init__()
|
||||
|
||||
self.num_tokens = num_tokens
|
||||
self.cross_attention_dim = cross_attention_dim
|
||||
self.use_residual = use_residual
|
||||
print(cross_attention_dim*num_tokens)
|
||||
self.token_proj = nn.Sequential(
|
||||
nn.Linear(id_embeddings_dim, id_embeddings_dim*ratio),
|
||||
nn.GELU(),
|
||||
nn.Linear(id_embeddings_dim*ratio, cross_attention_dim*num_tokens),
|
||||
)
|
||||
self.token_norm = nn.LayerNorm(cross_attention_dim)
|
||||
self.perceiver_resampler = FacePerceiverResampler(
|
||||
dim=cross_attention_dim,
|
||||
depth=4,
|
||||
dim_head=128,
|
||||
heads=cross_attention_dim // 128,
|
||||
embedding_dim=embedding_dim,
|
||||
output_dim=cross_attention_dim,
|
||||
ff_mult=4,
|
||||
)
|
||||
|
||||
def forward(self, x, last_hidden_state):
|
||||
x = self.token_proj(x)
|
||||
x = x.reshape(-1, self.num_tokens, self.cross_attention_dim)
|
||||
x = self.token_norm(x) # cls token
|
||||
out = self.perceiver_resampler(x, last_hidden_state) # retrieve from patch tokens
|
||||
if self.use_residual: # TODO: if use_residual is not true
|
||||
out = x + 1.0 * out
|
||||
return out
|
||||
*/
|
||||
|
||||
struct PMFeedForward : public GGMLBlock {
|
||||
// network hparams
|
||||
int dim;
|
||||
@@ -122,17 +87,8 @@ public:
|
||||
int64_t ne[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ne[i] = x->ne[i];
|
||||
// print_ggml_tensor(x, true, "PerceiverAttention reshape x 0: ");
|
||||
// printf("heads = %d \n", heads);
|
||||
// x = ggml_view_4d(ctx, x, x->ne[0], x->ne[1], heads, x->ne[2]/heads,
|
||||
// x->nb[1], x->nb[2], x->nb[3], 0);
|
||||
x = ggml_reshape_4d(ctx, x, x->ne[0] / heads, heads, x->ne[1], x->ne[2]);
|
||||
// x = ggml_view_4d(ctx, x, x->ne[0]/heads, heads, x->ne[1], x->ne[2],
|
||||
// x->nb[1], x->nb[2], x->nb[3], 0);
|
||||
// x = ggml_cont(ctx, x);
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3));
|
||||
// print_ggml_tensor(x, true, "PerceiverAttention reshape x 1: ");
|
||||
// x = ggml_reshape_4d(ctx, x, ne[0], heads, ne[1], ne[2]/heads);
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -269,17 +225,6 @@ public:
|
||||
4));
|
||||
}
|
||||
|
||||
/*
|
||||
def forward(self, x, last_hidden_state):
|
||||
x = self.token_proj(x)
|
||||
x = x.reshape(-1, self.num_tokens, self.cross_attention_dim)
|
||||
x = self.token_norm(x) # cls token
|
||||
out = self.perceiver_resampler(x, last_hidden_state) # retrieve from patch tokens
|
||||
if self.use_residual: # TODO: if use_residual is not true
|
||||
out = x + 1.0 * out
|
||||
return out
|
||||
*/
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* last_hidden_state) {
|
||||
@@ -299,113 +244,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
class FacePerceiverResampler(torch.nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
dim=768,
|
||||
depth=4,
|
||||
dim_head=64,
|
||||
heads=16,
|
||||
embedding_dim=1280,
|
||||
output_dim=768,
|
||||
ff_mult=4,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.proj_in = torch.nn.Linear(embedding_dim, dim)
|
||||
self.proj_out = torch.nn.Linear(dim, output_dim)
|
||||
self.norm_out = torch.nn.LayerNorm(output_dim)
|
||||
self.layers = torch.nn.ModuleList([])
|
||||
for _ in range(depth):
|
||||
self.layers.append(
|
||||
torch.nn.ModuleList(
|
||||
[
|
||||
PerceiverAttention(dim=dim, dim_head=dim_head, heads=heads),
|
||||
FeedForward(dim=dim, mult=ff_mult),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
def forward(self, latents, x):
|
||||
x = self.proj_in(x)
|
||||
for attn, ff in self.layers:
|
||||
latents = attn(x, latents) + latents
|
||||
latents = ff(latents) + latents
|
||||
latents = self.proj_out(latents)
|
||||
return self.norm_out(latents)
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
def FeedForward(dim, mult=4):
|
||||
inner_dim = int(dim * mult)
|
||||
return nn.Sequential(
|
||||
nn.LayerNorm(dim),
|
||||
nn.Linear(dim, inner_dim, bias=False),
|
||||
nn.GELU(),
|
||||
nn.Linear(inner_dim, dim, bias=False),
|
||||
)
|
||||
|
||||
def reshape_tensor(x, heads):
|
||||
bs, length, width = x.shape
|
||||
# (bs, length, width) --> (bs, length, n_heads, dim_per_head)
|
||||
x = x.view(bs, length, heads, -1)
|
||||
# (bs, length, n_heads, dim_per_head) --> (bs, n_heads, length, dim_per_head)
|
||||
x = x.transpose(1, 2)
|
||||
# (bs, n_heads, length, dim_per_head) --> (bs*n_heads, length, dim_per_head)
|
||||
x = x.reshape(bs, heads, length, -1)
|
||||
return x
|
||||
|
||||
class PerceiverAttention(nn.Module):
|
||||
def __init__(self, *, dim, dim_head=64, heads=8):
|
||||
super().__init__()
|
||||
self.scale = dim_head**-0.5
|
||||
self.dim_head = dim_head
|
||||
self.heads = heads
|
||||
inner_dim = dim_head * heads
|
||||
|
||||
self.norm1 = nn.LayerNorm(dim)
|
||||
self.norm2 = nn.LayerNorm(dim)
|
||||
|
||||
self.to_q = nn.Linear(dim, inner_dim, bias=False)
|
||||
self.to_kv = nn.Linear(dim, inner_dim * 2, bias=False)
|
||||
self.to_out = nn.Linear(inner_dim, dim, bias=False)
|
||||
|
||||
def forward(self, x, latents):
|
||||
"""
|
||||
Args:
|
||||
x (torch.Tensor): image features
|
||||
shape (b, n1, D)
|
||||
latent (torch.Tensor): latent features
|
||||
shape (b, n2, D)
|
||||
"""
|
||||
x = self.norm1(x)
|
||||
latents = self.norm2(latents)
|
||||
|
||||
b, l, _ = latents.shape
|
||||
|
||||
q = self.to_q(latents)
|
||||
kv_input = torch.cat((x, latents), dim=-2)
|
||||
k, v = self.to_kv(kv_input).chunk(2, dim=-1)
|
||||
|
||||
q = reshape_tensor(q, self.heads)
|
||||
k = reshape_tensor(k, self.heads)
|
||||
v = reshape_tensor(v, self.heads)
|
||||
|
||||
# attention
|
||||
scale = 1 / math.sqrt(math.sqrt(self.dim_head))
|
||||
weight = (q * scale) @ (k * scale).transpose(-2, -1) # More stable with f16 than dividing afterwards
|
||||
weight = torch.softmax(weight.float(), dim=-1).type(weight.dtype)
|
||||
out = weight @ v
|
||||
|
||||
out = out.permute(0, 2, 1, 3).reshape(b, l, -1)
|
||||
|
||||
return self.to_out(out)
|
||||
|
||||
*/
|
||||
|
||||
struct FuseModule : public GGMLBlock {
|
||||
// network hparams
|
||||
int embed_dim;
|
||||
@@ -425,31 +263,13 @@ public:
|
||||
auto mlp2 = std::dynamic_pointer_cast<FuseBlock>(blocks["mlp2"]);
|
||||
auto layer_norm = std::dynamic_pointer_cast<LayerNorm>(blocks["layer_norm"]);
|
||||
|
||||
// print_ggml_tensor(id_embeds, true, "Fuseblock id_embeds: ");
|
||||
// print_ggml_tensor(prompt_embeds, true, "Fuseblock prompt_embeds: ");
|
||||
|
||||
// auto prompt_embeds0 = ggml_cont(ctx, ggml_permute(ctx, prompt_embeds, 2, 0, 1, 3));
|
||||
// auto id_embeds0 = ggml_cont(ctx, ggml_permute(ctx, id_embeds, 2, 0, 1, 3));
|
||||
// print_ggml_tensor(id_embeds0, true, "Fuseblock id_embeds0: ");
|
||||
// print_ggml_tensor(prompt_embeds0, true, "Fuseblock prompt_embeds0: ");
|
||||
// concat is along dim 2
|
||||
// auto stacked_id_embeds = ggml_concat(ctx, prompt_embeds0, id_embeds0, 2);
|
||||
auto stacked_id_embeds = ggml_concat(ctx, prompt_embeds, id_embeds, 0);
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "Fuseblock stacked_id_embeds 0: ");
|
||||
// stacked_id_embeds = ggml_cont(ctx, ggml_permute(ctx, stacked_id_embeds, 1, 2, 0, 3));
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "Fuseblock stacked_id_embeds 1: ");
|
||||
// stacked_id_embeds = mlp1.forward(ctx, stacked_id_embeds);
|
||||
// stacked_id_embeds = ggml_add(ctx, stacked_id_embeds, prompt_embeds);
|
||||
// stacked_id_embeds = mlp2.forward(ctx, stacked_id_embeds);
|
||||
// stacked_id_embeds = ggml_nn_layer_norm(ctx, stacked_id_embeds, ln_w, ln_b);
|
||||
|
||||
stacked_id_embeds = mlp1->forward(ctx, stacked_id_embeds);
|
||||
stacked_id_embeds = ggml_add(ctx, stacked_id_embeds, prompt_embeds);
|
||||
stacked_id_embeds = mlp2->forward(ctx, stacked_id_embeds);
|
||||
stacked_id_embeds = layer_norm->forward(ctx, stacked_id_embeds);
|
||||
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "Fuseblock stacked_id_embeds 1: ");
|
||||
|
||||
return stacked_id_embeds;
|
||||
}
|
||||
|
||||
@@ -464,21 +284,14 @@ public:
|
||||
|
||||
struct ggml_tensor* valid_id_embeds = id_embeds;
|
||||
// # slice out the image token embeddings
|
||||
// print_ggml_tensor(class_tokens_mask_pos, false);
|
||||
ggml_set_name(class_tokens_mask_pos, "class_tokens_mask_pos");
|
||||
ggml_set_name(prompt_embeds, "prompt_embeds");
|
||||
// print_ggml_tensor(valid_id_embeds, true, "valid_id_embeds");
|
||||
// print_ggml_tensor(class_tokens_mask_pos, true, "class_tokens_mask_pos");
|
||||
struct ggml_tensor* image_token_embeds = ggml_get_rows(ctx, prompt_embeds, class_tokens_mask_pos);
|
||||
ggml_set_name(image_token_embeds, "image_token_embeds");
|
||||
valid_id_embeds = ggml_reshape_2d(ctx, valid_id_embeds, valid_id_embeds->ne[0],
|
||||
ggml_nelements(valid_id_embeds) / valid_id_embeds->ne[0]);
|
||||
struct ggml_tensor* stacked_id_embeds = fuse_fn(ctx, image_token_embeds, valid_id_embeds);
|
||||
|
||||
// stacked_id_embeds = ggml_cont(ctx, ggml_permute(ctx, stacked_id_embeds, 0, 2, 1, 3));
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "AA stacked_id_embeds");
|
||||
// print_ggml_tensor(left, true, "AA left");
|
||||
// print_ggml_tensor(right, true, "AA right");
|
||||
if (left && right) {
|
||||
stacked_id_embeds = ggml_concat(ctx, left, stacked_id_embeds, 1);
|
||||
stacked_id_embeds = ggml_concat(ctx, stacked_id_embeds, right, 1);
|
||||
@@ -487,15 +300,12 @@ public:
|
||||
} else if (right) {
|
||||
stacked_id_embeds = ggml_concat(ctx, stacked_id_embeds, right, 1);
|
||||
}
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "BB stacked_id_embeds");
|
||||
// stacked_id_embeds = ggml_cont(ctx, ggml_permute(ctx, stacked_id_embeds, 0, 2, 1, 3));
|
||||
// print_ggml_tensor(stacked_id_embeds, true, "CC stacked_id_embeds");
|
||||
|
||||
class_tokens_mask = ggml_cont(ctx, ggml_transpose(ctx, class_tokens_mask));
|
||||
class_tokens_mask = ggml_repeat(ctx, class_tokens_mask, prompt_embeds);
|
||||
prompt_embeds = ggml_mul(ctx, prompt_embeds, class_tokens_mask);
|
||||
struct ggml_tensor* updated_prompt_embeds = ggml_add(ctx, prompt_embeds, stacked_id_embeds);
|
||||
ggml_set_name(updated_prompt_embeds, "updated_prompt_embeds");
|
||||
// print_ggml_tensor(updated_prompt_embeds, true, "updated_prompt_embeds: ");
|
||||
return updated_prompt_embeds;
|
||||
}
|
||||
};
|
||||
@@ -508,6 +318,7 @@ struct PhotoMakerIDEncoderBlock : public CLIPVisionModelProjection {
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* id_pixel_values,
|
||||
struct ggml_tensor* prompt_embeds,
|
||||
struct ggml_tensor* class_tokens_mask,
|
||||
@@ -520,9 +331,9 @@ struct PhotoMakerIDEncoderBlock : public CLIPVisionModelProjection {
|
||||
auto visual_projection_2 = std::dynamic_pointer_cast<Linear>(blocks["visual_projection_2"]);
|
||||
auto fuse_module = std::dynamic_pointer_cast<FuseModule>(blocks["fuse_module"]);
|
||||
|
||||
struct ggml_tensor* shared_id_embeds = vision_model->forward(ctx, id_pixel_values); // [N, hidden_size]
|
||||
struct ggml_tensor* id_embeds = visual_projection->forward(ctx, shared_id_embeds); // [N, proj_dim(768)]
|
||||
struct ggml_tensor* id_embeds_2 = visual_projection_2->forward(ctx, shared_id_embeds); // [N, 1280]
|
||||
struct ggml_tensor* shared_id_embeds = vision_model->forward(ctx, backend, id_pixel_values); // [N, hidden_size]
|
||||
struct ggml_tensor* id_embeds = visual_projection->forward(ctx, shared_id_embeds); // [N, proj_dim(768)]
|
||||
struct ggml_tensor* id_embeds_2 = visual_projection_2->forward(ctx, shared_id_embeds); // [N, 1280]
|
||||
|
||||
id_embeds = ggml_cont(ctx, ggml_permute(ctx, id_embeds, 2, 0, 1, 3));
|
||||
id_embeds_2 = ggml_cont(ctx, ggml_permute(ctx, id_embeds_2, 2, 0, 1, 3));
|
||||
@@ -550,35 +361,13 @@ struct PhotoMakerIDEncoder_CLIPInsightfaceExtendtokenBlock : public CLIPVisionMo
|
||||
num_tokens(2) {
|
||||
blocks["visual_projection_2"] = std::shared_ptr<GGMLBlock>(new Linear(1024, 1280, false));
|
||||
blocks["fuse_module"] = std::shared_ptr<GGMLBlock>(new FuseModule(2048));
|
||||
/*
|
||||
cross_attention_dim = 2048
|
||||
# projection
|
||||
self.num_tokens = 2
|
||||
self.cross_attention_dim = cross_attention_dim
|
||||
self.qformer_perceiver = QFormerPerceiver(
|
||||
id_embeddings_dim,
|
||||
cross_attention_dim,
|
||||
self.num_tokens,
|
||||
)*/
|
||||
blocks["qformer_perceiver"] = std::shared_ptr<GGMLBlock>(new QFormerPerceiver(id_embeddings_dim,
|
||||
cross_attention_dim,
|
||||
num_tokens));
|
||||
blocks["qformer_perceiver"] = std::shared_ptr<GGMLBlock>(new QFormerPerceiver(id_embeddings_dim,
|
||||
cross_attention_dim,
|
||||
num_tokens));
|
||||
}
|
||||
|
||||
/*
|
||||
def forward(self, id_pixel_values, prompt_embeds, class_tokens_mask, id_embeds):
|
||||
b, num_inputs, c, h, w = id_pixel_values.shape
|
||||
id_pixel_values = id_pixel_values.view(b * num_inputs, c, h, w)
|
||||
|
||||
last_hidden_state = self.vision_model(id_pixel_values)[0]
|
||||
id_embeds = id_embeds.view(b * num_inputs, -1)
|
||||
|
||||
id_embeds = self.qformer_perceiver(id_embeds, last_hidden_state)
|
||||
id_embeds = id_embeds.view(b, num_inputs, self.num_tokens, -1)
|
||||
updated_prompt_embeds = self.fuse_module(prompt_embeds, id_embeds, class_tokens_mask)
|
||||
*/
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* id_pixel_values,
|
||||
struct ggml_tensor* prompt_embeds,
|
||||
struct ggml_tensor* class_tokens_mask,
|
||||
@@ -592,7 +381,7 @@ struct PhotoMakerIDEncoder_CLIPInsightfaceExtendtokenBlock : public CLIPVisionMo
|
||||
auto qformer_perceiver = std::dynamic_pointer_cast<QFormerPerceiver>(blocks["qformer_perceiver"]);
|
||||
|
||||
// struct ggml_tensor* last_hidden_state = vision_model->forward(ctx, id_pixel_values); // [N, hidden_size]
|
||||
struct ggml_tensor* last_hidden_state = vision_model->forward(ctx, id_pixel_values, false); // [N, hidden_size]
|
||||
struct ggml_tensor* last_hidden_state = vision_model->forward(ctx, backend, id_pixel_values, false); // [N, hidden_size]
|
||||
id_embeds = qformer_perceiver->forward(ctx, id_embeds, last_hidden_state);
|
||||
|
||||
struct ggml_tensor* updated_prompt_embeds = fuse_module->forward(ctx,
|
||||
@@ -623,8 +412,14 @@ public:
|
||||
std::vector<float> zeros_right;
|
||||
|
||||
public:
|
||||
PhotoMakerIDEncoder(ggml_backend_t backend, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix, SDVersion version = VERSION_SDXL, PMVersion pm_v = PM_VERSION_1, float sty = 20.f)
|
||||
: GGMLRunner(backend),
|
||||
PhotoMakerIDEncoder(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
SDVersion version = VERSION_SDXL,
|
||||
PMVersion pm_v = PM_VERSION_1,
|
||||
float sty = 20.f)
|
||||
: GGMLRunner(backend, offload_params_to_cpu),
|
||||
version(version),
|
||||
pm_version(pm_v),
|
||||
style_strength(sty) {
|
||||
@@ -736,6 +531,7 @@ public:
|
||||
struct ggml_tensor* updated_prompt_embeds = NULL;
|
||||
if (pm_version == PM_VERSION_1)
|
||||
updated_prompt_embeds = id_encoder.forward(ctx0,
|
||||
runtime_backend,
|
||||
id_pixel_values_d,
|
||||
prompt_embeds_d,
|
||||
class_tokens_mask_d,
|
||||
@@ -743,6 +539,7 @@ public:
|
||||
left, right);
|
||||
else if (pm_version == PM_VERSION_2)
|
||||
updated_prompt_embeds = id_encoder2.forward(ctx0,
|
||||
runtime_backend,
|
||||
id_pixel_values_d,
|
||||
prompt_embeds_d,
|
||||
class_tokens_mask_d,
|
||||
@@ -780,10 +577,11 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
bool applied = false;
|
||||
|
||||
PhotoMakerIDEmbed(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
ModelLoader* ml,
|
||||
const std::string& file_path = "",
|
||||
const std::string& prefix = "")
|
||||
: file_path(file_path), GGMLRunner(backend), model_loader(ml) {
|
||||
: file_path(file_path), GGMLRunner(backend, offload_params_to_cpu), model_loader(ml) {
|
||||
if (!model_loader->init_from_file(file_path, prefix)) {
|
||||
load_failed = true;
|
||||
}
|
||||
@@ -793,7 +591,7 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
return "id_embeds";
|
||||
}
|
||||
|
||||
bool load_from_file(bool filter_tensor = false) {
|
||||
bool load_from_file(bool filter_tensor, int n_threads) {
|
||||
LOG_INFO("loading PhotoMaker ID Embeds from '%s'", file_path.c_str());
|
||||
|
||||
if (load_failed) {
|
||||
@@ -801,7 +599,8 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dry_run = true;
|
||||
bool dry_run = true;
|
||||
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;
|
||||
|
||||
@@ -810,6 +609,7 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
return true;
|
||||
}
|
||||
if (dry_run) {
|
||||
std::lock_guard<std::mutex> lock(tensor_mutex);
|
||||
struct ggml_tensor* real = ggml_new_tensor(params_ctx,
|
||||
tensor_storage.type,
|
||||
tensor_storage.n_dims,
|
||||
@@ -823,11 +623,11 @@ struct PhotoMakerIDEmbed : public GGMLRunner {
|
||||
return true;
|
||||
};
|
||||
|
||||
model_loader->load_tensors(on_new_tensor_cb, backend);
|
||||
model_loader->load_tensors(on_new_tensor_cb, n_threads);
|
||||
alloc_params_buffer();
|
||||
|
||||
dry_run = false;
|
||||
model_loader->load_tensors(on_new_tensor_cb, backend);
|
||||
model_loader->load_tensors(on_new_tensor_cb, n_threads);
|
||||
|
||||
LOG_DEBUG("finished loading PhotoMaker ID Embeds ");
|
||||
return true;
|
||||
|
||||
+10
-11
@@ -6,7 +6,7 @@
|
||||
|
||||
void convolve(struct ggml_tensor* input, struct ggml_tensor* output, struct ggml_tensor* kernel, int padding) {
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = 20 * 1024 * 1024; // 10
|
||||
params.mem_size = 80 * input->ne[0] * input->ne[1]; // 20M for 512x512
|
||||
params.mem_buffer = NULL;
|
||||
params.no_alloc = false;
|
||||
struct ggml_context* ctx0 = ggml_init(params);
|
||||
@@ -162,16 +162,16 @@ void threshold_hystersis(struct ggml_tensor* img, float high_threshold, float lo
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* preprocess_canny(uint8_t* img, int width, int height, float high_threshold, float low_threshold, float weak, float strong, bool inverse) {
|
||||
bool preprocess_canny(sd_image_t img, float high_threshold, float low_threshold, float weak, float strong, bool inverse) {
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = static_cast<size_t>(10 * 1024 * 1024); // 10
|
||||
params.mem_size = static_cast<size_t>(40 * img.width * img.height); // 10MB for 512x512
|
||||
params.mem_buffer = NULL;
|
||||
params.no_alloc = false;
|
||||
struct ggml_context* work_ctx = ggml_init(params);
|
||||
|
||||
if (!work_ctx) {
|
||||
LOG_ERROR("ggml_init() failed");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
float kX[9] = {
|
||||
@@ -192,8 +192,8 @@ uint8_t* preprocess_canny(uint8_t* img, int width, int height, float high_thresh
|
||||
struct ggml_tensor* sf_ky = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 3, 3, 1, 1);
|
||||
memcpy(sf_ky->data, kY, ggml_nbytes(sf_ky));
|
||||
gaussian_kernel(gkernel);
|
||||
struct ggml_tensor* image = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, width, height, 3, 1);
|
||||
struct ggml_tensor* image_gray = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, width, height, 1, 1);
|
||||
struct ggml_tensor* image = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, img.width, img.height, 3, 1);
|
||||
struct ggml_tensor* image_gray = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, img.width, img.height, 1, 1);
|
||||
struct ggml_tensor* iX = ggml_dup_tensor(work_ctx, image_gray);
|
||||
struct ggml_tensor* iY = ggml_dup_tensor(work_ctx, image_gray);
|
||||
struct ggml_tensor* G = ggml_dup_tensor(work_ctx, image_gray);
|
||||
@@ -209,8 +209,8 @@ uint8_t* preprocess_canny(uint8_t* img, int width, int height, float high_thresh
|
||||
non_max_supression(image_gray, G, tetha);
|
||||
threshold_hystersis(image_gray, high_threshold, low_threshold, weak, strong);
|
||||
// to RGB channels
|
||||
for (int iy = 0; iy < height; iy++) {
|
||||
for (int ix = 0; ix < width; ix++) {
|
||||
for (int iy = 0; iy < img.height; iy++) {
|
||||
for (int ix = 0; ix < img.width; ix++) {
|
||||
float gray = ggml_tensor_get_f32(image_gray, ix, iy);
|
||||
gray = inverse ? 1.0f - gray : gray;
|
||||
ggml_tensor_set_f32(image, gray, ix, iy);
|
||||
@@ -218,10 +218,9 @@ uint8_t* preprocess_canny(uint8_t* img, int width, int height, float high_thresh
|
||||
ggml_tensor_set_f32(image, gray, ix, iy, 2);
|
||||
}
|
||||
}
|
||||
free(img);
|
||||
uint8_t* output = sd_tensor_to_image(image);
|
||||
sd_tensor_to_image(image, img.data);
|
||||
ggml_free(work_ctx);
|
||||
return output;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // __PREPROCESSING_HPP__
|
||||
+694
@@ -0,0 +1,694 @@
|
||||
#ifndef __QWEN_IMAGE_HPP__
|
||||
#define __QWEN_IMAGE_HPP__
|
||||
|
||||
#include "common.hpp"
|
||||
#include "flux.hpp"
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
namespace Qwen {
|
||||
constexpr int QWEN_IMAGE_GRAPH_SIZE = 20480;
|
||||
|
||||
struct TimestepEmbedding : public GGMLBlock {
|
||||
public:
|
||||
TimestepEmbedding(int64_t in_channels,
|
||||
int64_t time_embed_dim,
|
||||
int64_t out_dim = 0,
|
||||
int64_t cond_proj_dim = 0,
|
||||
bool sample_proj_bias = true) {
|
||||
blocks["linear_1"] = std::shared_ptr<GGMLBlock>(new Linear(in_channels, time_embed_dim, sample_proj_bias));
|
||||
if (cond_proj_dim > 0) {
|
||||
blocks["cond_proj"] = std::shared_ptr<GGMLBlock>(new Linear(cond_proj_dim, in_channels, false));
|
||||
}
|
||||
if (out_dim <= 0) {
|
||||
out_dim = time_embed_dim;
|
||||
}
|
||||
blocks["linear_2"] = std::shared_ptr<GGMLBlock>(new Linear(time_embed_dim, out_dim, sample_proj_bias));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
struct ggml_tensor* sample,
|
||||
struct ggml_tensor* condition = nullptr) {
|
||||
if (condition != nullptr) {
|
||||
auto cond_proj = std::dynamic_pointer_cast<Linear>(blocks["cond_proj"]);
|
||||
sample = ggml_add(ctx, sample, cond_proj->forward(ctx, condition));
|
||||
}
|
||||
auto linear_1 = std::dynamic_pointer_cast<Linear>(blocks["linear_1"]);
|
||||
auto linear_2 = std::dynamic_pointer_cast<Linear>(blocks["linear_2"]);
|
||||
|
||||
sample = linear_1->forward(ctx, sample);
|
||||
sample = ggml_silu_inplace(ctx, sample);
|
||||
sample = linear_2->forward(ctx, sample);
|
||||
return sample;
|
||||
}
|
||||
};
|
||||
|
||||
struct QwenTimestepProjEmbeddings : public GGMLBlock {
|
||||
public:
|
||||
QwenTimestepProjEmbeddings(int64_t embedding_dim) {
|
||||
blocks["timestep_embedder"] = std::shared_ptr<GGMLBlock>(new TimestepEmbedding(256, embedding_dim));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
struct ggml_tensor* timesteps) {
|
||||
// timesteps: [N,]
|
||||
// return: [N, embedding_dim]
|
||||
auto timestep_embedder = std::dynamic_pointer_cast<TimestepEmbedding>(blocks["timestep_embedder"]);
|
||||
|
||||
auto timesteps_proj = ggml_nn_timestep_embedding(ctx, timesteps, 256, 10000, 1.f);
|
||||
auto timesteps_emb = timestep_embedder->forward(ctx, timesteps_proj);
|
||||
return timesteps_emb;
|
||||
}
|
||||
};
|
||||
|
||||
struct QwenImageAttention : public GGMLBlock {
|
||||
protected:
|
||||
int64_t dim_head;
|
||||
bool flash_attn;
|
||||
|
||||
public:
|
||||
QwenImageAttention(int64_t query_dim,
|
||||
int64_t dim_head,
|
||||
int64_t num_heads,
|
||||
int64_t out_dim = 0,
|
||||
int64_t out_context_dim = 0,
|
||||
bool bias = true,
|
||||
bool out_bias = true,
|
||||
float eps = 1e-6,
|
||||
bool flash_attn = false)
|
||||
: dim_head(dim_head), flash_attn(flash_attn) {
|
||||
int64_t inner_dim = out_dim > 0 ? out_dim : dim_head * num_heads;
|
||||
out_dim = out_dim > 0 ? out_dim : query_dim;
|
||||
out_context_dim = out_context_dim > 0 ? out_context_dim : query_dim;
|
||||
|
||||
blocks["to_q"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
blocks["to_k"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
blocks["to_v"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
|
||||
blocks["norm_q"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
blocks["norm_k"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
|
||||
blocks["add_q_proj"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
blocks["add_k_proj"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
blocks["add_v_proj"] = std::shared_ptr<GGMLBlock>(new Linear(query_dim, inner_dim, bias));
|
||||
|
||||
blocks["norm_added_q"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
blocks["norm_added_k"] = std::shared_ptr<GGMLBlock>(new RMSNorm(dim_head, eps));
|
||||
|
||||
float scale = 1.f / 32.f;
|
||||
// The purpose of the scale here is to prevent NaN issues in certain situations.
|
||||
// For example when using CUDA but the weights are k-quants (not all prompts).
|
||||
blocks["to_out.0"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, out_dim, out_bias, false, false, scale));
|
||||
// to_out.1 is nn.Dropout
|
||||
|
||||
blocks["to_add_out"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, out_context_dim, out_bias, false, false, scale));
|
||||
}
|
||||
|
||||
std::pair<ggml_tensor*, ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* img,
|
||||
struct ggml_tensor* txt,
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* mask = nullptr) {
|
||||
// img: [N, n_img_token, hidden_size]
|
||||
// txt: [N, n_txt_token, hidden_size]
|
||||
// pe: [n_img_token + n_txt_token, d_head/2, 2, 2]
|
||||
// return: ([N, n_img_token, hidden_size], [N, n_txt_token, hidden_size])
|
||||
|
||||
auto norm_q = std::dynamic_pointer_cast<UnaryBlock>(blocks["norm_q"]);
|
||||
auto norm_k = std::dynamic_pointer_cast<UnaryBlock>(blocks["norm_k"]);
|
||||
|
||||
auto to_q = std::dynamic_pointer_cast<Linear>(blocks["to_q"]);
|
||||
auto to_k = std::dynamic_pointer_cast<Linear>(blocks["to_k"]);
|
||||
auto to_v = std::dynamic_pointer_cast<Linear>(blocks["to_v"]);
|
||||
auto to_out_0 = std::dynamic_pointer_cast<Linear>(blocks["to_out.0"]);
|
||||
|
||||
auto norm_added_q = std::dynamic_pointer_cast<UnaryBlock>(blocks["norm_added_q"]);
|
||||
auto norm_added_k = std::dynamic_pointer_cast<UnaryBlock>(blocks["norm_added_k"]);
|
||||
|
||||
auto add_q_proj = std::dynamic_pointer_cast<Linear>(blocks["add_q_proj"]);
|
||||
auto add_k_proj = std::dynamic_pointer_cast<Linear>(blocks["add_k_proj"]);
|
||||
auto add_v_proj = std::dynamic_pointer_cast<Linear>(blocks["add_v_proj"]);
|
||||
auto to_add_out = std::dynamic_pointer_cast<Linear>(blocks["to_add_out"]);
|
||||
|
||||
int64_t N = img->ne[2];
|
||||
int64_t n_img_token = img->ne[1];
|
||||
int64_t n_txt_token = txt->ne[1];
|
||||
|
||||
auto img_q = to_q->forward(ctx, img);
|
||||
int64_t num_heads = img_q->ne[0] / dim_head;
|
||||
img_q = ggml_reshape_4d(ctx, img_q, dim_head, num_heads, n_img_token, N); // [N, n_img_token, n_head, d_head]
|
||||
auto img_k = to_k->forward(ctx, img);
|
||||
img_k = ggml_reshape_4d(ctx, img_k, dim_head, num_heads, n_img_token, N); // [N, n_img_token, n_head, d_head]
|
||||
auto img_v = to_v->forward(ctx, img);
|
||||
img_v = ggml_reshape_4d(ctx, img_v, dim_head, num_heads, n_img_token, N); // [N, n_img_token, n_head, d_head]
|
||||
|
||||
img_q = norm_q->forward(ctx, img_q);
|
||||
img_k = norm_k->forward(ctx, img_k);
|
||||
|
||||
auto txt_q = add_q_proj->forward(ctx, txt);
|
||||
txt_q = ggml_reshape_4d(ctx, txt_q, dim_head, num_heads, n_txt_token, N); // [N, n_txt_token, n_head, d_head]
|
||||
auto txt_k = add_k_proj->forward(ctx, txt);
|
||||
txt_k = ggml_reshape_4d(ctx, txt_k, dim_head, num_heads, n_txt_token, N); // [N, n_txt_token, n_head, d_head]
|
||||
auto txt_v = add_v_proj->forward(ctx, txt);
|
||||
txt_v = ggml_reshape_4d(ctx, txt_v, dim_head, num_heads, n_txt_token, N); // [N, n_txt_token, n_head, d_head]
|
||||
|
||||
txt_q = norm_added_q->forward(ctx, txt_q);
|
||||
txt_k = norm_added_k->forward(ctx, txt_k);
|
||||
|
||||
auto q = ggml_concat(ctx, txt_q, img_q, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
auto k = ggml_concat(ctx, txt_k, img_k, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
auto v = ggml_concat(ctx, txt_v, img_v, 2); // [N, n_txt_token + n_img_token, n_head, d_head]
|
||||
|
||||
auto attn = Rope::attention(ctx, backend, q, k, v, pe, mask, flash_attn, (1.0f / 128.f)); // [N, n_txt_token + n_img_token, n_head*d_head]
|
||||
attn = ggml_cont(ctx, ggml_permute(ctx, attn, 0, 2, 1, 3)); // [n_txt_token + n_img_token, N, hidden_size]
|
||||
auto txt_attn_out = ggml_view_3d(ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
txt->ne[1],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
0); // [n_txt_token, N, hidden_size]
|
||||
txt_attn_out = ggml_cont(ctx, ggml_permute(ctx, txt_attn_out, 0, 2, 1, 3)); // [N, n_txt_token, hidden_size]
|
||||
auto img_attn_out = ggml_view_3d(ctx,
|
||||
attn,
|
||||
attn->ne[0],
|
||||
attn->ne[1],
|
||||
img->ne[1],
|
||||
attn->nb[1],
|
||||
attn->nb[2],
|
||||
attn->nb[2] * txt->ne[1]); // [n_img_token, N, hidden_size]
|
||||
img_attn_out = ggml_cont(ctx, ggml_permute(ctx, img_attn_out, 0, 2, 1, 3)); // [N, n_img_token, hidden_size]
|
||||
|
||||
img_attn_out = to_out_0->forward(ctx, img_attn_out);
|
||||
txt_attn_out = to_add_out->forward(ctx, txt_attn_out);
|
||||
|
||||
return {img_attn_out, txt_attn_out};
|
||||
}
|
||||
};
|
||||
|
||||
class QwenImageTransformerBlock : public GGMLBlock {
|
||||
public:
|
||||
QwenImageTransformerBlock(int64_t dim,
|
||||
int64_t num_attention_heads,
|
||||
int64_t attention_head_dim,
|
||||
float eps = 1e-6,
|
||||
bool flash_attn = false) {
|
||||
// img_mod.0 is nn.SiLU()
|
||||
blocks["img_mod.1"] = std::shared_ptr<GGMLBlock>(new Linear(dim, 6 * dim, true));
|
||||
|
||||
blocks["img_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["img_norm2"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["img_mlp"] = std::shared_ptr<GGMLBlock>(new FeedForward(dim, dim, 4, FeedForward::Activation::GELU, true));
|
||||
|
||||
// txt_mod.0 is nn.SiLU()
|
||||
blocks["txt_mod.1"] = std::shared_ptr<GGMLBlock>(new Linear(dim, 6 * dim, true));
|
||||
|
||||
blocks["txt_norm1"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["txt_norm2"] = std::shared_ptr<GGMLBlock>(new LayerNorm(dim, eps, false));
|
||||
blocks["txt_mlp"] = std::shared_ptr<GGMLBlock>(new FeedForward(dim, dim, 4, FeedForward::Activation::GELU));
|
||||
|
||||
blocks["attn"] = std::shared_ptr<GGMLBlock>(new QwenImageAttention(dim,
|
||||
attention_head_dim,
|
||||
num_attention_heads,
|
||||
0, // out_dim
|
||||
0, // out_context-dim
|
||||
true, // bias
|
||||
true, // out_bias
|
||||
eps,
|
||||
flash_attn));
|
||||
}
|
||||
|
||||
virtual std::pair<ggml_tensor*, ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* img,
|
||||
struct ggml_tensor* txt,
|
||||
struct ggml_tensor* t_emb,
|
||||
struct ggml_tensor* pe) {
|
||||
// img: [N, n_img_token, hidden_size]
|
||||
// txt: [N, n_txt_token, hidden_size]
|
||||
// pe: [n_img_token + n_txt_token, d_head/2, 2, 2]
|
||||
// return: ([N, n_img_token, hidden_size], [N, n_txt_token, hidden_size])
|
||||
|
||||
auto img_mod_1 = std::dynamic_pointer_cast<Linear>(blocks["img_mod.1"]);
|
||||
auto img_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["img_norm1"]);
|
||||
auto img_norm2 = std::dynamic_pointer_cast<LayerNorm>(blocks["img_norm2"]);
|
||||
auto img_mlp = std::dynamic_pointer_cast<FeedForward>(blocks["img_mlp"]);
|
||||
|
||||
auto txt_mod_1 = std::dynamic_pointer_cast<Linear>(blocks["txt_mod.1"]);
|
||||
auto txt_norm1 = std::dynamic_pointer_cast<LayerNorm>(blocks["txt_norm1"]);
|
||||
auto txt_norm2 = std::dynamic_pointer_cast<LayerNorm>(blocks["txt_norm2"]);
|
||||
auto txt_mlp = std::dynamic_pointer_cast<FeedForward>(blocks["txt_mlp"]);
|
||||
|
||||
auto attn = std::dynamic_pointer_cast<QwenImageAttention>(blocks["attn"]);
|
||||
|
||||
auto img_mod_params = ggml_silu(ctx, t_emb);
|
||||
img_mod_params = img_mod_1->forward(ctx, img_mod_params);
|
||||
auto img_mod_param_vec = ggml_chunk(ctx, img_mod_params, 6, 0);
|
||||
|
||||
auto txt_mod_params = ggml_silu(ctx, t_emb);
|
||||
txt_mod_params = txt_mod_1->forward(ctx, txt_mod_params);
|
||||
auto txt_mod_param_vec = ggml_chunk(ctx, txt_mod_params, 6, 0);
|
||||
|
||||
auto img_normed = img_norm1->forward(ctx, img);
|
||||
auto img_modulated = Flux::modulate(ctx, img_normed, img_mod_param_vec[0], img_mod_param_vec[1]);
|
||||
auto img_gate1 = img_mod_param_vec[2];
|
||||
|
||||
auto txt_normed = txt_norm1->forward(ctx, txt);
|
||||
auto txt_modulated = Flux::modulate(ctx, txt_normed, txt_mod_param_vec[0], txt_mod_param_vec[1]);
|
||||
auto txt_gate1 = txt_mod_param_vec[2];
|
||||
|
||||
auto [img_attn_output, txt_attn_output] = attn->forward(ctx, backend, img_modulated, txt_modulated, pe);
|
||||
|
||||
img = ggml_add(ctx, img, ggml_mul(ctx, img_attn_output, img_gate1));
|
||||
txt = ggml_add(ctx, txt, ggml_mul(ctx, txt_attn_output, txt_gate1));
|
||||
|
||||
auto img_normed2 = img_norm2->forward(ctx, img);
|
||||
auto img_modulated2 = Flux::modulate(ctx, img_normed2, img_mod_param_vec[3], img_mod_param_vec[4]);
|
||||
auto img_gate2 = img_mod_param_vec[5];
|
||||
|
||||
auto txt_normed2 = txt_norm2->forward(ctx, txt);
|
||||
auto txt_modulated2 = Flux::modulate(ctx, txt_normed2, txt_mod_param_vec[3], txt_mod_param_vec[4]);
|
||||
auto txt_gate2 = txt_mod_param_vec[5];
|
||||
|
||||
auto img_mlp_out = img_mlp->forward(ctx, img_modulated2);
|
||||
auto txt_mlp_out = txt_mlp->forward(ctx, txt_modulated2);
|
||||
|
||||
img = ggml_add(ctx, img, ggml_mul(ctx, img_mlp_out, img_gate2));
|
||||
txt = ggml_add(ctx, txt, ggml_mul(ctx, txt_mlp_out, txt_gate2));
|
||||
|
||||
return {img, txt};
|
||||
}
|
||||
};
|
||||
|
||||
struct AdaLayerNormContinuous : public GGMLBlock {
|
||||
public:
|
||||
AdaLayerNormContinuous(int64_t embedding_dim,
|
||||
int64_t conditioning_embedding_dim,
|
||||
bool elementwise_affine = true,
|
||||
float eps = 1e-5f,
|
||||
bool bias = true) {
|
||||
blocks["norm"] = std::shared_ptr<GGMLBlock>(new LayerNorm(conditioning_embedding_dim, eps, elementwise_affine, bias));
|
||||
blocks["linear"] = std::shared_ptr<GGMLBlock>(new Linear(conditioning_embedding_dim, embedding_dim * 2, bias));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* c) {
|
||||
// x: [N, n_token, hidden_size]
|
||||
// c: [N, hidden_size]
|
||||
// return: [N, n_token, patch_size * patch_size * out_channels]
|
||||
|
||||
auto norm = std::dynamic_pointer_cast<LayerNorm>(blocks["norm"]);
|
||||
auto linear = std::dynamic_pointer_cast<Linear>(blocks["linear"]);
|
||||
|
||||
auto emb = linear->forward(ctx, ggml_silu(ctx, c));
|
||||
auto mods = ggml_chunk(ctx, emb, 2, 0);
|
||||
auto scale = mods[0];
|
||||
auto shift = mods[1];
|
||||
|
||||
x = norm->forward(ctx, x);
|
||||
x = Flux::modulate(ctx, x, shift, scale);
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
struct QwenImageParams {
|
||||
int64_t patch_size = 2;
|
||||
int64_t in_channels = 64;
|
||||
int64_t out_channels = 16;
|
||||
int64_t num_layers = 60;
|
||||
int64_t attention_head_dim = 128;
|
||||
int64_t num_attention_heads = 24;
|
||||
int64_t joint_attention_dim = 3584;
|
||||
float theta = 10000;
|
||||
std::vector<int> axes_dim = {16, 56, 56};
|
||||
int64_t axes_dim_sum = 128;
|
||||
bool flash_attn = false;
|
||||
};
|
||||
|
||||
class QwenImageModel : public GGMLBlock {
|
||||
protected:
|
||||
QwenImageParams params;
|
||||
|
||||
public:
|
||||
QwenImageModel() {}
|
||||
QwenImageModel(QwenImageParams params)
|
||||
: params(params) {
|
||||
int64_t inner_dim = params.num_attention_heads * params.attention_head_dim;
|
||||
blocks["time_text_embed"] = std::shared_ptr<GGMLBlock>(new QwenTimestepProjEmbeddings(inner_dim));
|
||||
blocks["txt_norm"] = std::shared_ptr<GGMLBlock>(new RMSNorm(params.joint_attention_dim, 1e-6f));
|
||||
blocks["img_in"] = std::shared_ptr<GGMLBlock>(new Linear(params.in_channels, inner_dim));
|
||||
blocks["txt_in"] = std::shared_ptr<GGMLBlock>(new Linear(params.joint_attention_dim, inner_dim));
|
||||
|
||||
// blocks
|
||||
for (int i = 0; i < params.num_layers; i++) {
|
||||
auto block = std::shared_ptr<GGMLBlock>(new QwenImageTransformerBlock(inner_dim,
|
||||
params.num_attention_heads,
|
||||
params.attention_head_dim,
|
||||
1e-6f,
|
||||
params.flash_attn));
|
||||
blocks["transformer_blocks." + std::to_string(i)] = block;
|
||||
}
|
||||
|
||||
blocks["norm_out"] = std::shared_ptr<GGMLBlock>(new AdaLayerNormContinuous(inner_dim, inner_dim, false, 1e-6f));
|
||||
blocks["proj_out"] = std::shared_ptr<GGMLBlock>(new Linear(inner_dim, params.patch_size * params.patch_size * params.out_channels));
|
||||
}
|
||||
|
||||
struct ggml_tensor* pad_to_patch_size(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
|
||||
int pad_h = (params.patch_size - H % params.patch_size) % params.patch_size;
|
||||
int pad_w = (params.patch_size - W % params.patch_size) % params.patch_size;
|
||||
x = ggml_pad(ctx, x, pad_w, pad_h, 0, 0); // [N, C, H + pad_h, W + pad_w]
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* patchify(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
// x: [N, C, H, W]
|
||||
// return: [N, h*w, C * patch_size * patch_size]
|
||||
int64_t N = x->ne[3];
|
||||
int64_t C = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
int64_t p = params.patch_size;
|
||||
int64_t h = H / params.patch_size;
|
||||
int64_t w = W / params.patch_size;
|
||||
|
||||
GGML_ASSERT(h * p == H && w * p == W);
|
||||
|
||||
x = ggml_reshape_4d(ctx, x, p, w, p, h * C * N); // [N*C*h, p, w, p]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N*C*h, w, p, p]
|
||||
x = ggml_reshape_4d(ctx, x, p * p, w * h, C, N); // [N, C, h*w, p*p]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N, h*w, C, p*p]
|
||||
x = ggml_reshape_3d(ctx, x, p * p * C, w * h, N); // [N, h*w, C*p*p]
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* process_img(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x) {
|
||||
x = pad_to_patch_size(ctx, x);
|
||||
x = patchify(ctx, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* unpatchify(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
int64_t h,
|
||||
int64_t w) {
|
||||
// x: [N, h*w, C*patch_size*patch_size]
|
||||
// return: [N, C, H, W]
|
||||
int64_t N = x->ne[2];
|
||||
int64_t C = x->ne[0] / params.patch_size / params.patch_size;
|
||||
int64_t H = h * params.patch_size;
|
||||
int64_t W = w * params.patch_size;
|
||||
int64_t p = params.patch_size;
|
||||
|
||||
GGML_ASSERT(C * p * p == x->ne[0]);
|
||||
|
||||
x = ggml_reshape_4d(ctx, x, p * p, C, w * h, N); // [N, h*w, C, p*p]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N, C, h*w, p*p]
|
||||
x = ggml_reshape_4d(ctx, x, p, p, w, h * C * N); // [N*C*h, w, p, p]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N*C*h, p, w, p]
|
||||
x = ggml_reshape_4d(ctx, x, W, H, C, N); // [N, C, h*p, w*p]
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward_orig(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timestep,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* pe) {
|
||||
auto time_text_embed = std::dynamic_pointer_cast<QwenTimestepProjEmbeddings>(blocks["time_text_embed"]);
|
||||
auto txt_norm = std::dynamic_pointer_cast<RMSNorm>(blocks["txt_norm"]);
|
||||
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<AdaLayerNormContinuous>(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 = img_in->forward(ctx, x);
|
||||
auto txt = txt_norm->forward(ctx, context);
|
||||
txt = txt_in->forward(ctx, txt);
|
||||
|
||||
for (int i = 0; i < params.num_layers; i++) {
|
||||
auto block = std::dynamic_pointer_cast<QwenImageTransformerBlock>(blocks["transformer_blocks." + std::to_string(i)]);
|
||||
|
||||
auto result = block->forward(ctx, backend, img, txt, t_emb, pe);
|
||||
img = result.first;
|
||||
txt = result.second;
|
||||
}
|
||||
|
||||
img = norm_out->forward(ctx, img, t_emb);
|
||||
img = proj_out->forward(ctx, img);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timestep,
|
||||
struct ggml_tensor* context,
|
||||
struct ggml_tensor* pe,
|
||||
std::vector<ggml_tensor*> ref_latents = {}) {
|
||||
// Forward pass of DiT.
|
||||
// x: [N, C, H, W]
|
||||
// timestep: [N,]
|
||||
// context: [N, L, D]
|
||||
// pe: [L, d_head/2, 2, 2]
|
||||
// return: [N, C, H, W]
|
||||
|
||||
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 img = process_img(ctx, x);
|
||||
uint64_t img_tokens = img->ne[1];
|
||||
|
||||
if (ref_latents.size() > 0) {
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
ref = process_img(ctx, ref);
|
||||
img = ggml_concat(ctx, img, ref, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t h_len = ((H + (params.patch_size / 2)) / params.patch_size);
|
||||
int64_t w_len = ((W + (params.patch_size / 2)) / params.patch_size);
|
||||
|
||||
auto out = forward_orig(ctx, backend, img, timestep, context, pe); // [N, h_len*w_len, ph*pw*C]
|
||||
|
||||
if (out->ne[1] > img_tokens) {
|
||||
out = ggml_cont(ctx, ggml_permute(ctx, out, 0, 2, 1, 3)); // [num_tokens, N, C * patch_size * patch_size]
|
||||
out = ggml_view_3d(ctx, out, out->ne[0], out->ne[1], img_tokens, out->nb[1], out->nb[2], 0);
|
||||
out = ggml_cont(ctx, ggml_permute(ctx, out, 0, 2, 1, 3)); // [N, h*w, C * patch_size * patch_size]
|
||||
}
|
||||
|
||||
out = unpatchify(ctx, out, h_len, w_len); // [N, C, H + pad_h, W + pad_w]
|
||||
|
||||
// slice
|
||||
out = ggml_slice(ctx, out, 1, 0, H); // [N, C, H, W + pad_w]
|
||||
out = ggml_slice(ctx, out, 0, 0, W); // [N, C, H, W]
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
struct QwenImageRunner : public GGMLRunner {
|
||||
public:
|
||||
QwenImageParams qwen_image_params;
|
||||
QwenImageModel qwen_image;
|
||||
std::vector<float> pe_vec;
|
||||
SDVersion version;
|
||||
|
||||
QwenImageRunner(ggml_backend_t backend,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "",
|
||||
SDVersion version = VERSION_QWEN_IMAGE,
|
||||
bool flash_attn = false)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||
qwen_image_params.flash_attn = flash_attn;
|
||||
qwen_image_params.num_layers = 0;
|
||||
for (auto pair : tensor_types) {
|
||||
std::string tensor_name = pair.first;
|
||||
if (tensor_name.find(prefix) == std::string::npos)
|
||||
continue;
|
||||
size_t pos = tensor_name.find("transformer_blocks.");
|
||||
if (pos != std::string::npos) {
|
||||
tensor_name = tensor_name.substr(pos); // remove prefix
|
||||
auto items = split_string(tensor_name, '.');
|
||||
if (items.size() > 1) {
|
||||
int block_index = atoi(items[1].c_str());
|
||||
if (block_index + 1 > qwen_image_params.num_layers) {
|
||||
qwen_image_params.num_layers = block_index + 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
LOG_INFO("qwen_image_params.num_layers: %ld", qwen_image_params.num_layers);
|
||||
qwen_image = QwenImageModel(qwen_image_params);
|
||||
qwen_image.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return "qwen_image";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
qwen_image.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
bool increase_ref_index = false) {
|
||||
GGML_ASSERT(x->ne[3] == 1);
|
||||
struct ggml_cgraph* gf = ggml_new_graph_custom(compute_ctx, QWEN_IMAGE_GRAPH_SIZE, false);
|
||||
|
||||
x = to_backend(x);
|
||||
context = to_backend(context);
|
||||
timesteps = to_backend(timesteps);
|
||||
|
||||
for (int i = 0; i < ref_latents.size(); i++) {
|
||||
ref_latents[i] = to_backend(ref_latents[i]);
|
||||
}
|
||||
|
||||
pe_vec = Rope::gen_qwen_image_pe(x->ne[1],
|
||||
x->ne[0],
|
||||
qwen_image_params.patch_size,
|
||||
x->ne[3],
|
||||
context->ne[1],
|
||||
ref_latents,
|
||||
increase_ref_index,
|
||||
qwen_image_params.theta,
|
||||
qwen_image_params.axes_dim);
|
||||
int pos_len = pe_vec.size() / qwen_image_params.axes_dim_sum / 2;
|
||||
// LOG_DEBUG("pos_len %d", pos_len);
|
||||
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, qwen_image_params.axes_dim_sum / 2, pos_len);
|
||||
// pe->data = pe_vec.data();
|
||||
// print_ggml_tensor(pe, true, "pe");
|
||||
// pe->data = NULL;
|
||||
set_backend_tensor_data(pe, pe_vec.data());
|
||||
|
||||
struct ggml_tensor* out = qwen_image.forward(compute_ctx,
|
||||
runtime_backend,
|
||||
x,
|
||||
timesteps,
|
||||
context,
|
||||
pe,
|
||||
ref_latents);
|
||||
|
||||
ggml_build_forward_expand(gf, out);
|
||||
|
||||
return gf;
|
||||
}
|
||||
|
||||
void compute(int n_threads,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
std::vector<ggml_tensor*> ref_latents = {},
|
||||
bool increase_ref_index = false,
|
||||
struct ggml_tensor** output = NULL,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
// x: [N, in_channels, h, w]
|
||||
// timesteps: [N, ]
|
||||
// context: [N, max_position, hidden_size]
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, ref_latents, increase_ref_index);
|
||||
};
|
||||
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = static_cast<size_t>(1024 * 1024) * 1024; // 1GB
|
||||
params.mem_buffer = NULL;
|
||||
params.no_alloc = false;
|
||||
|
||||
struct ggml_context* work_ctx = ggml_init(params);
|
||||
GGML_ASSERT(work_ctx != NULL);
|
||||
|
||||
{
|
||||
// auto x = ggml_new_tensor_4d(work_ctx, GGML_TYPE_F32, 16, 16, 16, 1);
|
||||
// ggml_set_f32(x, 0.01f);
|
||||
auto x = load_tensor_from_file(work_ctx, "./qwen_image_x.bin");
|
||||
print_ggml_tensor(x);
|
||||
|
||||
std::vector<float> timesteps_vec(1, 1000.f);
|
||||
auto timesteps = vector_to_ggml_tensor(work_ctx, timesteps_vec);
|
||||
|
||||
// auto context = ggml_new_tensor_3d(work_ctx, GGML_TYPE_F32, 3584, 256, 1);
|
||||
// ggml_set_f32(context, 0.01f);
|
||||
auto context = load_tensor_from_file(work_ctx, "./qwen_image_context.bin");
|
||||
print_ggml_tensor(context);
|
||||
|
||||
struct ggml_tensor* out = NULL;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
compute(8, x, timesteps, context, {}, false, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
LOG_DEBUG("qwen_image test done in %dms", t1 - t0);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_from_file_and_test(const std::string& file_path) {
|
||||
// cuda q8: pass
|
||||
// cuda q8 fa: nan
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_Q8_0;
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path, "model.diffusion_model.")) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto tensor_types = model_loader.tensor_storages_types;
|
||||
for (auto& item : tensor_types) {
|
||||
// LOG_DEBUG("%s %u", item.first.c_str(), item.second);
|
||||
if (ends_with(item.first, "weight")) {
|
||||
item.second = model_data_type;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<QwenImageRunner> qwen_image = std::shared_ptr<QwenImageRunner>(new QwenImageRunner(backend,
|
||||
false,
|
||||
tensor_types,
|
||||
"model.diffusion_model",
|
||||
VERSION_QWEN_IMAGE,
|
||||
true));
|
||||
|
||||
qwen_image->alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
qwen_image->get_param_tensors(tensors, "model.diffusion_model");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("qwen_image model loaded");
|
||||
qwen_image->test();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace name
|
||||
|
||||
#endif // __QWEN_IMAGE_HPP__
|
||||
+1388
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,410 @@
|
||||
#ifndef __ROPE_HPP__
|
||||
#define __ROPE_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include "ggml_extend.hpp"
|
||||
|
||||
namespace Rope {
|
||||
template <class T>
|
||||
__STATIC_INLINE__ std::vector<T> linspace(T start, T end, int num) {
|
||||
std::vector<T> result(num);
|
||||
if (num == 1) {
|
||||
result[0] = start;
|
||||
return result;
|
||||
}
|
||||
T step = (end - start) / (num - 1);
|
||||
for (int i = 0; i < num; ++i) {
|
||||
result[i] = start + i * step;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> transpose(const std::vector<std::vector<float>>& mat) {
|
||||
int rows = mat.size();
|
||||
int cols = mat[0].size();
|
||||
std::vector<std::vector<float>> transposed(cols, std::vector<float>(rows));
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
transposed[j][i] = mat[i][j];
|
||||
}
|
||||
}
|
||||
return transposed;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<float> flatten(const std::vector<std::vector<float>>& vec) {
|
||||
std::vector<float> flat_vec;
|
||||
for (const auto& sub_vec : vec) {
|
||||
flat_vec.insert(flat_vec.end(), sub_vec.begin(), sub_vec.end());
|
||||
}
|
||||
return flat_vec;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> rope(const std::vector<float>& pos, int dim, int theta) {
|
||||
assert(dim % 2 == 0);
|
||||
int half_dim = dim / 2;
|
||||
|
||||
std::vector<float> scale = linspace(0.f, (dim * 1.f - 2) / dim, half_dim);
|
||||
|
||||
std::vector<float> omega(half_dim);
|
||||
for (int i = 0; i < half_dim; ++i) {
|
||||
omega[i] = 1.0 / std::pow(theta, scale[i]);
|
||||
}
|
||||
|
||||
int pos_size = pos.size();
|
||||
std::vector<std::vector<float>> out(pos_size, std::vector<float>(half_dim));
|
||||
for (int i = 0; i < pos_size; ++i) {
|
||||
for (int j = 0; j < half_dim; ++j) {
|
||||
out[i][j] = pos[i] * omega[j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> result(pos_size, std::vector<float>(half_dim * 4));
|
||||
for (int i = 0; i < pos_size; ++i) {
|
||||
for (int j = 0; j < half_dim; ++j) {
|
||||
result[i][4 * j] = std::cos(out[i][j]);
|
||||
result[i][4 * j + 1] = -std::sin(out[i][j]);
|
||||
result[i][4 * j + 2] = std::sin(out[i][j]);
|
||||
result[i][4 * j + 3] = std::cos(out[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generate IDs for image patches and text
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_txt_ids(int bs, int context_len) {
|
||||
return std::vector<std::vector<float>>(bs * context_len, std::vector<float>(3, 0.0));
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_img_ids(int h, int w, int patch_size, int bs, int index = 0, int h_offset = 0, int w_offset = 0) {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
|
||||
std::vector<std::vector<float>> img_ids(h_len * w_len, std::vector<float>(3, 0.0));
|
||||
|
||||
std::vector<float> row_ids = linspace<float>(h_offset, h_len - 1 + h_offset, h_len);
|
||||
std::vector<float> col_ids = linspace<float>(w_offset, w_len - 1 + w_offset, w_len);
|
||||
|
||||
for (int i = 0; i < h_len; ++i) {
|
||||
for (int j = 0; j < w_len; ++j) {
|
||||
img_ids[i * w_len + j][0] = index;
|
||||
img_ids[i * w_len + j][1] = row_ids[i];
|
||||
img_ids[i * w_len + j][2] = col_ids[j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> img_ids_repeated(bs * img_ids.size(), std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < img_ids.size(); ++j) {
|
||||
img_ids_repeated[i * img_ids.size() + j] = img_ids[j];
|
||||
}
|
||||
}
|
||||
return img_ids_repeated;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> concat_ids(const std::vector<std::vector<float>>& a,
|
||||
const std::vector<std::vector<float>>& b,
|
||||
int bs) {
|
||||
size_t a_len = a.size() / bs;
|
||||
size_t b_len = b.size() / bs;
|
||||
std::vector<std::vector<float>> ids(a.size() + b.size(), std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < a_len; ++j) {
|
||||
ids[i * (a_len + b_len) + j] = a[i * a_len + j];
|
||||
}
|
||||
for (int j = 0; j < b_len; ++j) {
|
||||
ids[i * (a_len + b_len) + a_len + j] = b[i * b_len + j];
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<float> embed_nd(const std::vector<std::vector<float>>& ids,
|
||||
int bs,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> trans_ids = transpose(ids);
|
||||
size_t pos_len = ids.size() / bs;
|
||||
int num_axes = axes_dim.size();
|
||||
// for (int i = 0; i < pos_len; i++) {
|
||||
// std::cout << trans_ids[0][i] << " " << trans_ids[1][i] << " " << trans_ids[2][i] << std::endl;
|
||||
// }
|
||||
|
||||
int emb_dim = 0;
|
||||
for (int d : axes_dim)
|
||||
emb_dim += d / 2;
|
||||
|
||||
std::vector<std::vector<float>> emb(bs * pos_len, std::vector<float>(emb_dim * 2 * 2, 0.0));
|
||||
int offset = 0;
|
||||
for (int i = 0; i < num_axes; ++i) {
|
||||
std::vector<std::vector<float>> rope_emb = rope(trans_ids[i], axes_dim[i], theta); // [bs*pos_len, axes_dim[i]/2 * 2 * 2]
|
||||
for (int b = 0; b < bs; ++b) {
|
||||
for (int j = 0; j < pos_len; ++j) {
|
||||
for (int k = 0; k < rope_emb[0].size(); ++k) {
|
||||
emb[b * pos_len + j][offset + k] = rope_emb[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += rope_emb[0].size();
|
||||
}
|
||||
|
||||
return flatten(emb);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_refs_ids(int patch_size,
|
||||
int bs,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
std::vector<std::vector<float>> ids;
|
||||
uint64_t curr_h_offset = 0;
|
||||
uint64_t curr_w_offset = 0;
|
||||
int index = 1;
|
||||
for (ggml_tensor* ref : ref_latents) {
|
||||
uint64_t h_offset = 0;
|
||||
uint64_t w_offset = 0;
|
||||
if (!increase_ref_index) {
|
||||
if (ref->ne[1] + curr_h_offset > ref->ne[0] + curr_w_offset) {
|
||||
w_offset = curr_w_offset;
|
||||
} else {
|
||||
h_offset = curr_h_offset;
|
||||
}
|
||||
}
|
||||
|
||||
auto ref_ids = gen_img_ids(ref->ne[1], ref->ne[0], patch_size, bs, index, h_offset, w_offset);
|
||||
ids = concat_ids(ids, ref_ids, bs);
|
||||
|
||||
if (increase_ref_index) {
|
||||
index++;
|
||||
}
|
||||
|
||||
curr_h_offset = std::max(curr_h_offset, ref->ne[1] + h_offset);
|
||||
curr_w_offset = std::max(curr_w_offset, ref->ne[0] + w_offset);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_flux_ids(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
auto txt_ids = gen_txt_ids(bs, context_len);
|
||||
auto img_ids = gen_img_ids(h, w, patch_size, bs);
|
||||
|
||||
auto ids = concat_ids(txt_ids, img_ids, bs);
|
||||
if (ref_latents.size() > 0) {
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, ref_latents, increase_ref_index);
|
||||
ids = concat_ids(ids, refs_ids, bs);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Generate flux positional embeddings
|
||||
__STATIC_INLINE__ std::vector<float> gen_flux_pe(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_flux_ids(h, w, patch_size, bs, context_len, ref_latents, increase_ref_index);
|
||||
return embed_nd(ids, bs, theta, axes_dim);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_qwen_image_ids(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index) {
|
||||
int h_len = (h + (patch_size / 2)) / patch_size;
|
||||
int w_len = (w + (patch_size / 2)) / patch_size;
|
||||
int txt_id_start = std::max(h_len, w_len);
|
||||
auto txt_ids = linspace<float>(txt_id_start, context_len + txt_id_start, context_len);
|
||||
std::vector<std::vector<float>> txt_ids_repeated(bs * context_len, std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < txt_ids.size(); ++j) {
|
||||
txt_ids_repeated[i * txt_ids.size() + j] = {txt_ids[j], txt_ids[j], txt_ids[j]};
|
||||
}
|
||||
}
|
||||
auto img_ids = gen_img_ids(h, w, patch_size, bs);
|
||||
auto ids = concat_ids(txt_ids_repeated, img_ids, bs);
|
||||
if (ref_latents.size() > 0) {
|
||||
auto refs_ids = gen_refs_ids(patch_size, bs, ref_latents, increase_ref_index);
|
||||
ids = concat_ids(ids, refs_ids, bs);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Generate qwen_image positional embeddings
|
||||
__STATIC_INLINE__ std::vector<float> gen_qwen_image_pe(int h,
|
||||
int w,
|
||||
int patch_size,
|
||||
int bs,
|
||||
int context_len,
|
||||
const std::vector<ggml_tensor*>& ref_latents,
|
||||
bool increase_ref_index,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_qwen_image_ids(h, w, patch_size, bs, context_len, ref_latents, increase_ref_index);
|
||||
return embed_nd(ids, bs, theta, axes_dim);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_vid_ids(int t,
|
||||
int h,
|
||||
int w,
|
||||
int pt,
|
||||
int ph,
|
||||
int pw,
|
||||
int bs,
|
||||
int t_offset = 0,
|
||||
int h_offset = 0,
|
||||
int w_offset = 0) {
|
||||
int t_len = (t + (pt / 2)) / pt;
|
||||
int h_len = (h + (ph / 2)) / ph;
|
||||
int w_len = (w + (pw / 2)) / pw;
|
||||
|
||||
std::vector<std::vector<float>> vid_ids(t_len * h_len * w_len, std::vector<float>(3, 0.0));
|
||||
|
||||
std::vector<float> t_ids = linspace<float>(t_offset, t_len - 1 + t_offset, t_len);
|
||||
std::vector<float> h_ids = linspace<float>(h_offset, h_len - 1 + h_offset, h_len);
|
||||
std::vector<float> w_ids = linspace<float>(w_offset, w_len - 1 + w_offset, w_len);
|
||||
|
||||
for (int i = 0; i < t_len; ++i) {
|
||||
for (int j = 0; j < h_len; ++j) {
|
||||
for (int k = 0; k < w_len; ++k) {
|
||||
int idx = i * h_len * w_len + j * w_len + k;
|
||||
vid_ids[idx][0] = t_ids[i];
|
||||
vid_ids[idx][1] = h_ids[j];
|
||||
vid_ids[idx][2] = w_ids[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> vid_ids_repeated(bs * vid_ids.size(), std::vector<float>(3));
|
||||
for (int i = 0; i < bs; ++i) {
|
||||
for (int j = 0; j < vid_ids.size(); ++j) {
|
||||
vid_ids_repeated[i * vid_ids.size() + j] = vid_ids[j];
|
||||
}
|
||||
}
|
||||
return vid_ids_repeated;
|
||||
}
|
||||
|
||||
// Generate wan positional embeddings
|
||||
__STATIC_INLINE__ std::vector<float> gen_wan_pe(int t,
|
||||
int h,
|
||||
int w,
|
||||
int pt,
|
||||
int ph,
|
||||
int pw,
|
||||
int bs,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_vid_ids(t, h, w, pt, ph, pw, bs);
|
||||
return embed_nd(ids, bs, theta, axes_dim);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ std::vector<std::vector<float>> gen_qwen2vl_ids(int grid_h,
|
||||
int grid_w,
|
||||
int merge_size,
|
||||
const std::vector<int>& window_index) {
|
||||
std::vector<std::vector<float>> ids(grid_h * grid_w, std::vector<float>(2, 0.0));
|
||||
int index = 0;
|
||||
for (int ih = 0; ih < grid_h; ih += merge_size) {
|
||||
for (int iw = 0; iw < grid_w; iw += merge_size) {
|
||||
for (int iy = 0; iy < merge_size; iy++) {
|
||||
for (int ix = 0; ix < merge_size; ix++) {
|
||||
int inverse_index = window_index[index / (merge_size * merge_size)];
|
||||
int i = inverse_index * (merge_size * merge_size) + index % (merge_size * merge_size);
|
||||
|
||||
GGML_ASSERT(i < grid_h * grid_w);
|
||||
|
||||
ids[i][0] = ih + iy;
|
||||
ids[i][1] = iw + ix;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Generate qwen2vl positional embeddings
|
||||
__STATIC_INLINE__ std::vector<float> gen_qwen2vl_pe(int grid_h,
|
||||
int grid_w,
|
||||
int merge_size,
|
||||
const std::vector<int>& window_index,
|
||||
int theta,
|
||||
const std::vector<int>& axes_dim) {
|
||||
std::vector<std::vector<float>> ids = gen_qwen2vl_ids(grid_h, grid_w, merge_size, window_index);
|
||||
return embed_nd(ids, 1, theta, axes_dim);
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* apply_rope(struct ggml_context* ctx,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* pe,
|
||||
bool rope_interleaved = true) {
|
||||
// x: [N, L, n_head, d_head]
|
||||
// pe: [L, d_head/2, 2, 2], [[cos, -sin], [sin, cos]]
|
||||
int64_t d_head = x->ne[0];
|
||||
int64_t n_head = x->ne[1];
|
||||
int64_t L = x->ne[2];
|
||||
int64_t N = x->ne[3];
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 0, 2, 1, 3)); // [N, n_head, L, d_head]
|
||||
if (rope_interleaved) {
|
||||
x = ggml_reshape_4d(ctx, x, 2, d_head / 2, L, n_head * N); // [N * n_head, L, d_head/2, 2]
|
||||
x = ggml_cont(ctx, ggml_permute(ctx, x, 3, 0, 1, 2)); // [2, N * n_head, L, d_head/2]
|
||||
} else {
|
||||
x = ggml_reshape_4d(ctx, x, d_head / 2, 2, L, n_head * N); // [N * n_head, L, 2, d_head/2]
|
||||
x = ggml_cont(ctx, ggml_torch_permute(ctx, x, 0, 2, 3, 1)); // [2, N * n_head, L, d_head/2]
|
||||
}
|
||||
|
||||
int64_t offset = x->nb[2] * x->ne[2];
|
||||
auto x_0 = ggml_view_3d(ctx, x, x->ne[0], x->ne[1], x->ne[2], x->nb[1], x->nb[2], offset * 0); // [N * n_head, L, d_head/2]
|
||||
auto x_1 = ggml_view_3d(ctx, x, x->ne[0], x->ne[1], x->ne[2], x->nb[1], x->nb[2], offset * 1); // [N * n_head, L, d_head/2]
|
||||
x_0 = ggml_reshape_4d(ctx, x_0, 1, x_0->ne[0], x_0->ne[1], x_0->ne[2]); // [N * n_head, L, d_head/2, 1]
|
||||
x_1 = ggml_reshape_4d(ctx, x_1, 1, x_1->ne[0], x_1->ne[1], x_1->ne[2]); // [N * n_head, L, d_head/2, 1]
|
||||
auto temp_x = ggml_new_tensor_4d(ctx, x_0->type, 2, x_0->ne[1], x_0->ne[2], x_0->ne[3]);
|
||||
x_0 = ggml_repeat(ctx, x_0, temp_x); // [N * n_head, L, d_head/2, 2]
|
||||
x_1 = ggml_repeat(ctx, x_1, temp_x); // [N * n_head, L, d_head/2, 2]
|
||||
|
||||
pe = ggml_cont(ctx, ggml_permute(ctx, pe, 3, 0, 1, 2)); // [2, L, d_head/2, 2]
|
||||
offset = pe->nb[2] * pe->ne[2];
|
||||
auto pe_0 = ggml_view_3d(ctx, pe, pe->ne[0], pe->ne[1], pe->ne[2], pe->nb[1], pe->nb[2], offset * 0); // [L, d_head/2, 2]
|
||||
auto pe_1 = ggml_view_3d(ctx, pe, pe->ne[0], pe->ne[1], pe->ne[2], pe->nb[1], pe->nb[2], offset * 1); // [L, d_head/2, 2]
|
||||
|
||||
auto x_out = ggml_add_inplace(ctx, ggml_mul(ctx, x_0, pe_0), ggml_mul(ctx, x_1, pe_1)); // [N * n_head, L, d_head/2, 2]
|
||||
if (!rope_interleaved) {
|
||||
x_out = ggml_cont(ctx, ggml_permute(ctx, x_out, 1, 0, 2, 3)); // [N * n_head, L, x, d_head/2]
|
||||
}
|
||||
x_out = ggml_reshape_3d(ctx, x_out, d_head, L, n_head * N); // [N*n_head, L, d_head]
|
||||
return x_out;
|
||||
}
|
||||
|
||||
__STATIC_INLINE__ struct ggml_tensor* attention(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* q,
|
||||
struct ggml_tensor* k,
|
||||
struct ggml_tensor* v,
|
||||
struct ggml_tensor* pe,
|
||||
struct ggml_tensor* mask,
|
||||
bool flash_attn,
|
||||
float kv_scale = 1.0f,
|
||||
bool rope_interleaved = true) {
|
||||
// q,k,v: [N, L, n_head, d_head]
|
||||
// pe: [L, d_head/2, 2, 2]
|
||||
// return: [N, L, n_head*d_head]
|
||||
q = apply_rope(ctx, q, pe, rope_interleaved); // [N*n_head, L, d_head]
|
||||
k = apply_rope(ctx, k, pe, rope_interleaved); // [N*n_head, L, d_head]
|
||||
|
||||
auto x = ggml_nn_attention_ext(ctx, backend, q, k, v, v->ne[1], mask, false, true, flash_attn, kv_scale); // [N, L, n_head*d_head]
|
||||
return x;
|
||||
}
|
||||
}; // namespace Rope
|
||||
|
||||
#endif // __ROPE_HPP__
|
||||
+2149
-965
File diff suppressed because it is too large
Load Diff
+222
-139
@@ -30,11 +30,12 @@ extern "C" {
|
||||
|
||||
enum rng_type_t {
|
||||
STD_DEFAULT_RNG,
|
||||
CUDA_RNG
|
||||
CUDA_RNG,
|
||||
RNG_TYPE_COUNT
|
||||
};
|
||||
|
||||
enum sample_method_t {
|
||||
EULER_A,
|
||||
SAMPLE_METHOD_DEFAULT,
|
||||
EULER,
|
||||
HEUN,
|
||||
DPM2,
|
||||
@@ -44,17 +45,33 @@ enum sample_method_t {
|
||||
IPNDM,
|
||||
IPNDM_V,
|
||||
LCM,
|
||||
N_SAMPLE_METHODS
|
||||
DDIM_TRAILING,
|
||||
TCD,
|
||||
EULER_A,
|
||||
SAMPLE_METHOD_COUNT
|
||||
};
|
||||
|
||||
enum schedule_t {
|
||||
enum scheduler_t {
|
||||
DEFAULT,
|
||||
DISCRETE,
|
||||
KARRAS,
|
||||
EXPONENTIAL,
|
||||
AYS,
|
||||
GITS,
|
||||
N_SCHEDULES
|
||||
SGM_UNIFORM,
|
||||
SIMPLE,
|
||||
SMOOTHSTEP,
|
||||
SCHEDULE_COUNT
|
||||
};
|
||||
|
||||
enum prediction_t {
|
||||
DEFAULT_PRED,
|
||||
EPS_PRED,
|
||||
V_PRED,
|
||||
EDM_V_PRED,
|
||||
SD3_FLOW_PRED,
|
||||
FLUX_FLOW_PRED,
|
||||
PREDICTION_COUNT
|
||||
};
|
||||
|
||||
// same as enum ggml_type
|
||||
@@ -65,41 +82,43 @@ enum sd_type_t {
|
||||
SD_TYPE_Q4_1 = 3,
|
||||
// SD_TYPE_Q4_2 = 4, support has been removed
|
||||
// SD_TYPE_Q4_3 = 5, support has been removed
|
||||
SD_TYPE_Q5_0 = 6,
|
||||
SD_TYPE_Q5_1 = 7,
|
||||
SD_TYPE_Q8_0 = 8,
|
||||
SD_TYPE_Q8_1 = 9,
|
||||
SD_TYPE_Q2_K = 10,
|
||||
SD_TYPE_Q3_K = 11,
|
||||
SD_TYPE_Q4_K = 12,
|
||||
SD_TYPE_Q5_K = 13,
|
||||
SD_TYPE_Q6_K = 14,
|
||||
SD_TYPE_Q8_K = 15,
|
||||
SD_TYPE_IQ2_XXS = 16,
|
||||
SD_TYPE_IQ2_XS = 17,
|
||||
SD_TYPE_IQ3_XXS = 18,
|
||||
SD_TYPE_IQ1_S = 19,
|
||||
SD_TYPE_IQ4_NL = 20,
|
||||
SD_TYPE_IQ3_S = 21,
|
||||
SD_TYPE_IQ2_S = 22,
|
||||
SD_TYPE_IQ4_XS = 23,
|
||||
SD_TYPE_I8 = 24,
|
||||
SD_TYPE_I16 = 25,
|
||||
SD_TYPE_I32 = 26,
|
||||
SD_TYPE_I64 = 27,
|
||||
SD_TYPE_F64 = 28,
|
||||
SD_TYPE_IQ1_M = 29,
|
||||
SD_TYPE_BF16 = 30,
|
||||
SD_TYPE_Q4_0_4_4 = 31,
|
||||
SD_TYPE_Q4_0_4_8 = 32,
|
||||
SD_TYPE_Q4_0_8_8 = 33,
|
||||
SD_TYPE_TQ1_0 = 34,
|
||||
SD_TYPE_TQ2_0 = 35,
|
||||
SD_TYPE_COUNT,
|
||||
SD_TYPE_Q5_0 = 6,
|
||||
SD_TYPE_Q5_1 = 7,
|
||||
SD_TYPE_Q8_0 = 8,
|
||||
SD_TYPE_Q8_1 = 9,
|
||||
SD_TYPE_Q2_K = 10,
|
||||
SD_TYPE_Q3_K = 11,
|
||||
SD_TYPE_Q4_K = 12,
|
||||
SD_TYPE_Q5_K = 13,
|
||||
SD_TYPE_Q6_K = 14,
|
||||
SD_TYPE_Q8_K = 15,
|
||||
SD_TYPE_IQ2_XXS = 16,
|
||||
SD_TYPE_IQ2_XS = 17,
|
||||
SD_TYPE_IQ3_XXS = 18,
|
||||
SD_TYPE_IQ1_S = 19,
|
||||
SD_TYPE_IQ4_NL = 20,
|
||||
SD_TYPE_IQ3_S = 21,
|
||||
SD_TYPE_IQ2_S = 22,
|
||||
SD_TYPE_IQ4_XS = 23,
|
||||
SD_TYPE_I8 = 24,
|
||||
SD_TYPE_I16 = 25,
|
||||
SD_TYPE_I32 = 26,
|
||||
SD_TYPE_I64 = 27,
|
||||
SD_TYPE_F64 = 28,
|
||||
SD_TYPE_IQ1_M = 29,
|
||||
SD_TYPE_BF16 = 30,
|
||||
// SD_TYPE_Q4_0_4_4 = 31, support has been removed from gguf files
|
||||
// SD_TYPE_Q4_0_4_8 = 32,
|
||||
// SD_TYPE_Q4_0_8_8 = 33,
|
||||
SD_TYPE_TQ1_0 = 34,
|
||||
SD_TYPE_TQ2_0 = 35,
|
||||
// SD_TYPE_IQ4_NL_4_4 = 36,
|
||||
// SD_TYPE_IQ4_NL_4_8 = 37,
|
||||
// SD_TYPE_IQ4_NL_8_8 = 38,
|
||||
SD_TYPE_MXFP4 = 39, // MXFP4 (1 block)
|
||||
SD_TYPE_COUNT = 40,
|
||||
};
|
||||
|
||||
SD_API const char* sd_type_name(enum sd_type_t type);
|
||||
|
||||
enum sd_log_level_t {
|
||||
SD_LOG_DEBUG,
|
||||
SD_LOG_INFO,
|
||||
@@ -107,6 +126,132 @@ enum sd_log_level_t {
|
||||
SD_LOG_ERROR
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
int tile_size_x;
|
||||
int tile_size_y;
|
||||
float target_overlap;
|
||||
float rel_size_x;
|
||||
float rel_size_y;
|
||||
} sd_tiling_params_t;
|
||||
|
||||
typedef struct {
|
||||
const char* model_path;
|
||||
const char* clip_l_path;
|
||||
const char* clip_g_path;
|
||||
const char* clip_vision_path;
|
||||
const char* t5xxl_path;
|
||||
const char* qwen2vl_path;
|
||||
const char* qwen2vl_vision_path;
|
||||
const char* diffusion_model_path;
|
||||
const char* high_noise_diffusion_model_path;
|
||||
const char* vae_path;
|
||||
const char* taesd_path;
|
||||
const char* control_net_path;
|
||||
const char* lora_model_dir;
|
||||
const char* embedding_dir;
|
||||
const char* photo_maker_path;
|
||||
bool vae_decode_only;
|
||||
bool free_params_immediately;
|
||||
int n_threads;
|
||||
enum sd_type_t wtype;
|
||||
enum rng_type_t rng_type;
|
||||
enum prediction_t prediction;
|
||||
bool offload_params_to_cpu;
|
||||
bool keep_clip_on_cpu;
|
||||
bool keep_control_net_on_cpu;
|
||||
bool keep_vae_on_cpu;
|
||||
bool diffusion_flash_attn;
|
||||
bool diffusion_conv_direct;
|
||||
bool vae_conv_direct;
|
||||
bool force_sdxl_vae_conv_scale;
|
||||
bool chroma_use_dit_mask;
|
||||
bool chroma_use_t5_mask;
|
||||
int chroma_t5_mask_pad;
|
||||
float flow_shift;
|
||||
} sd_ctx_params_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t channel;
|
||||
uint8_t* data;
|
||||
} sd_image_t;
|
||||
|
||||
typedef struct {
|
||||
int* layers;
|
||||
size_t layer_count;
|
||||
float layer_start;
|
||||
float layer_end;
|
||||
float scale;
|
||||
} sd_slg_params_t;
|
||||
|
||||
typedef struct {
|
||||
float txt_cfg;
|
||||
float img_cfg;
|
||||
float distilled_guidance;
|
||||
sd_slg_params_t slg;
|
||||
} sd_guidance_params_t;
|
||||
|
||||
typedef struct {
|
||||
sd_guidance_params_t guidance;
|
||||
enum scheduler_t scheduler;
|
||||
enum sample_method_t sample_method;
|
||||
int sample_steps;
|
||||
float eta;
|
||||
int shifted_timestep;
|
||||
} sd_sample_params_t;
|
||||
|
||||
typedef struct {
|
||||
sd_image_t* id_images;
|
||||
int id_images_count;
|
||||
const char* id_embed_path;
|
||||
float style_strength;
|
||||
} sd_pm_params_t; // photo maker
|
||||
|
||||
typedef struct {
|
||||
const char* prompt;
|
||||
const char* negative_prompt;
|
||||
int clip_skip;
|
||||
sd_image_t init_image;
|
||||
sd_image_t* ref_images;
|
||||
int ref_images_count;
|
||||
bool auto_resize_ref_image;
|
||||
bool increase_ref_index;
|
||||
sd_image_t mask_image;
|
||||
int width;
|
||||
int height;
|
||||
sd_sample_params_t sample_params;
|
||||
float strength;
|
||||
int64_t seed;
|
||||
int batch_count;
|
||||
sd_image_t control_image;
|
||||
float control_strength;
|
||||
sd_pm_params_t pm_params;
|
||||
sd_tiling_params_t vae_tiling_params;
|
||||
} sd_img_gen_params_t;
|
||||
|
||||
typedef struct {
|
||||
const char* prompt;
|
||||
const char* negative_prompt;
|
||||
int clip_skip;
|
||||
sd_image_t init_image;
|
||||
sd_image_t end_image;
|
||||
sd_image_t* control_frames;
|
||||
int control_frames_size;
|
||||
int width;
|
||||
int height;
|
||||
sd_sample_params_t sample_params;
|
||||
sd_sample_params_t high_noise_sample_params;
|
||||
float moe_boundary;
|
||||
float strength;
|
||||
int64_t seed;
|
||||
int video_frames;
|
||||
float vace_strength;
|
||||
} sd_vid_gen_params_t;
|
||||
|
||||
typedef struct sd_ctx_t sd_ctx_t;
|
||||
|
||||
typedef void (*sd_log_cb_t)(enum sd_log_level_t level, const char* text, void* data);
|
||||
typedef void (*sd_progress_cb_t)(int step, int steps, float time, void* data);
|
||||
|
||||
@@ -115,122 +260,60 @@ SD_API void sd_set_progress_callback(sd_progress_cb_t cb, void* data);
|
||||
SD_API int32_t get_num_physical_cores();
|
||||
SD_API const char* sd_get_system_info();
|
||||
|
||||
typedef struct {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t channel;
|
||||
uint8_t* data;
|
||||
} sd_image_t;
|
||||
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 const char* sd_rng_type_name(enum rng_type_t rng_type);
|
||||
SD_API enum rng_type_t str_to_rng_type(const char* str);
|
||||
SD_API const char* sd_sample_method_name(enum sample_method_t sample_method);
|
||||
SD_API enum sample_method_t str_to_sample_method(const char* str);
|
||||
SD_API const char* sd_schedule_name(enum scheduler_t scheduler);
|
||||
SD_API enum scheduler_t str_to_schedule(const char* str);
|
||||
SD_API const char* sd_prediction_name(enum prediction_t prediction);
|
||||
SD_API enum prediction_t str_to_prediction(const char* str);
|
||||
|
||||
typedef struct sd_ctx_t sd_ctx_t;
|
||||
|
||||
SD_API sd_ctx_t* new_sd_ctx(const char* model_path,
|
||||
const char* clip_l_path,
|
||||
const char* clip_g_path,
|
||||
const char* t5xxl_path,
|
||||
const char* diffusion_model_path,
|
||||
const char* vae_path,
|
||||
const char* taesd_path,
|
||||
const char* control_net_path_c_str,
|
||||
const char* lora_model_dir,
|
||||
const char* embed_dir_c_str,
|
||||
const char* stacked_id_embed_dir_c_str,
|
||||
bool vae_decode_only,
|
||||
bool vae_tiling,
|
||||
bool free_params_immediately,
|
||||
int n_threads,
|
||||
enum sd_type_t wtype,
|
||||
enum rng_type_t rng_type,
|
||||
enum schedule_t s,
|
||||
bool keep_clip_on_cpu,
|
||||
bool keep_control_net_cpu,
|
||||
bool keep_vae_on_cpu,
|
||||
bool diffusion_flash_attn);
|
||||
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 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 enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx);
|
||||
|
||||
SD_API sd_image_t* txt2img(sd_ctx_t* sd_ctx,
|
||||
const char* prompt,
|
||||
const char* negative_prompt,
|
||||
int clip_skip,
|
||||
float cfg_scale,
|
||||
float guidance,
|
||||
int width,
|
||||
int height,
|
||||
enum sample_method_t sample_method,
|
||||
int sample_steps,
|
||||
int64_t seed,
|
||||
int batch_count,
|
||||
const sd_image_t* control_cond,
|
||||
float control_strength,
|
||||
float style_strength,
|
||||
bool normalize_input,
|
||||
const char* input_id_images_path,
|
||||
int* skip_layers,
|
||||
size_t skip_layers_count,
|
||||
float slg_scale,
|
||||
float skip_layer_start,
|
||||
float skip_layer_end);
|
||||
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 sd_image_t* img2img(sd_ctx_t* sd_ctx,
|
||||
sd_image_t init_image,
|
||||
sd_image_t mask_image,
|
||||
const char* prompt,
|
||||
const char* negative_prompt,
|
||||
int clip_skip,
|
||||
float cfg_scale,
|
||||
float guidance,
|
||||
int width,
|
||||
int height,
|
||||
enum sample_method_t sample_method,
|
||||
int sample_steps,
|
||||
float strength,
|
||||
int64_t seed,
|
||||
int batch_count,
|
||||
const sd_image_t* control_cond,
|
||||
float control_strength,
|
||||
float style_strength,
|
||||
bool normalize_input,
|
||||
const char* input_id_images_path,
|
||||
int* skip_layers,
|
||||
size_t skip_layers_count,
|
||||
float slg_scale,
|
||||
float skip_layer_start,
|
||||
float skip_layer_end);
|
||||
SD_API void sd_img_gen_params_init(sd_img_gen_params_t* sd_img_gen_params);
|
||||
SD_API char* sd_img_gen_params_to_str(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 sd_image_t* img2vid(sd_ctx_t* sd_ctx,
|
||||
sd_image_t init_image,
|
||||
int width,
|
||||
int height,
|
||||
int video_frames,
|
||||
int motion_bucket_id,
|
||||
int fps,
|
||||
float augmentation_level,
|
||||
float min_cfg,
|
||||
float cfg_scale,
|
||||
enum sample_method_t sample_method,
|
||||
int sample_steps,
|
||||
float strength,
|
||||
int64_t seed);
|
||||
SD_API void sd_vid_gen_params_init(sd_vid_gen_params_t* sd_vid_gen_params);
|
||||
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);
|
||||
|
||||
typedef struct upscaler_ctx_t upscaler_ctx_t;
|
||||
|
||||
SD_API upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads);
|
||||
SD_API void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
|
||||
|
||||
SD_API sd_image_t upscale(upscaler_ctx_t* upscaler_ctx, sd_image_t input_image, uint32_t upscale_factor);
|
||||
SD_API sd_image_t upscale(upscaler_ctx_t* upscaler_ctx,
|
||||
sd_image_t input_image,
|
||||
uint32_t upscale_factor);
|
||||
|
||||
SD_API bool convert(const char* input_path, const char* vae_path, const char* output_path, enum sd_type_t output_type);
|
||||
SD_API int get_upscale_factor(upscaler_ctx_t* upscaler_ctx);
|
||||
|
||||
SD_API uint8_t* preprocess_canny(uint8_t* img,
|
||||
int width,
|
||||
int height,
|
||||
float high_threshold,
|
||||
float low_threshold,
|
||||
float weak,
|
||||
float strong,
|
||||
bool inverse);
|
||||
SD_API bool convert(const char* input_path,
|
||||
const char* vae_path,
|
||||
const char* output_path,
|
||||
enum sd_type_t output_type,
|
||||
const char* tensor_type_rules);
|
||||
|
||||
SD_API bool preprocess_canny(sd_image_t image,
|
||||
float high_threshold,
|
||||
float low_threshold,
|
||||
float weak,
|
||||
float strong,
|
||||
bool inverse);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -124,7 +124,10 @@ protected:
|
||||
return;
|
||||
}
|
||||
std::string piece = item[0];
|
||||
float score = item[1];
|
||||
if (piece.empty()) {
|
||||
piece = "<empty_token>";
|
||||
}
|
||||
float score = item[1];
|
||||
piece_score_pairs.emplace_back(piece, score);
|
||||
}
|
||||
}
|
||||
@@ -147,6 +150,7 @@ protected:
|
||||
std::vector<const char*> key(pieces->size());
|
||||
std::vector<int> value(pieces->size());
|
||||
for (size_t i = 0; i < pieces->size(); ++i) {
|
||||
// LOG_DEBUG("%s %d", (*pieces)[i].first.c_str(), (*pieces)[i].second);
|
||||
key[i] = (*pieces)[i].first.data(); // sorted piece.
|
||||
value[i] = (*pieces)[i].second; // vocab_id
|
||||
}
|
||||
@@ -335,9 +339,9 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
explicit T5UniGramTokenizer(const std::string& json_str = "") {
|
||||
if (json_str.size() != 0) {
|
||||
InitializePieces(json_str);
|
||||
explicit T5UniGramTokenizer(bool is_umt5 = false) {
|
||||
if (is_umt5) {
|
||||
InitializePieces(ModelLoader::load_umt5_tokenizer_json());
|
||||
} else {
|
||||
InitializePieces(ModelLoader::load_t5_tokenizer_json());
|
||||
}
|
||||
@@ -385,6 +389,7 @@ public:
|
||||
|
||||
void pad_tokens(std::vector<int>& tokens,
|
||||
std::vector<float>& weights,
|
||||
std::vector<float>* attention_mask,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
if (max_length > 0 && padding) {
|
||||
@@ -397,11 +402,15 @@ public:
|
||||
LOG_DEBUG("token length: %llu", length);
|
||||
std::vector<int> new_tokens;
|
||||
std::vector<float> new_weights;
|
||||
std::vector<float> new_attention_mask;
|
||||
int token_idx = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (token_idx >= orig_token_num) {
|
||||
break;
|
||||
}
|
||||
if (attention_mask != nullptr) {
|
||||
new_attention_mask.push_back(0.0);
|
||||
}
|
||||
if (i % max_length == max_length - 1) {
|
||||
new_tokens.push_back(eos_id_);
|
||||
new_weights.push_back(1.0);
|
||||
@@ -414,13 +423,24 @@ public:
|
||||
|
||||
new_tokens.push_back(eos_id_);
|
||||
new_weights.push_back(1.0);
|
||||
if (attention_mask != nullptr) {
|
||||
new_attention_mask.push_back(0.0);
|
||||
}
|
||||
|
||||
tokens = new_tokens;
|
||||
weights = new_weights;
|
||||
if (attention_mask != nullptr) {
|
||||
*attention_mask = new_attention_mask;
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
int pad_token_id = pad_id_;
|
||||
tokens.insert(tokens.end(), length - tokens.size(), pad_token_id);
|
||||
weights.insert(weights.end(), length - weights.size(), 1.0);
|
||||
if (attention_mask != nullptr) {
|
||||
// maybe keep some padding tokens unmasked?
|
||||
attention_mask->insert(attention_mask->end(), length - attention_mask->size(), -HUGE_VALF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,8 +461,8 @@ protected:
|
||||
int64_t hidden_size;
|
||||
float eps;
|
||||
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
enum ggml_type wtype = GGML_TYPE_F32; //(tensor_types.find(prefix + "weight") != tensor_types.end()) ? tensor_types[prefix + "weight"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
enum ggml_type wtype = GGML_TYPE_F32;
|
||||
params["weight"] = ggml_new_tensor_1d(ctx, wtype, hidden_size);
|
||||
}
|
||||
|
||||
@@ -484,7 +504,9 @@ public:
|
||||
T5DenseGatedActDense(int64_t model_dim, int64_t ff_dim) {
|
||||
blocks["wi_0"] = std::shared_ptr<GGMLBlock>(new Linear(model_dim, ff_dim, false));
|
||||
blocks["wi_1"] = std::shared_ptr<GGMLBlock>(new Linear(model_dim, ff_dim, false));
|
||||
blocks["wo"] = std::shared_ptr<GGMLBlock>(new Linear(ff_dim, model_dim, false));
|
||||
float scale = 1.f / 32.f;
|
||||
// The purpose of the scale here is to prevent NaN issues on some backends(CUDA, ...).
|
||||
blocks["wo"] = std::shared_ptr<GGMLBlock>(new Linear(ff_dim, model_dim, false, false, false, scale));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx, struct ggml_tensor* x) {
|
||||
@@ -558,6 +580,7 @@ public:
|
||||
|
||||
// x: [N, n_token, model_dim]
|
||||
std::pair<struct ggml_tensor*, struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* past_bias = NULL,
|
||||
struct ggml_tensor* mask = NULL,
|
||||
@@ -579,6 +602,7 @@ public:
|
||||
}
|
||||
if (past_bias != NULL) {
|
||||
if (mask != NULL) {
|
||||
mask = ggml_repeat(ctx, mask, past_bias);
|
||||
mask = ggml_add(ctx, mask, past_bias);
|
||||
} else {
|
||||
mask = past_bias;
|
||||
@@ -587,7 +611,7 @@ public:
|
||||
|
||||
k = ggml_scale_inplace(ctx, k, sqrt(d_head));
|
||||
|
||||
x = ggml_nn_attention_ext(ctx, q, k, v, num_heads, mask); // [N, n_token, d_head * n_head]
|
||||
x = ggml_nn_attention_ext(ctx, backend, q, k, v, num_heads, mask); // [N, n_token, d_head * n_head]
|
||||
|
||||
x = out_proj->forward(ctx, x); // [N, n_token, model_dim]
|
||||
return {x, past_bias};
|
||||
@@ -606,6 +630,7 @@ public:
|
||||
}
|
||||
|
||||
std::pair<struct ggml_tensor*, struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* past_bias = NULL,
|
||||
struct ggml_tensor* mask = NULL,
|
||||
@@ -615,7 +640,7 @@ public:
|
||||
auto layer_norm = std::dynamic_pointer_cast<T5LayerNorm>(blocks["layer_norm"]);
|
||||
|
||||
auto normed_hidden_state = layer_norm->forward(ctx, x);
|
||||
auto ret = SelfAttention->forward(ctx, normed_hidden_state, past_bias, mask, relative_position_bucket);
|
||||
auto ret = SelfAttention->forward(ctx, backend, normed_hidden_state, past_bias, mask, relative_position_bucket);
|
||||
auto output = ret.first;
|
||||
past_bias = ret.second;
|
||||
|
||||
@@ -632,6 +657,7 @@ public:
|
||||
}
|
||||
|
||||
std::pair<struct ggml_tensor*, struct ggml_tensor*> forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* past_bias = NULL,
|
||||
struct ggml_tensor* mask = NULL,
|
||||
@@ -640,7 +666,7 @@ public:
|
||||
auto layer_0 = std::dynamic_pointer_cast<T5LayerSelfAttention>(blocks["layer.0"]);
|
||||
auto layer_1 = std::dynamic_pointer_cast<T5LayerFF>(blocks["layer.1"]);
|
||||
|
||||
auto ret = layer_0->forward(ctx, x, past_bias, mask, relative_position_bucket);
|
||||
auto ret = layer_0->forward(ctx, backend, x, past_bias, mask, relative_position_bucket);
|
||||
x = ret.first;
|
||||
past_bias = ret.second;
|
||||
x = layer_1->forward(ctx, x);
|
||||
@@ -656,16 +682,18 @@ public:
|
||||
int64_t model_dim,
|
||||
int64_t inner_dim,
|
||||
int64_t ff_dim,
|
||||
int64_t num_heads)
|
||||
int64_t num_heads,
|
||||
bool relative_attention = true)
|
||||
: num_layers(num_layers) {
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
blocks["block." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new T5Block(model_dim, inner_dim, ff_dim, num_heads, i == 0));
|
||||
blocks["block." + std::to_string(i)] = std::shared_ptr<GGMLBlock>(new T5Block(model_dim, inner_dim, ff_dim, num_heads, (!relative_attention || i == 0)));
|
||||
}
|
||||
|
||||
blocks["final_layer_norm"] = std::shared_ptr<GGMLBlock>(new T5LayerNorm(model_dim));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* past_bias = NULL,
|
||||
struct ggml_tensor* attention_mask = NULL,
|
||||
@@ -674,7 +702,7 @@ public:
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
auto block = std::dynamic_pointer_cast<T5Block>(blocks["block." + std::to_string(i)]);
|
||||
|
||||
auto ret = block->forward(ctx, x, past_bias, attention_mask, relative_position_bucket);
|
||||
auto ret = block->forward(ctx, backend, x, past_bias, attention_mask, relative_position_bucket);
|
||||
x = ret.first;
|
||||
past_bias = ret.second;
|
||||
}
|
||||
@@ -686,18 +714,34 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct T5Params {
|
||||
int64_t num_layers = 24;
|
||||
int64_t model_dim = 4096;
|
||||
int64_t ff_dim = 10240;
|
||||
int64_t num_heads = 64;
|
||||
int64_t vocab_size = 32128;
|
||||
bool relative_attention = true;
|
||||
};
|
||||
|
||||
struct T5 : public GGMLBlock {
|
||||
T5Params params;
|
||||
|
||||
public:
|
||||
T5(int64_t num_layers,
|
||||
int64_t model_dim,
|
||||
int64_t ff_dim,
|
||||
int64_t num_heads,
|
||||
int64_t vocab_size) {
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new T5Stack(num_layers, model_dim, model_dim, ff_dim, num_heads));
|
||||
blocks["shared"] = std::shared_ptr<GGMLBlock>(new Embedding(vocab_size, model_dim));
|
||||
T5() {}
|
||||
T5(T5Params params)
|
||||
: params(params) {
|
||||
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new T5Stack(params.num_layers,
|
||||
params.model_dim,
|
||||
params.model_dim,
|
||||
params.ff_dim,
|
||||
params.num_heads,
|
||||
params.relative_attention));
|
||||
blocks["shared"] = std::shared_ptr<GGMLBlock>(new Embedding(params.vocab_size,
|
||||
params.model_dim));
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* past_bias = NULL,
|
||||
struct ggml_tensor* attention_mask = NULL,
|
||||
@@ -708,24 +752,27 @@ public:
|
||||
auto encoder = std::dynamic_pointer_cast<T5Stack>(blocks["encoder"]);
|
||||
|
||||
auto x = shared->forward(ctx, input_ids);
|
||||
x = encoder->forward(ctx, x, past_bias, attention_mask, relative_position_bucket);
|
||||
x = encoder->forward(ctx, backend, x, past_bias, attention_mask, relative_position_bucket);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
struct T5Runner : public GGMLRunner {
|
||||
T5Params params;
|
||||
T5 model;
|
||||
std::vector<int> relative_position_bucket_vec;
|
||||
|
||||
T5Runner(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
int64_t num_layers = 24,
|
||||
int64_t model_dim = 4096,
|
||||
int64_t ff_dim = 10240,
|
||||
int64_t num_heads = 64,
|
||||
int64_t vocab_size = 32128)
|
||||
: GGMLRunner(backend), model(num_layers, model_dim, ff_dim, num_heads, vocab_size) {
|
||||
bool is_umt5 = false)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {
|
||||
if (is_umt5) {
|
||||
params.vocab_size = 256384;
|
||||
params.relative_attention = false;
|
||||
}
|
||||
model = T5(params);
|
||||
model.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
@@ -738,19 +785,23 @@ struct T5Runner : public GGMLRunner {
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* relative_position_bucket) {
|
||||
struct ggml_tensor* relative_position_bucket,
|
||||
struct ggml_tensor* attention_mask = NULL) {
|
||||
size_t N = input_ids->ne[1];
|
||||
size_t n_token = input_ids->ne[0];
|
||||
|
||||
auto hidden_states = model.forward(ctx, input_ids, NULL, NULL, relative_position_bucket); // [N, n_token, model_dim]
|
||||
auto hidden_states = model.forward(ctx, backend, input_ids, NULL, attention_mask, relative_position_bucket); // [N, n_token, model_dim]
|
||||
return hidden_states;
|
||||
}
|
||||
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* input_ids) {
|
||||
struct ggml_cgraph* build_graph(struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* attention_mask = NULL) {
|
||||
struct ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
||||
|
||||
input_ids = to_backend(input_ids);
|
||||
input_ids = to_backend(input_ids);
|
||||
attention_mask = to_backend(attention_mask);
|
||||
|
||||
relative_position_bucket_vec = compute_relative_position_bucket(input_ids->ne[0], input_ids->ne[0]);
|
||||
|
||||
@@ -767,7 +818,7 @@ struct T5Runner : public GGMLRunner {
|
||||
input_ids->ne[0]);
|
||||
set_backend_tensor_data(relative_position_bucket, relative_position_bucket_vec.data());
|
||||
|
||||
struct ggml_tensor* hidden_states = forward(compute_ctx, input_ids, relative_position_bucket);
|
||||
struct ggml_tensor* hidden_states = forward(compute_ctx, runtime_backend, input_ids, relative_position_bucket, attention_mask);
|
||||
|
||||
ggml_build_forward_expand(gf, hidden_states);
|
||||
|
||||
@@ -776,10 +827,11 @@ struct T5Runner : public GGMLRunner {
|
||||
|
||||
void compute(const int n_threads,
|
||||
struct ggml_tensor* input_ids,
|
||||
struct ggml_tensor* attention_mask,
|
||||
ggml_tensor** output,
|
||||
ggml_context* output_ctx = NULL) {
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(input_ids);
|
||||
return build_graph(input_ids, attention_mask);
|
||||
};
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
}
|
||||
@@ -856,17 +908,12 @@ struct T5Embedder {
|
||||
T5UniGramTokenizer tokenizer;
|
||||
T5Runner model;
|
||||
|
||||
static std::map<std::string, enum ggml_type> empty_tensor_types;
|
||||
|
||||
T5Embedder(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types = empty_tensor_types,
|
||||
const std::string prefix = "",
|
||||
int64_t num_layers = 24,
|
||||
int64_t model_dim = 4096,
|
||||
int64_t ff_dim = 10240,
|
||||
int64_t num_heads = 64,
|
||||
int64_t vocab_size = 32128)
|
||||
: model(backend, tensor_types, prefix, num_layers, model_dim, ff_dim, num_heads, vocab_size) {
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types = {},
|
||||
const std::string prefix = "",
|
||||
bool is_umt5 = false)
|
||||
: model(backend, offload_params_to_cpu, tensor_types, prefix, is_umt5), tokenizer(is_umt5) {
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) {
|
||||
@@ -877,9 +924,9 @@ struct T5Embedder {
|
||||
model.alloc_params_buffer();
|
||||
}
|
||||
|
||||
std::pair<std::vector<int>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
std::tuple<std::vector<int>, std::vector<float>, std::vector<float>> tokenize(std::string text,
|
||||
size_t max_length = 0,
|
||||
bool padding = false) {
|
||||
auto parsed_attention = parse_prompt_attention(text);
|
||||
|
||||
{
|
||||
@@ -906,14 +953,16 @@ struct T5Embedder {
|
||||
tokens.push_back(EOS_TOKEN_ID);
|
||||
weights.push_back(1.0);
|
||||
|
||||
tokenizer.pad_tokens(tokens, weights, max_length, padding);
|
||||
std::vector<float> attention_mask;
|
||||
|
||||
tokenizer.pad_tokens(tokens, weights, &attention_mask, max_length, padding);
|
||||
|
||||
// for (int i = 0; i < tokens.size(); i++) {
|
||||
// std::cout << tokens[i] << ":" << weights[i] << ", ";
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
|
||||
return {tokens, weights};
|
||||
return {tokens, weights, attention_mask};
|
||||
}
|
||||
|
||||
void test() {
|
||||
@@ -926,25 +975,22 @@ struct T5Embedder {
|
||||
GGML_ASSERT(work_ctx != NULL);
|
||||
|
||||
{
|
||||
// cpu f16: pass
|
||||
// cpu f32: pass
|
||||
// cuda f16: nan
|
||||
// cuda f32: pass
|
||||
// cuda q8_0: nan
|
||||
// TODO: fix cuda nan
|
||||
std::string text("a lovely cat");
|
||||
auto tokens_and_weights = tokenize(text, 77, true);
|
||||
std::vector<int>& tokens = tokens_and_weights.first;
|
||||
std::vector<float>& weights = tokens_and_weights.second;
|
||||
// std::string text("一只可爱的猫"); // umt5 chinease test
|
||||
auto tokens_and_weights = tokenize(text, 512, true);
|
||||
std::vector<int>& tokens = std::get<0>(tokens_and_weights);
|
||||
std::vector<float>& weights = std::get<1>(tokens_and_weights);
|
||||
std::vector<float>& masks = std::get<2>(tokens_and_weights);
|
||||
for (auto token : tokens) {
|
||||
printf("%d ", token);
|
||||
}
|
||||
printf("\n");
|
||||
auto input_ids = vector_to_ggml_tensor_i32(work_ctx, tokens);
|
||||
auto attention_mask = vector_to_ggml_tensor(work_ctx, masks);
|
||||
struct ggml_tensor* out = NULL;
|
||||
|
||||
int t0 = ggml_time_ms();
|
||||
model.compute(8, input_ids, &out, work_ctx);
|
||||
model.compute(8, input_ids, attention_mask, &out, work_ctx);
|
||||
int t1 = ggml_time_ms();
|
||||
|
||||
print_ggml_tensor(out);
|
||||
@@ -953,32 +999,43 @@ struct T5Embedder {
|
||||
}
|
||||
|
||||
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_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F32;
|
||||
std::shared_ptr<T5Embedder> t5 = std::shared_ptr<T5Embedder>(new T5Embedder(backend));
|
||||
{
|
||||
LOG_INFO("loading from '%s'", file_path.c_str());
|
||||
// cpu f16: pass
|
||||
// cpu f32: pass
|
||||
// cuda f16: pass
|
||||
// cuda f32: pass
|
||||
// cuda q8_0: pass
|
||||
// ggml_backend_t backend = ggml_backend_cuda_init(0);
|
||||
ggml_backend_t backend = ggml_backend_cpu_init();
|
||||
ggml_type model_data_type = GGML_TYPE_F16;
|
||||
|
||||
t5->alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
t5->get_param_tensors(tensors, "");
|
||||
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(tensors, backend);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("t5 model loaded");
|
||||
ModelLoader model_loader;
|
||||
if (!model_loader.init_from_file(file_path)) {
|
||||
LOG_ERROR("init model loader from file failed: '%s'", file_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto tensor_types = model_loader.tensor_storages_types;
|
||||
for (auto& item : tensor_types) {
|
||||
// LOG_DEBUG("%s %u", item.first.c_str(), item.second);
|
||||
if (ends_with(item.first, "weight")) {
|
||||
item.second = model_data_type;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<T5Embedder> t5 = std::shared_ptr<T5Embedder>(new T5Embedder(backend, false, tensor_types, "", true));
|
||||
|
||||
t5->alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> tensors;
|
||||
t5->get_param_tensors(tensors, "");
|
||||
|
||||
bool success = model_loader.load_tensors(tensors);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tensors from model loader failed");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("t5 model loaded");
|
||||
t5->test();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
if (i == 1) {
|
||||
h = ggml_relu_inplace(ctx, h);
|
||||
} else {
|
||||
h = ggml_upscale(ctx, h, 2);
|
||||
h = ggml_upscale(ctx, h, 2, GGML_SCALE_MODE_NEAREST);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -196,21 +196,33 @@ struct TinyAutoEncoder : public GGMLRunner {
|
||||
bool decode_only = false;
|
||||
|
||||
TinyAutoEncoder(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
bool decoder_only = true,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decoder_only),
|
||||
taesd(decode_only, version),
|
||||
GGMLRunner(backend) {
|
||||
taesd(decoder_only, version),
|
||||
GGMLRunner(backend, offload_params_to_cpu) {
|
||||
taesd.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
void enable_conv2d_direct() {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
taesd.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->enable_direct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return "taesd";
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& file_path) {
|
||||
bool load_from_file(const std::string& file_path, int n_threads) {
|
||||
LOG_INFO("loading taesd from '%s', decode_only = %s", file_path.c_str(), decode_only ? "true" : "false");
|
||||
alloc_params_buffer();
|
||||
std::map<std::string, ggml_tensor*> taesd_tensors;
|
||||
@@ -226,7 +238,7 @@ struct TinyAutoEncoder : public GGMLRunner {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = model_loader.load_tensors(taesd_tensors, backend, ignore_tensors);
|
||||
bool success = model_loader.load_tensors(taesd_tensors, ignore_tensors, n_threads);
|
||||
|
||||
if (!success) {
|
||||
LOG_ERROR("load tae tensors from model loader failed");
|
||||
|
||||
Vendored
+3
@@ -4,6 +4,7 @@
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <new>
|
||||
#include <iostream>
|
||||
|
||||
#define DARTS_VERSION "0.32"
|
||||
|
||||
@@ -1140,9 +1141,11 @@ inline void DawgBuilder::insert(const char *key, std::size_t length,
|
||||
if (value < 0) {
|
||||
DARTS_THROW("failed to insert key: negative value");
|
||||
} else if (length == 0) {
|
||||
std::cout << value << std::endl;
|
||||
DARTS_THROW("failed to insert key: zero-length key");
|
||||
}
|
||||
|
||||
|
||||
id_type id = 0;
|
||||
std::size_t key_pos = 0;
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -177,7 +177,7 @@ STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const
|
||||
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
||||
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
|
||||
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
|
||||
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality);
|
||||
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality, const char* parameters = NULL);
|
||||
|
||||
#ifdef STBIW_WINDOWS_UTF8
|
||||
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
|
||||
|
||||
@@ -0,0 +1,985 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "tokenize_util.h"
|
||||
|
||||
bool is_number(char32_t ch) {
|
||||
return (ch >= U'0' && ch <= U'9');
|
||||
}
|
||||
|
||||
bool is_letter(char32_t ch) {
|
||||
static const struct { char32_t start, end; } ranges[] = {
|
||||
{0x41, 0x5A},
|
||||
{0x61, 0x7A},
|
||||
{0xAA, 0xAA},
|
||||
{0xB5, 0xB5},
|
||||
{0xBA, 0xBA},
|
||||
{0xC0, 0xD6},
|
||||
{0xD8, 0xF6},
|
||||
{0xF8, 0x2C1},
|
||||
{0x2C6, 0x2D1},
|
||||
{0x2E0, 0x2E4},
|
||||
{0x2EC, 0x2EC},
|
||||
{0x2EE, 0x2EE},
|
||||
{0x370, 0x374},
|
||||
{0x376, 0x377},
|
||||
{0x37A, 0x37D},
|
||||
{0x37F, 0x37F},
|
||||
{0x386, 0x386},
|
||||
{0x388, 0x38A},
|
||||
{0x38C, 0x38C},
|
||||
{0x38E, 0x3A1},
|
||||
{0x3A3, 0x3F5},
|
||||
{0x3F7, 0x481},
|
||||
{0x48A, 0x52F},
|
||||
{0x531, 0x556},
|
||||
{0x559, 0x559},
|
||||
{0x560, 0x588},
|
||||
{0x5D0, 0x5EA},
|
||||
{0x5EF, 0x5F2},
|
||||
{0x620, 0x64A},
|
||||
{0x66E, 0x66F},
|
||||
{0x671, 0x6D3},
|
||||
{0x6D5, 0x6D5},
|
||||
{0x6E5, 0x6E6},
|
||||
{0x6EE, 0x6EF},
|
||||
{0x6FA, 0x6FC},
|
||||
{0x6FF, 0x6FF},
|
||||
{0x710, 0x710},
|
||||
{0x712, 0x72F},
|
||||
{0x74D, 0x7A5},
|
||||
{0x7B1, 0x7B1},
|
||||
{0x7CA, 0x7EA},
|
||||
{0x7F4, 0x7F5},
|
||||
{0x7FA, 0x7FA},
|
||||
{0x800, 0x815},
|
||||
{0x81A, 0x81A},
|
||||
{0x824, 0x824},
|
||||
{0x828, 0x828},
|
||||
{0x840, 0x858},
|
||||
{0x860, 0x86A},
|
||||
{0x870, 0x887},
|
||||
{0x889, 0x88F},
|
||||
{0x8A0, 0x8C9},
|
||||
{0x904, 0x939},
|
||||
{0x93D, 0x93D},
|
||||
{0x950, 0x950},
|
||||
{0x958, 0x961},
|
||||
{0x971, 0x980},
|
||||
{0x985, 0x98C},
|
||||
{0x98F, 0x990},
|
||||
{0x993, 0x9A8},
|
||||
{0x9AA, 0x9B0},
|
||||
{0x9B2, 0x9B2},
|
||||
{0x9B6, 0x9B9},
|
||||
{0x9BD, 0x9BD},
|
||||
{0x9CE, 0x9CE},
|
||||
{0x9DC, 0x9DD},
|
||||
{0x9DF, 0x9E1},
|
||||
{0x9F0, 0x9F1},
|
||||
{0x9FC, 0x9FC},
|
||||
{0xA05, 0xA0A},
|
||||
{0xA0F, 0xA10},
|
||||
{0xA13, 0xA28},
|
||||
{0xA2A, 0xA30},
|
||||
{0xA32, 0xA33},
|
||||
{0xA35, 0xA36},
|
||||
{0xA38, 0xA39},
|
||||
{0xA59, 0xA5C},
|
||||
{0xA5E, 0xA5E},
|
||||
{0xA72, 0xA74},
|
||||
{0xA85, 0xA8D},
|
||||
{0xA8F, 0xA91},
|
||||
{0xA93, 0xAA8},
|
||||
{0xAAA, 0xAB0},
|
||||
{0xAB2, 0xAB3},
|
||||
{0xAB5, 0xAB9},
|
||||
{0xABD, 0xABD},
|
||||
{0xAD0, 0xAD0},
|
||||
{0xAE0, 0xAE1},
|
||||
{0xAF9, 0xAF9},
|
||||
{0xB05, 0xB0C},
|
||||
{0xB0F, 0xB10},
|
||||
{0xB13, 0xB28},
|
||||
{0xB2A, 0xB30},
|
||||
{0xB32, 0xB33},
|
||||
{0xB35, 0xB39},
|
||||
{0xB3D, 0xB3D},
|
||||
{0xB5C, 0xB5D},
|
||||
{0xB5F, 0xB61},
|
||||
{0xB71, 0xB71},
|
||||
{0xB83, 0xB83},
|
||||
{0xB85, 0xB8A},
|
||||
{0xB8E, 0xB90},
|
||||
{0xB92, 0xB95},
|
||||
{0xB99, 0xB9A},
|
||||
{0xB9C, 0xB9C},
|
||||
{0xB9E, 0xB9F},
|
||||
{0xBA3, 0xBA4},
|
||||
{0xBA8, 0xBAA},
|
||||
{0xBAE, 0xBB9},
|
||||
{0xBD0, 0xBD0},
|
||||
{0xC05, 0xC0C},
|
||||
{0xC0E, 0xC10},
|
||||
{0xC12, 0xC28},
|
||||
{0xC2A, 0xC39},
|
||||
{0xC3D, 0xC3D},
|
||||
{0xC58, 0xC5A},
|
||||
{0xC5C, 0xC5D},
|
||||
{0xC60, 0xC61},
|
||||
{0xC80, 0xC80},
|
||||
{0xC85, 0xC8C},
|
||||
{0xC8E, 0xC90},
|
||||
{0xC92, 0xCA8},
|
||||
{0xCAA, 0xCB3},
|
||||
{0xCB5, 0xCB9},
|
||||
{0xCBD, 0xCBD},
|
||||
{0xCDC, 0xCDE},
|
||||
{0xCE0, 0xCE1},
|
||||
{0xCF1, 0xCF2},
|
||||
{0xD04, 0xD0C},
|
||||
{0xD0E, 0xD10},
|
||||
{0xD12, 0xD3A},
|
||||
{0xD3D, 0xD3D},
|
||||
{0xD4E, 0xD4E},
|
||||
{0xD54, 0xD56},
|
||||
{0xD5F, 0xD61},
|
||||
{0xD7A, 0xD7F},
|
||||
{0xD85, 0xD96},
|
||||
{0xD9A, 0xDB1},
|
||||
{0xDB3, 0xDBB},
|
||||
{0xDBD, 0xDBD},
|
||||
{0xDC0, 0xDC6},
|
||||
{0xE01, 0xE30},
|
||||
{0xE32, 0xE33},
|
||||
{0xE40, 0xE46},
|
||||
{0xE81, 0xE82},
|
||||
{0xE84, 0xE84},
|
||||
{0xE86, 0xE8A},
|
||||
{0xE8C, 0xEA3},
|
||||
{0xEA5, 0xEA5},
|
||||
{0xEA7, 0xEB0},
|
||||
{0xEB2, 0xEB3},
|
||||
{0xEBD, 0xEBD},
|
||||
{0xEC0, 0xEC4},
|
||||
{0xEC6, 0xEC6},
|
||||
{0xEDC, 0xEDF},
|
||||
{0xF00, 0xF00},
|
||||
{0xF40, 0xF47},
|
||||
{0xF49, 0xF6C},
|
||||
{0xF88, 0xF8C},
|
||||
{0x1000, 0x102A},
|
||||
{0x103F, 0x103F},
|
||||
{0x1050, 0x1055},
|
||||
{0x105A, 0x105D},
|
||||
{0x1061, 0x1061},
|
||||
{0x1065, 0x1066},
|
||||
{0x106E, 0x1070},
|
||||
{0x1075, 0x1081},
|
||||
{0x108E, 0x108E},
|
||||
{0x10A0, 0x10C5},
|
||||
{0x10C7, 0x10C7},
|
||||
{0x10CD, 0x10CD},
|
||||
{0x10D0, 0x10FA},
|
||||
{0x10FC, 0x1248},
|
||||
{0x124A, 0x124D},
|
||||
{0x1250, 0x1256},
|
||||
{0x1258, 0x1258},
|
||||
{0x125A, 0x125D},
|
||||
{0x1260, 0x1288},
|
||||
{0x128A, 0x128D},
|
||||
{0x1290, 0x12B0},
|
||||
{0x12B2, 0x12B5},
|
||||
{0x12B8, 0x12BE},
|
||||
{0x12C0, 0x12C0},
|
||||
{0x12C2, 0x12C5},
|
||||
{0x12C8, 0x12D6},
|
||||
{0x12D8, 0x1310},
|
||||
{0x1312, 0x1315},
|
||||
{0x1318, 0x135A},
|
||||
{0x1380, 0x138F},
|
||||
{0x13A0, 0x13F5},
|
||||
{0x13F8, 0x13FD},
|
||||
{0x1401, 0x166C},
|
||||
{0x166F, 0x167F},
|
||||
{0x1681, 0x169A},
|
||||
{0x16A0, 0x16EA},
|
||||
{0x16F1, 0x16F8},
|
||||
{0x1700, 0x1711},
|
||||
{0x171F, 0x1731},
|
||||
{0x1740, 0x1751},
|
||||
{0x1760, 0x176C},
|
||||
{0x176E, 0x1770},
|
||||
{0x1780, 0x17B3},
|
||||
{0x17D7, 0x17D7},
|
||||
{0x17DC, 0x17DC},
|
||||
{0x1820, 0x1878},
|
||||
{0x1880, 0x1884},
|
||||
{0x1887, 0x18A8},
|
||||
{0x18AA, 0x18AA},
|
||||
{0x18B0, 0x18F5},
|
||||
{0x1900, 0x191E},
|
||||
{0x1950, 0x196D},
|
||||
{0x1970, 0x1974},
|
||||
{0x1980, 0x19AB},
|
||||
{0x19B0, 0x19C9},
|
||||
{0x1A00, 0x1A16},
|
||||
{0x1A20, 0x1A54},
|
||||
{0x1AA7, 0x1AA7},
|
||||
{0x1B05, 0x1B33},
|
||||
{0x1B45, 0x1B4C},
|
||||
{0x1B83, 0x1BA0},
|
||||
{0x1BAE, 0x1BAF},
|
||||
{0x1BBA, 0x1BE5},
|
||||
{0x1C00, 0x1C23},
|
||||
{0x1C4D, 0x1C4F},
|
||||
{0x1C5A, 0x1C7D},
|
||||
{0x1C80, 0x1C8A},
|
||||
{0x1C90, 0x1CBA},
|
||||
{0x1CBD, 0x1CBF},
|
||||
{0x1CE9, 0x1CEC},
|
||||
{0x1CEE, 0x1CF3},
|
||||
{0x1CF5, 0x1CF6},
|
||||
{0x1CFA, 0x1CFA},
|
||||
{0x1D00, 0x1DBF},
|
||||
{0x1E00, 0x1F15},
|
||||
{0x1F18, 0x1F1D},
|
||||
{0x1F20, 0x1F45},
|
||||
{0x1F48, 0x1F4D},
|
||||
{0x1F50, 0x1F57},
|
||||
{0x1F59, 0x1F59},
|
||||
{0x1F5B, 0x1F5B},
|
||||
{0x1F5D, 0x1F5D},
|
||||
{0x1F5F, 0x1F7D},
|
||||
{0x1F80, 0x1FB4},
|
||||
{0x1FB6, 0x1FBC},
|
||||
{0x1FBE, 0x1FBE},
|
||||
{0x1FC2, 0x1FC4},
|
||||
{0x1FC6, 0x1FCC},
|
||||
{0x1FD0, 0x1FD3},
|
||||
{0x1FD6, 0x1FDB},
|
||||
{0x1FE0, 0x1FEC},
|
||||
{0x1FF2, 0x1FF4},
|
||||
{0x1FF6, 0x1FFC},
|
||||
{0x2071, 0x2071},
|
||||
{0x207F, 0x207F},
|
||||
{0x2090, 0x209C},
|
||||
{0x2102, 0x2102},
|
||||
{0x2107, 0x2107},
|
||||
{0x210A, 0x2113},
|
||||
{0x2115, 0x2115},
|
||||
{0x2119, 0x211D},
|
||||
{0x2124, 0x2124},
|
||||
{0x2126, 0x2126},
|
||||
{0x2128, 0x2128},
|
||||
{0x212A, 0x212D},
|
||||
{0x212F, 0x2139},
|
||||
{0x213C, 0x213F},
|
||||
{0x2145, 0x2149},
|
||||
{0x214E, 0x214E},
|
||||
{0x2183, 0x2184},
|
||||
{0x2C00, 0x2CE4},
|
||||
{0x2CEB, 0x2CEE},
|
||||
{0x2CF2, 0x2CF3},
|
||||
{0x2D00, 0x2D25},
|
||||
{0x2D27, 0x2D27},
|
||||
{0x2D2D, 0x2D2D},
|
||||
{0x2D30, 0x2D67},
|
||||
{0x2D6F, 0x2D6F},
|
||||
{0x2D80, 0x2D96},
|
||||
{0x2DA0, 0x2DA6},
|
||||
{0x2DA8, 0x2DAE},
|
||||
{0x2DB0, 0x2DB6},
|
||||
{0x2DB8, 0x2DBE},
|
||||
{0x2DC0, 0x2DC6},
|
||||
{0x2DC8, 0x2DCE},
|
||||
{0x2DD0, 0x2DD6},
|
||||
{0x2DD8, 0x2DDE},
|
||||
{0x2E2F, 0x2E2F},
|
||||
{0x3005, 0x3006},
|
||||
{0x3031, 0x3035},
|
||||
{0x303B, 0x303C},
|
||||
{0x3041, 0x3096},
|
||||
{0x309D, 0x309F},
|
||||
{0x30A1, 0x30FA},
|
||||
{0x30FC, 0x30FF},
|
||||
{0x3105, 0x312F},
|
||||
{0x3131, 0x318E},
|
||||
{0x31A0, 0x31BF},
|
||||
{0x31F0, 0x31FF},
|
||||
{0x3400, 0x4DBF},
|
||||
{0x4E00, 0xA48C},
|
||||
{0xA4D0, 0xA4FD},
|
||||
{0xA500, 0xA60C},
|
||||
{0xA610, 0xA61F},
|
||||
{0xA62A, 0xA62B},
|
||||
{0xA640, 0xA66E},
|
||||
{0xA67F, 0xA69D},
|
||||
{0xA6A0, 0xA6E5},
|
||||
{0xA717, 0xA71F},
|
||||
{0xA722, 0xA788},
|
||||
{0xA78B, 0xA7DC},
|
||||
{0xA7F1, 0xA801},
|
||||
{0xA803, 0xA805},
|
||||
{0xA807, 0xA80A},
|
||||
{0xA80C, 0xA822},
|
||||
{0xA840, 0xA873},
|
||||
{0xA882, 0xA8B3},
|
||||
{0xA8F2, 0xA8F7},
|
||||
{0xA8FB, 0xA8FB},
|
||||
{0xA8FD, 0xA8FE},
|
||||
{0xA90A, 0xA925},
|
||||
{0xA930, 0xA946},
|
||||
{0xA960, 0xA97C},
|
||||
{0xA984, 0xA9B2},
|
||||
{0xA9CF, 0xA9CF},
|
||||
{0xA9E0, 0xA9E4},
|
||||
{0xA9E6, 0xA9EF},
|
||||
{0xA9FA, 0xA9FE},
|
||||
{0xAA00, 0xAA28},
|
||||
{0xAA40, 0xAA42},
|
||||
{0xAA44, 0xAA4B},
|
||||
{0xAA60, 0xAA76},
|
||||
{0xAA7A, 0xAA7A},
|
||||
{0xAA7E, 0xAAAF},
|
||||
{0xAAB1, 0xAAB1},
|
||||
{0xAAB5, 0xAAB6},
|
||||
{0xAAB9, 0xAABD},
|
||||
{0xAAC0, 0xAAC0},
|
||||
{0xAAC2, 0xAAC2},
|
||||
{0xAADB, 0xAADD},
|
||||
{0xAAE0, 0xAAEA},
|
||||
{0xAAF2, 0xAAF4},
|
||||
{0xAB01, 0xAB06},
|
||||
{0xAB09, 0xAB0E},
|
||||
{0xAB11, 0xAB16},
|
||||
{0xAB20, 0xAB26},
|
||||
{0xAB28, 0xAB2E},
|
||||
{0xAB30, 0xAB5A},
|
||||
{0xAB5C, 0xAB69},
|
||||
{0xAB70, 0xABE2},
|
||||
{0xAC00, 0xD7A3},
|
||||
{0xD7B0, 0xD7C6},
|
||||
{0xD7CB, 0xD7FB},
|
||||
{0xF900, 0xFA6D},
|
||||
{0xFA70, 0xFAD9},
|
||||
{0xFB00, 0xFB06},
|
||||
{0xFB13, 0xFB17},
|
||||
{0xFB1D, 0xFB1D},
|
||||
{0xFB1F, 0xFB28},
|
||||
{0xFB2A, 0xFB36},
|
||||
{0xFB38, 0xFB3C},
|
||||
{0xFB3E, 0xFB3E},
|
||||
{0xFB40, 0xFB41},
|
||||
{0xFB43, 0xFB44},
|
||||
{0xFB46, 0xFBB1},
|
||||
{0xFBD3, 0xFD3D},
|
||||
{0xFD50, 0xFD8F},
|
||||
{0xFD92, 0xFDC7},
|
||||
{0xFDF0, 0xFDFB},
|
||||
{0xFE70, 0xFE74},
|
||||
{0xFE76, 0xFEFC},
|
||||
{0xFF21, 0xFF3A},
|
||||
{0xFF41, 0xFF5A},
|
||||
{0xFF66, 0xFFBE},
|
||||
{0xFFC2, 0xFFC7},
|
||||
{0xFFCA, 0xFFCF},
|
||||
{0xFFD2, 0xFFD7},
|
||||
{0xFFDA, 0xFFDC},
|
||||
{0x10000, 0x1000B},
|
||||
{0x1000D, 0x10026},
|
||||
{0x10028, 0x1003A},
|
||||
{0x1003C, 0x1003D},
|
||||
{0x1003F, 0x1004D},
|
||||
{0x10050, 0x1005D},
|
||||
{0x10080, 0x100FA},
|
||||
{0x10280, 0x1029C},
|
||||
{0x102A0, 0x102D0},
|
||||
{0x10300, 0x1031F},
|
||||
{0x1032D, 0x10340},
|
||||
{0x10342, 0x10349},
|
||||
{0x10350, 0x10375},
|
||||
{0x10380, 0x1039D},
|
||||
{0x103A0, 0x103C3},
|
||||
{0x103C8, 0x103CF},
|
||||
{0x10400, 0x1049D},
|
||||
{0x104B0, 0x104D3},
|
||||
{0x104D8, 0x104FB},
|
||||
{0x10500, 0x10527},
|
||||
{0x10530, 0x10563},
|
||||
{0x10570, 0x1057A},
|
||||
{0x1057C, 0x1058A},
|
||||
{0x1058C, 0x10592},
|
||||
{0x10594, 0x10595},
|
||||
{0x10597, 0x105A1},
|
||||
{0x105A3, 0x105B1},
|
||||
{0x105B3, 0x105B9},
|
||||
{0x105BB, 0x105BC},
|
||||
{0x105C0, 0x105F3},
|
||||
{0x10600, 0x10736},
|
||||
{0x10740, 0x10755},
|
||||
{0x10760, 0x10767},
|
||||
{0x10780, 0x10785},
|
||||
{0x10787, 0x107B0},
|
||||
{0x107B2, 0x107BA},
|
||||
{0x10800, 0x10805},
|
||||
{0x10808, 0x10808},
|
||||
{0x1080A, 0x10835},
|
||||
{0x10837, 0x10838},
|
||||
{0x1083C, 0x1083C},
|
||||
{0x1083F, 0x10855},
|
||||
{0x10860, 0x10876},
|
||||
{0x10880, 0x1089E},
|
||||
{0x108E0, 0x108F2},
|
||||
{0x108F4, 0x108F5},
|
||||
{0x10900, 0x10915},
|
||||
{0x10920, 0x10939},
|
||||
{0x10940, 0x10959},
|
||||
{0x10980, 0x109B7},
|
||||
{0x109BE, 0x109BF},
|
||||
{0x10A00, 0x10A00},
|
||||
{0x10A10, 0x10A13},
|
||||
{0x10A15, 0x10A17},
|
||||
{0x10A19, 0x10A35},
|
||||
{0x10A60, 0x10A7C},
|
||||
{0x10A80, 0x10A9C},
|
||||
{0x10AC0, 0x10AC7},
|
||||
{0x10AC9, 0x10AE4},
|
||||
{0x10B00, 0x10B35},
|
||||
{0x10B40, 0x10B55},
|
||||
{0x10B60, 0x10B72},
|
||||
{0x10B80, 0x10B91},
|
||||
{0x10C00, 0x10C48},
|
||||
{0x10C80, 0x10CB2},
|
||||
{0x10CC0, 0x10CF2},
|
||||
{0x10D00, 0x10D23},
|
||||
{0x10D4A, 0x10D65},
|
||||
{0x10D6F, 0x10D85},
|
||||
{0x10E80, 0x10EA9},
|
||||
{0x10EB0, 0x10EB1},
|
||||
{0x10EC2, 0x10EC7},
|
||||
{0x10F00, 0x10F1C},
|
||||
{0x10F27, 0x10F27},
|
||||
{0x10F30, 0x10F45},
|
||||
{0x10F70, 0x10F81},
|
||||
{0x10FB0, 0x10FC4},
|
||||
{0x10FE0, 0x10FF6},
|
||||
{0x11003, 0x11037},
|
||||
{0x11071, 0x11072},
|
||||
{0x11075, 0x11075},
|
||||
{0x11083, 0x110AF},
|
||||
{0x110D0, 0x110E8},
|
||||
{0x11103, 0x11126},
|
||||
{0x11144, 0x11144},
|
||||
{0x11147, 0x11147},
|
||||
{0x11150, 0x11172},
|
||||
{0x11176, 0x11176},
|
||||
{0x11183, 0x111B2},
|
||||
{0x111C1, 0x111C4},
|
||||
{0x111DA, 0x111DA},
|
||||
{0x111DC, 0x111DC},
|
||||
{0x11200, 0x11211},
|
||||
{0x11213, 0x1122B},
|
||||
{0x1123F, 0x11240},
|
||||
{0x11280, 0x11286},
|
||||
{0x11288, 0x11288},
|
||||
{0x1128A, 0x1128D},
|
||||
{0x1128F, 0x1129D},
|
||||
{0x1129F, 0x112A8},
|
||||
{0x112B0, 0x112DE},
|
||||
{0x11305, 0x1130C},
|
||||
{0x1130F, 0x11310},
|
||||
{0x11313, 0x11328},
|
||||
{0x1132A, 0x11330},
|
||||
{0x11332, 0x11333},
|
||||
{0x11335, 0x11339},
|
||||
{0x1133D, 0x1133D},
|
||||
{0x11350, 0x11350},
|
||||
{0x1135D, 0x11361},
|
||||
{0x11380, 0x11389},
|
||||
{0x1138B, 0x1138B},
|
||||
{0x1138E, 0x1138E},
|
||||
{0x11390, 0x113B5},
|
||||
{0x113B7, 0x113B7},
|
||||
{0x113D1, 0x113D1},
|
||||
{0x113D3, 0x113D3},
|
||||
{0x11400, 0x11434},
|
||||
{0x11447, 0x1144A},
|
||||
{0x1145F, 0x11461},
|
||||
{0x11480, 0x114AF},
|
||||
{0x114C4, 0x114C5},
|
||||
{0x114C7, 0x114C7},
|
||||
{0x11580, 0x115AE},
|
||||
{0x115D8, 0x115DB},
|
||||
{0x11600, 0x1162F},
|
||||
{0x11644, 0x11644},
|
||||
{0x11680, 0x116AA},
|
||||
{0x116B8, 0x116B8},
|
||||
{0x11700, 0x1171A},
|
||||
{0x11740, 0x11746},
|
||||
{0x11800, 0x1182B},
|
||||
{0x118A0, 0x118DF},
|
||||
{0x118FF, 0x11906},
|
||||
{0x11909, 0x11909},
|
||||
{0x1190C, 0x11913},
|
||||
{0x11915, 0x11916},
|
||||
{0x11918, 0x1192F},
|
||||
{0x1193F, 0x1193F},
|
||||
{0x11941, 0x11941},
|
||||
{0x119A0, 0x119A7},
|
||||
{0x119AA, 0x119D0},
|
||||
{0x119E1, 0x119E1},
|
||||
{0x119E3, 0x119E3},
|
||||
{0x11A00, 0x11A00},
|
||||
{0x11A0B, 0x11A32},
|
||||
{0x11A3A, 0x11A3A},
|
||||
{0x11A50, 0x11A50},
|
||||
{0x11A5C, 0x11A89},
|
||||
{0x11A9D, 0x11A9D},
|
||||
{0x11AB0, 0x11AF8},
|
||||
{0x11BC0, 0x11BE0},
|
||||
{0x11C00, 0x11C08},
|
||||
{0x11C0A, 0x11C2E},
|
||||
{0x11C40, 0x11C40},
|
||||
{0x11C72, 0x11C8F},
|
||||
{0x11D00, 0x11D06},
|
||||
{0x11D08, 0x11D09},
|
||||
{0x11D0B, 0x11D30},
|
||||
{0x11D46, 0x11D46},
|
||||
{0x11D60, 0x11D65},
|
||||
{0x11D67, 0x11D68},
|
||||
{0x11D6A, 0x11D89},
|
||||
{0x11D98, 0x11D98},
|
||||
{0x11DB0, 0x11DDB},
|
||||
{0x11EE0, 0x11EF2},
|
||||
{0x11F02, 0x11F02},
|
||||
{0x11F04, 0x11F10},
|
||||
{0x11F12, 0x11F33},
|
||||
{0x11FB0, 0x11FB0},
|
||||
{0x12000, 0x12399},
|
||||
{0x12480, 0x12543},
|
||||
{0x12F90, 0x12FF0},
|
||||
{0x13000, 0x1342F},
|
||||
{0x13441, 0x13446},
|
||||
{0x13460, 0x143FA},
|
||||
{0x14400, 0x14646},
|
||||
{0x16100, 0x1611D},
|
||||
{0x16800, 0x16A38},
|
||||
{0x16A40, 0x16A5E},
|
||||
{0x16A70, 0x16ABE},
|
||||
{0x16AD0, 0x16AED},
|
||||
{0x16B00, 0x16B2F},
|
||||
{0x16B40, 0x16B43},
|
||||
{0x16B63, 0x16B77},
|
||||
{0x16B7D, 0x16B8F},
|
||||
{0x16D40, 0x16D6C},
|
||||
{0x16E40, 0x16E7F},
|
||||
{0x16EA0, 0x16EB8},
|
||||
{0x16EBB, 0x16ED3},
|
||||
{0x16F00, 0x16F4A},
|
||||
{0x16F50, 0x16F50},
|
||||
{0x16F93, 0x16F9F},
|
||||
{0x16FE0, 0x16FE1},
|
||||
{0x16FE3, 0x16FE3},
|
||||
{0x16FF2, 0x16FF3},
|
||||
{0x17000, 0x18CD5},
|
||||
{0x18CFF, 0x18D1E},
|
||||
{0x18D80, 0x18DF2},
|
||||
{0x1AFF0, 0x1AFF3},
|
||||
{0x1AFF5, 0x1AFFB},
|
||||
{0x1AFFD, 0x1AFFE},
|
||||
{0x1B000, 0x1B122},
|
||||
{0x1B132, 0x1B132},
|
||||
{0x1B150, 0x1B152},
|
||||
{0x1B155, 0x1B155},
|
||||
{0x1B164, 0x1B167},
|
||||
{0x1B170, 0x1B2FB},
|
||||
{0x1BC00, 0x1BC6A},
|
||||
{0x1BC70, 0x1BC7C},
|
||||
{0x1BC80, 0x1BC88},
|
||||
{0x1BC90, 0x1BC99},
|
||||
{0x1D400, 0x1D454},
|
||||
{0x1D456, 0x1D49C},
|
||||
{0x1D49E, 0x1D49F},
|
||||
{0x1D4A2, 0x1D4A2},
|
||||
{0x1D4A5, 0x1D4A6},
|
||||
{0x1D4A9, 0x1D4AC},
|
||||
{0x1D4AE, 0x1D4B9},
|
||||
{0x1D4BB, 0x1D4BB},
|
||||
{0x1D4BD, 0x1D4C3},
|
||||
{0x1D4C5, 0x1D505},
|
||||
{0x1D507, 0x1D50A},
|
||||
{0x1D50D, 0x1D514},
|
||||
{0x1D516, 0x1D51C},
|
||||
{0x1D51E, 0x1D539},
|
||||
{0x1D53B, 0x1D53E},
|
||||
{0x1D540, 0x1D544},
|
||||
{0x1D546, 0x1D546},
|
||||
{0x1D54A, 0x1D550},
|
||||
{0x1D552, 0x1D6A5},
|
||||
{0x1D6A8, 0x1D6C0},
|
||||
{0x1D6C2, 0x1D6DA},
|
||||
{0x1D6DC, 0x1D6FA},
|
||||
{0x1D6FC, 0x1D714},
|
||||
{0x1D716, 0x1D734},
|
||||
{0x1D736, 0x1D74E},
|
||||
{0x1D750, 0x1D76E},
|
||||
{0x1D770, 0x1D788},
|
||||
{0x1D78A, 0x1D7A8},
|
||||
{0x1D7AA, 0x1D7C2},
|
||||
{0x1D7C4, 0x1D7CB},
|
||||
{0x1DF00, 0x1DF1E},
|
||||
{0x1DF25, 0x1DF2A},
|
||||
{0x1E030, 0x1E06D},
|
||||
{0x1E100, 0x1E12C},
|
||||
{0x1E137, 0x1E13D},
|
||||
{0x1E14E, 0x1E14E},
|
||||
{0x1E290, 0x1E2AD},
|
||||
{0x1E2C0, 0x1E2EB},
|
||||
{0x1E4D0, 0x1E4EB},
|
||||
{0x1E5D0, 0x1E5ED},
|
||||
{0x1E5F0, 0x1E5F0},
|
||||
{0x1E6C0, 0x1E6DE},
|
||||
{0x1E6E0, 0x1E6E2},
|
||||
{0x1E6E4, 0x1E6E5},
|
||||
{0x1E6E7, 0x1E6ED},
|
||||
{0x1E6F0, 0x1E6F4},
|
||||
{0x1E6FE, 0x1E6FF},
|
||||
{0x1E7E0, 0x1E7E6},
|
||||
{0x1E7E8, 0x1E7EB},
|
||||
{0x1E7ED, 0x1E7EE},
|
||||
{0x1E7F0, 0x1E7FE},
|
||||
{0x1E800, 0x1E8C4},
|
||||
{0x1E900, 0x1E943},
|
||||
{0x1E94B, 0x1E94B},
|
||||
{0x1EE00, 0x1EE03},
|
||||
{0x1EE05, 0x1EE1F},
|
||||
{0x1EE21, 0x1EE22},
|
||||
{0x1EE24, 0x1EE24},
|
||||
{0x1EE27, 0x1EE27},
|
||||
{0x1EE29, 0x1EE32},
|
||||
{0x1EE34, 0x1EE37},
|
||||
{0x1EE39, 0x1EE39},
|
||||
{0x1EE3B, 0x1EE3B},
|
||||
{0x1EE42, 0x1EE42},
|
||||
{0x1EE47, 0x1EE47},
|
||||
{0x1EE49, 0x1EE49},
|
||||
{0x1EE4B, 0x1EE4B},
|
||||
{0x1EE4D, 0x1EE4F},
|
||||
{0x1EE51, 0x1EE52},
|
||||
{0x1EE54, 0x1EE54},
|
||||
{0x1EE57, 0x1EE57},
|
||||
{0x1EE59, 0x1EE59},
|
||||
{0x1EE5B, 0x1EE5B},
|
||||
{0x1EE5D, 0x1EE5D},
|
||||
{0x1EE5F, 0x1EE5F},
|
||||
{0x1EE61, 0x1EE62},
|
||||
{0x1EE64, 0x1EE64},
|
||||
{0x1EE67, 0x1EE6A},
|
||||
{0x1EE6C, 0x1EE72},
|
||||
{0x1EE74, 0x1EE77},
|
||||
{0x1EE79, 0x1EE7C},
|
||||
{0x1EE7E, 0x1EE7E},
|
||||
{0x1EE80, 0x1EE89},
|
||||
{0x1EE8B, 0x1EE9B},
|
||||
{0x1EEA1, 0x1EEA3},
|
||||
{0x1EEA5, 0x1EEA9},
|
||||
{0x1EEAB, 0x1EEBB},
|
||||
{0x20000, 0x2A6DF},
|
||||
{0x2A700, 0x2B81D},
|
||||
{0x2B820, 0x2CEAD},
|
||||
{0x2CEB0, 0x2EBE0},
|
||||
{0x2EBF0, 0x2EE5D},
|
||||
{0x2F800, 0x2FA1D},
|
||||
{0x30000, 0x3134A},
|
||||
{0x31350, 0x33479},
|
||||
};
|
||||
|
||||
for (const auto& r : ranges) {
|
||||
if (ch >= r.start && ch <= r.end)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_space(char32_t cp) {
|
||||
switch (cp) {
|
||||
case 0x0009: // TAB \t
|
||||
case 0x000A: // LF \n
|
||||
case 0x000B: // VT
|
||||
case 0x000C: // FF
|
||||
case 0x000D: // CR \r
|
||||
case 0x0020: // Space
|
||||
case 0x00A0: // No-Break Space
|
||||
case 0x1680: // Ogham Space Mark
|
||||
case 0x2000: // En Quad
|
||||
case 0x2001: // Em Quad
|
||||
case 0x2002: // En Space
|
||||
case 0x2003: // Em Space
|
||||
case 0x2004: // Three-Per-Em Space
|
||||
case 0x2005: // Four-Per-Em Space
|
||||
case 0x2006: // Six-Per-Em Space
|
||||
case 0x2007: // Figure Space
|
||||
case 0x2008: // Punctuation Space
|
||||
case 0x2009: // Thin Space
|
||||
case 0x200A: // Hair Space
|
||||
case 0x202F: // Narrow No-Break Space
|
||||
case 0x205F: // Medium Mathematical Space
|
||||
case 0x3000: // Ideographic Space
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string str_to_lower(const std::string& input) {
|
||||
std::string result = input;
|
||||
std::transform(result.begin(), result.end(), result.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return result;
|
||||
}
|
||||
|
||||
// UTF-8 -> Unicode code points
|
||||
std::vector<char32_t> utf8_to_codepoints(const std::string& str) {
|
||||
std::vector<char32_t> codepoints;
|
||||
size_t i = 0;
|
||||
while (i < str.size()) {
|
||||
unsigned char c = str[i];
|
||||
char32_t cp = 0;
|
||||
size_t extra_bytes = 0;
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
cp = c;
|
||||
else if ((c & 0xE0) == 0xC0) {
|
||||
cp = c & 0x1F;
|
||||
extra_bytes = 1;
|
||||
} else if ((c & 0xF0) == 0xE0) {
|
||||
cp = c & 0x0F;
|
||||
extra_bytes = 2;
|
||||
} else if ((c & 0xF8) == 0xF0) {
|
||||
cp = c & 0x07;
|
||||
extra_bytes = 3;
|
||||
} else {
|
||||
++i;
|
||||
continue;
|
||||
} // Invalid UTF-8
|
||||
|
||||
if (i + extra_bytes >= str.size())
|
||||
break;
|
||||
|
||||
for (size_t j = 1; j <= extra_bytes; ++j)
|
||||
cp = (cp << 6) | (str[i + j] & 0x3F);
|
||||
|
||||
codepoints.push_back(cp);
|
||||
i += 1 + extra_bytes;
|
||||
}
|
||||
return codepoints;
|
||||
}
|
||||
|
||||
// Unicode code point -> UTF-8
|
||||
std::string codepoint_to_utf8(char32_t cp) {
|
||||
std::string out;
|
||||
if (cp <= 0x7F)
|
||||
out.push_back(static_cast<char>(cp));
|
||||
else if (cp <= 0x7FF) {
|
||||
out.push_back(static_cast<char>(0xC0 | (cp >> 6)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else if (cp <= 0xFFFF) {
|
||||
out.push_back(static_cast<char>(0xE0 | (cp >> 12)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else {
|
||||
out.push_back(static_cast<char>(0xF0 | (cp >> 18)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool starts_with(const std::vector<char32_t>& text,
|
||||
const std::vector<char32_t>& prefix,
|
||||
std::size_t index) {
|
||||
if (index > text.size()) {
|
||||
return false;
|
||||
}
|
||||
if (prefix.size() > text.size() - index) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(prefix.begin(), prefix.end(), text.begin() + index);
|
||||
}
|
||||
|
||||
std::vector<std::string> token_split(const std::string& text) {
|
||||
std::vector<std::string> tokens;
|
||||
auto cps = utf8_to_codepoints(text);
|
||||
size_t i = 0;
|
||||
|
||||
while (i < cps.size()) {
|
||||
char32_t cp = cps[i];
|
||||
|
||||
// `(?i:'s|'t|'re|'ve|'m|'ll|'d)`
|
||||
if (cp == U'\'' && i + 1 < cps.size()) {
|
||||
std::string next = str_to_lower(codepoint_to_utf8(cps[i + 1]));
|
||||
if (next == "s" || next == "t" || next == "m") {
|
||||
tokens.push_back("'" + next);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (i + 2 < cps.size()) {
|
||||
next += str_to_lower(codepoint_to_utf8(cps[i + 2]));
|
||||
if (next == "re" || next == "ve" || next == "ll" || next == "d") {
|
||||
tokens.push_back("'" + next);
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `\p{N}`
|
||||
if (is_number(cp)) {
|
||||
tokens.push_back(codepoint_to_utf8(cp));
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// `[^\r\n\p{L}\p{N}]?\p{L}+`
|
||||
{
|
||||
// `[^\r\n\p{L}\p{N}]\p{L}+`
|
||||
if (!is_letter(cp) && cp != U'\r' && cp != U'\n' && i + 1 < cps.size() && is_letter(cps[i + 1])) {
|
||||
std::string token = codepoint_to_utf8(cp);
|
||||
++i;
|
||||
|
||||
while (i < cps.size() && is_letter(cps[i])) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
tokens.push_back(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// `\p{L}+`
|
||||
if (is_letter(cp)) {
|
||||
std::string token = codepoint_to_utf8(cp);
|
||||
++i;
|
||||
while (i < cps.size() && is_letter(cps[i])) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
tokens.push_back(token);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// ` ?[^\s\p{L}\p{N}]+[\r\n]*`
|
||||
{
|
||||
// ` [^\s\p{L}\p{N}]+[\r\n]*`
|
||||
if (cp == U' ' && i + 1 < cps.size() && !isspace(cps[i + 1]) && !is_letter(cps[i + 1]) && !is_number(cps[i + 1])) {
|
||||
std::string token = codepoint_to_utf8(cp);
|
||||
token += codepoint_to_utf8(cps[i + 1]);
|
||||
i += 2;
|
||||
|
||||
while (i < cps.size() && !is_letter(cps[i]) && !is_number(cps[i]) && !isspace(cps[i])) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
while (i < cps.size() && (cps[i] == U'\r' || cps[i] == U'\n')) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
tokens.push_back(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// `[^\s\p{L}\p{N}]+[\r\n]*`
|
||||
std::string token;
|
||||
if (!is_letter(cps[i]) && !is_number(cps[i]) && !isspace(cps[i])) {
|
||||
std::string token = codepoint_to_utf8(cp);
|
||||
++i;
|
||||
|
||||
while (i < cps.size() && !is_letter(cps[i]) && !is_number(cps[i]) && !isspace(cps[i])) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
while (i < cps.size() && (cps[i] == U'\r' || cps[i] == U'\n')) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
tokens.push_back(token);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// `\s*[\r\n]+|\s+(?!\S)|\s+`
|
||||
if (is_space(cp)) {
|
||||
std::string token = codepoint_to_utf8(cp);
|
||||
++i;
|
||||
|
||||
while (i < cps.size() && is_space(cps[i])) {
|
||||
token += codepoint_to_utf8(cps[i]);
|
||||
++i;
|
||||
if (cps[i] == U'\r' || cps[i] == U'\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokens.push_back(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip
|
||||
++i;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::vector<std::string> split_with_special_tokens(
|
||||
const std::string& text,
|
||||
const std::vector<std::string>& special_tokens) {
|
||||
std::vector<std::string> result;
|
||||
size_t pos = 0;
|
||||
size_t text_len = text.size();
|
||||
|
||||
while (pos < text_len) {
|
||||
size_t next_pos = text_len;
|
||||
std::string matched_token;
|
||||
|
||||
for (const auto& token : special_tokens) {
|
||||
size_t token_pos = text.find(token, pos);
|
||||
if (token_pos != std::string::npos && token_pos < next_pos) {
|
||||
next_pos = token_pos;
|
||||
matched_token = token;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_pos > pos) {
|
||||
result.push_back(text.substr(pos, next_pos - pos));
|
||||
}
|
||||
|
||||
if (!matched_token.empty()) {
|
||||
result.push_back(matched_token);
|
||||
pos = next_pos + matched_token.size();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// int main() {
|
||||
// std::string text = "I'm testing C++ token_split function. 你好,世界! 123";
|
||||
// auto tokens = token_split(text);
|
||||
|
||||
// for (const auto& t : tokens) {
|
||||
// std::cout << "[" << t << "] ";
|
||||
// }
|
||||
// std::cout << "\n";
|
||||
// return 0;
|
||||
// }
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef __TOKENIZE_UTIL__
|
||||
#define __TOKENIZE_UTIL__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string> token_split(const std::string& text);
|
||||
std::vector<std::string> split_with_special_tokens(const std::string& text, const std::vector<std::string>& special_tokens);
|
||||
|
||||
#endif // __TOKENIZE_UTIL__
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context,
|
||||
int timesteps) {
|
||||
@@ -127,7 +128,7 @@ public:
|
||||
auto block = std::dynamic_pointer_cast<BasicTransformerBlock>(blocks[transformer_name]);
|
||||
auto mix_block = std::dynamic_pointer_cast<BasicTransformerBlock>(blocks[time_stack_name]);
|
||||
|
||||
x = block->forward(ctx, x, spatial_context); // [N, h * w, inner_dim]
|
||||
x = block->forward(ctx, backend, x, spatial_context); // [N, h * w, inner_dim]
|
||||
|
||||
// in_channels == inner_dim
|
||||
auto x_mix = x;
|
||||
@@ -143,7 +144,7 @@ public:
|
||||
x_mix = ggml_cont(ctx, ggml_permute(ctx, x_mix, 0, 2, 1, 3)); // b t s c -> b s t c
|
||||
x_mix = ggml_reshape_3d(ctx, x_mix, C, T, S * B); // b s t c -> (b s) t c
|
||||
|
||||
x_mix = mix_block->forward(ctx, x_mix, time_context); // [B * h * w, T, inner_dim]
|
||||
x_mix = mix_block->forward(ctx, backend, x_mix, time_context); // [B * h * w, T, inner_dim]
|
||||
|
||||
x_mix = ggml_reshape_4d(ctx, x_mix, C, T, S, B); // (b s) t c -> b s t c
|
||||
x_mix = ggml_cont(ctx, ggml_permute(ctx, x_mix, 0, 2, 1, 3)); // b s t c -> b t s c
|
||||
@@ -166,7 +167,6 @@ public:
|
||||
// ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
class UnetModelBlock : public GGMLBlock {
|
||||
protected:
|
||||
static std::map<std::string, enum ggml_type> empty_tensor_types;
|
||||
SDVersion version = VERSION_SD1;
|
||||
// network hparams
|
||||
int in_channels = 4;
|
||||
@@ -184,7 +184,7 @@ public:
|
||||
int model_channels = 320;
|
||||
int adm_in_channels = 2816; // only for VERSION_SDXL/SVD
|
||||
|
||||
UnetModelBlock(SDVersion version = VERSION_SD1, std::map<std::string, enum ggml_type>& tensor_types = empty_tensor_types, bool flash_attn = false)
|
||||
UnetModelBlock(SDVersion version = VERSION_SD1, const String2GGMLType& tensor_types = {}, bool flash_attn = false)
|
||||
: version(version) {
|
||||
if (sd_version_is_sd2(version)) {
|
||||
context_dim = 1024;
|
||||
@@ -207,6 +207,8 @@ public:
|
||||
}
|
||||
if (sd_version_is_inpaint(version)) {
|
||||
in_channels = 9;
|
||||
} else if (sd_version_is_unet_edit(version)) {
|
||||
in_channels = 8;
|
||||
}
|
||||
|
||||
// dims is always 2
|
||||
@@ -362,21 +364,23 @@ public:
|
||||
|
||||
struct ggml_tensor* attention_layer_forward(std::string name,
|
||||
struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* context,
|
||||
int timesteps) {
|
||||
if (version == VERSION_SVD) {
|
||||
auto block = std::dynamic_pointer_cast<SpatialVideoTransformer>(blocks[name]);
|
||||
|
||||
return block->forward(ctx, x, context, timesteps);
|
||||
return block->forward(ctx, backend, x, context, timesteps);
|
||||
} else {
|
||||
auto block = std::dynamic_pointer_cast<SpatialTransformer>(blocks[name]);
|
||||
|
||||
return block->forward(ctx, x, context);
|
||||
return block->forward(ctx, backend, x, context);
|
||||
}
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(struct ggml_context* ctx,
|
||||
ggml_backend_t backend,
|
||||
struct ggml_tensor* x,
|
||||
struct ggml_tensor* timesteps,
|
||||
struct ggml_tensor* context,
|
||||
@@ -455,7 +459,7 @@ public:
|
||||
h = resblock_forward(name, ctx, h, emb, num_video_frames); // [N, mult*model_channels, h, w]
|
||||
if (std::find(attention_resolutions.begin(), attention_resolutions.end(), ds) != attention_resolutions.end()) {
|
||||
std::string name = "input_blocks." + std::to_string(input_block_idx) + ".1";
|
||||
h = attention_layer_forward(name, ctx, h, context, num_video_frames); // [N, mult*model_channels, h, w]
|
||||
h = attention_layer_forward(name, ctx, backend, h, context, num_video_frames); // [N, mult*model_channels, h, w]
|
||||
}
|
||||
hs.push_back(h);
|
||||
}
|
||||
@@ -473,9 +477,9 @@ public:
|
||||
// [N, 4*model_channels, h/8, w/8]
|
||||
|
||||
// middle_block
|
||||
h = resblock_forward("middle_block.0", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = attention_layer_forward("middle_block.1", ctx, h, context, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.2", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.0", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = attention_layer_forward("middle_block.1", ctx, backend, h, context, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
h = resblock_forward("middle_block.2", ctx, h, emb, num_video_frames); // [N, 4*model_channels, h/8, w/8]
|
||||
|
||||
if (controls.size() > 0) {
|
||||
auto cs = ggml_scale_inplace(ctx, controls[controls.size() - 1], control_strength);
|
||||
@@ -506,7 +510,7 @@ public:
|
||||
if (std::find(attention_resolutions.begin(), attention_resolutions.end(), ds) != attention_resolutions.end()) {
|
||||
std::string name = "output_blocks." + std::to_string(output_block_idx) + ".1";
|
||||
|
||||
h = attention_layer_forward(name, ctx, h, context, num_video_frames);
|
||||
h = attention_layer_forward(name, ctx, backend, h, context, num_video_frames);
|
||||
|
||||
up_sample_idx++;
|
||||
}
|
||||
@@ -537,14 +541,27 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
UnetModelBlock unet;
|
||||
|
||||
UNetModelRunner(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
SDVersion version = VERSION_SD1,
|
||||
bool flash_attn = false)
|
||||
: GGMLRunner(backend), unet(version, tensor_types, flash_attn) {
|
||||
: GGMLRunner(backend, offload_params_to_cpu), unet(version, tensor_types, flash_attn) {
|
||||
unet.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
void enable_conv2d_direct() {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
unet.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
LOG_DEBUG("block %s", block->get_desc().c_str());
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->enable_direct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return "unet";
|
||||
}
|
||||
@@ -578,6 +595,7 @@ struct UNetModelRunner : public GGMLRunner {
|
||||
}
|
||||
|
||||
struct ggml_tensor* out = unet.forward(compute_ctx,
|
||||
runtime_backend,
|
||||
x,
|
||||
timesteps,
|
||||
context,
|
||||
|
||||
+32
-12
@@ -9,25 +9,34 @@ struct UpscalerGGML {
|
||||
std::shared_ptr<ESRGAN> esrgan_upscaler;
|
||||
std::string esrgan_path;
|
||||
int n_threads;
|
||||
bool direct = false;
|
||||
|
||||
UpscalerGGML(int n_threads)
|
||||
: n_threads(n_threads) {
|
||||
UpscalerGGML(int n_threads,
|
||||
bool direct = false)
|
||||
: n_threads(n_threads),
|
||||
direct(direct) {
|
||||
}
|
||||
|
||||
bool load_from_file(const std::string& esrgan_path) {
|
||||
bool load_from_file(const std::string& esrgan_path,
|
||||
bool offload_params_to_cpu,
|
||||
int n_threads) {
|
||||
ggml_log_set(ggml_log_callback_default, nullptr);
|
||||
#ifdef SD_USE_CUDA
|
||||
LOG_DEBUG("Using CUDA backend");
|
||||
backend = ggml_backend_cuda_init(0);
|
||||
#endif
|
||||
#ifdef SD_USE_METAL
|
||||
LOG_DEBUG("Using Metal backend");
|
||||
ggml_log_set(ggml_log_callback_default, nullptr);
|
||||
backend = ggml_backend_metal_init();
|
||||
#endif
|
||||
#ifdef SD_USE_VULKAN
|
||||
LOG_DEBUG("Using Vulkan backend");
|
||||
backend = ggml_backend_vk_init(0);
|
||||
#endif
|
||||
#ifdef SD_USE_OPENCL
|
||||
LOG_DEBUG("Using OpenCL backend");
|
||||
backend = ggml_backend_opencl_init();
|
||||
#endif
|
||||
#ifdef SD_USE_SYCL
|
||||
LOG_DEBUG("Using SYCL backend");
|
||||
backend = ggml_backend_sycl_init(0);
|
||||
@@ -42,8 +51,11 @@ struct UpscalerGGML {
|
||||
backend = ggml_backend_cpu_init();
|
||||
}
|
||||
LOG_INFO("Upscaler weight type: %s", ggml_type_name(model_data_type));
|
||||
esrgan_upscaler = std::make_shared<ESRGAN>(backend, model_loader.tensor_storages_types);
|
||||
if (!esrgan_upscaler->load_from_file(esrgan_path)) {
|
||||
esrgan_upscaler = std::make_shared<ESRGAN>(backend, offload_params_to_cpu, model_loader.tensor_storages_types);
|
||||
if (direct) {
|
||||
esrgan_upscaler->enable_conv2d_direct();
|
||||
}
|
||||
if (!esrgan_upscaler->load_from_file(esrgan_path, n_threads)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -58,8 +70,7 @@ struct UpscalerGGML {
|
||||
input_image.width, input_image.height, output_width, output_height);
|
||||
|
||||
struct ggml_init_params params;
|
||||
params.mem_size = output_width * output_height * 3 * sizeof(float) * 2;
|
||||
params.mem_size += 2 * ggml_tensor_overhead();
|
||||
params.mem_size = static_cast<size_t>(1024 * 1024) * 1024; // 1G
|
||||
params.mem_buffer = NULL;
|
||||
params.no_alloc = false;
|
||||
|
||||
@@ -69,9 +80,9 @@ struct UpscalerGGML {
|
||||
LOG_ERROR("ggml_init() failed");
|
||||
return upscaled_image;
|
||||
}
|
||||
LOG_DEBUG("upscale work buffer size: %.2f MB", params.mem_size / 1024.f / 1024.f);
|
||||
// LOG_DEBUG("upscale work buffer size: %.2f MB", params.mem_size / 1024.f / 1024.f);
|
||||
ggml_tensor* input_image_tensor = ggml_new_tensor_4d(upscale_ctx, GGML_TYPE_F32, input_image.width, input_image.height, 3, 1);
|
||||
sd_image_to_tensor(input_image.data, input_image_tensor);
|
||||
sd_image_to_tensor(input_image, input_image_tensor);
|
||||
|
||||
ggml_tensor* upscaled = ggml_new_tensor_4d(upscale_ctx, GGML_TYPE_F32, output_width, output_height, 3, 1);
|
||||
auto on_tiling = [&](ggml_tensor* in, ggml_tensor* out, bool init) {
|
||||
@@ -100,6 +111,8 @@ struct upscaler_ctx_t {
|
||||
};
|
||||
|
||||
upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path_c_str,
|
||||
bool offload_params_to_cpu,
|
||||
bool direct,
|
||||
int n_threads) {
|
||||
upscaler_ctx_t* upscaler_ctx = (upscaler_ctx_t*)malloc(sizeof(upscaler_ctx_t));
|
||||
if (upscaler_ctx == NULL) {
|
||||
@@ -107,12 +120,12 @@ upscaler_ctx_t* new_upscaler_ctx(const char* esrgan_path_c_str,
|
||||
}
|
||||
std::string esrgan_path(esrgan_path_c_str);
|
||||
|
||||
upscaler_ctx->upscaler = new UpscalerGGML(n_threads);
|
||||
upscaler_ctx->upscaler = new UpscalerGGML(n_threads, direct);
|
||||
if (upscaler_ctx->upscaler == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!upscaler_ctx->upscaler->load_from_file(esrgan_path)) {
|
||||
if (!upscaler_ctx->upscaler->load_from_file(esrgan_path, offload_params_to_cpu, n_threads)) {
|
||||
delete upscaler_ctx->upscaler;
|
||||
upscaler_ctx->upscaler = NULL;
|
||||
free(upscaler_ctx);
|
||||
@@ -125,6 +138,13 @@ sd_image_t upscale(upscaler_ctx_t* upscaler_ctx, sd_image_t input_image, uint32_
|
||||
return upscaler_ctx->upscaler->upscale(input_image, upscale_factor);
|
||||
}
|
||||
|
||||
int get_upscale_factor(upscaler_ctx_t* upscaler_ctx) {
|
||||
if (upscaler_ctx == NULL || upscaler_ctx->upscaler == NULL || upscaler_ctx->upscaler->esrgan_upscaler == NULL) {
|
||||
return 1;
|
||||
}
|
||||
return upscaler_ctx->upscaler->esrgan_upscaler->scale;
|
||||
}
|
||||
|
||||
void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx) {
|
||||
if (upscaler_ctx->upscaler != NULL) {
|
||||
delete upscaler_ctx->upscaler;
|
||||
|
||||
@@ -72,7 +72,19 @@ std::string format(const char* fmt, ...) {
|
||||
return std::string(buf.data(), size);
|
||||
}
|
||||
|
||||
int round_up_to(int value, int base) {
|
||||
if (base <= 0) {
|
||||
return value;
|
||||
}
|
||||
if (value % base == 0) {
|
||||
return value;
|
||||
} else {
|
||||
return ((value / base) + 1) * base;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32 // code for windows
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
|
||||
bool file_exists(const std::string& filename) {
|
||||
@@ -99,43 +111,6 @@ std::string get_full_path(const std::string& dir, const std::string& filename) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> get_files_from_dir(const std::string& dir) {
|
||||
std::vector<std::string> files;
|
||||
|
||||
WIN32_FIND_DATA findFileData;
|
||||
HANDLE hFind;
|
||||
|
||||
char currentDirectory[MAX_PATH];
|
||||
GetCurrentDirectory(MAX_PATH, currentDirectory);
|
||||
|
||||
char directoryPath[MAX_PATH]; // this is absolute path
|
||||
sprintf(directoryPath, "%s\\%s\\*", currentDirectory, dir.c_str());
|
||||
|
||||
// Find the first file in the directory
|
||||
hFind = FindFirstFile(directoryPath, &findFileData);
|
||||
|
||||
// Check if the directory was found
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
printf("Unable to find directory.\n");
|
||||
return files;
|
||||
}
|
||||
|
||||
// Loop through all files in the directory
|
||||
do {
|
||||
// Check if the found file is a regular file (not a directory)
|
||||
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
files.push_back(std::string(currentDirectory) + "\\" + dir + "\\" + std::string(findFileData.cFileName));
|
||||
}
|
||||
} while (FindNextFile(hFind, &findFileData) != 0);
|
||||
|
||||
// Close the handle
|
||||
FindClose(hFind);
|
||||
|
||||
sort(files.begin(), files.end());
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#else // Unix
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -170,27 +145,6 @@ std::string get_full_path(const std::string& dir, const std::string& filename) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::string> get_files_from_dir(const std::string& dir) {
|
||||
std::vector<std::string> files;
|
||||
|
||||
DIR* dp = opendir(dir.c_str());
|
||||
|
||||
if (dp != nullptr) {
|
||||
struct dirent* entry;
|
||||
|
||||
while ((entry = readdir(dp)) != nullptr) {
|
||||
std::string fname = dir + "/" + entry->d_name;
|
||||
if (!is_directory(fname))
|
||||
files.push_back(fname);
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
|
||||
sort(files.begin(), files.end());
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// get_num_physical_cores is copy from
|
||||
@@ -277,7 +231,7 @@ std::string path_join(const std::string& p1, const std::string& p2) {
|
||||
return p1 + "/" + p2;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitString(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> split_string(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> result;
|
||||
size_t start = 0;
|
||||
size_t end = str.find(delimiter);
|
||||
@@ -294,39 +248,6 @@ std::vector<std::string> splitString(const std::string& str, char delimiter) {
|
||||
return result;
|
||||
}
|
||||
|
||||
sd_image_t* preprocess_id_image(sd_image_t* img) {
|
||||
int shortest_edge = 224;
|
||||
int size = shortest_edge;
|
||||
sd_image_t* resized = NULL;
|
||||
uint32_t w = img->width;
|
||||
uint32_t h = img->height;
|
||||
uint32_t c = img->channel;
|
||||
|
||||
// 1. do resize using stb_resize functions
|
||||
|
||||
unsigned char* buf = (unsigned char*)malloc(sizeof(unsigned char) * 3 * size * size);
|
||||
if (!stbir_resize_uint8(img->data, w, h, 0,
|
||||
buf, size, size, 0,
|
||||
c)) {
|
||||
fprintf(stderr, "%s: resize operation failed \n ", __func__);
|
||||
return resized;
|
||||
}
|
||||
|
||||
// 2. do center crop (likely unnecessary due to step 1)
|
||||
|
||||
// 3. do rescale
|
||||
|
||||
// 4. do normalize
|
||||
|
||||
// 3 and 4 will need to be done in float format.
|
||||
|
||||
resized = new sd_image_t{(uint32_t)shortest_edge,
|
||||
(uint32_t)shortest_edge,
|
||||
3,
|
||||
buf};
|
||||
return resized;
|
||||
}
|
||||
|
||||
void pretty_progress(int step, int steps, float time) {
|
||||
if (sd_progress_cb) {
|
||||
sd_progress_cb(step, steps, time, sd_progress_cb_data);
|
||||
@@ -378,7 +299,7 @@ std::string trim(const std::string& s) {
|
||||
static sd_log_cb_t sd_log_cb = NULL;
|
||||
void* sd_log_cb_data = NULL;
|
||||
|
||||
#define LOG_BUFFER_SIZE 1024
|
||||
#define LOG_BUFFER_SIZE 4096
|
||||
|
||||
void log_printf(sd_log_level_t level, const char* file, int line, const char* format, ...) {
|
||||
va_list args;
|
||||
@@ -390,7 +311,10 @@ void log_printf(sd_log_level_t level, const char* file, int line, const char* fo
|
||||
if (written >= 0 && written < LOG_BUFFER_SIZE) {
|
||||
vsnprintf(log_buffer + written, LOG_BUFFER_SIZE - written, format, args);
|
||||
}
|
||||
strncat(log_buffer, "\n", LOG_BUFFER_SIZE - strlen(log_buffer));
|
||||
size_t len = strlen(log_buffer);
|
||||
if (log_buffer[len - 1] != '\n') {
|
||||
strncat(log_buffer, "\n", LOG_BUFFER_SIZE - len);
|
||||
}
|
||||
|
||||
if (sd_log_cb) {
|
||||
sd_log_cb(level, log_buffer, sd_log_cb_data);
|
||||
@@ -428,10 +352,6 @@ const char* sd_get_system_info() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* sd_type_name(enum sd_type_t type) {
|
||||
return ggml_type_name((ggml_type)type);
|
||||
}
|
||||
|
||||
sd_image_f32_t sd_image_t_to_sd_image_f32_t(sd_image_t image) {
|
||||
sd_image_f32_t converted_image;
|
||||
converted_image.width = image.width;
|
||||
@@ -468,10 +388,10 @@ sd_image_f32_t resize_sd_image_f32_t(sd_image_f32_t image, int target_width, int
|
||||
float original_x = (float)x * image.width / target_width;
|
||||
float original_y = (float)y * image.height / target_height;
|
||||
|
||||
int x1 = (int)original_x;
|
||||
int y1 = (int)original_y;
|
||||
int x2 = x1 + 1;
|
||||
int y2 = y1 + 1;
|
||||
uint32_t x1 = (uint32_t)original_x;
|
||||
uint32_t y1 = (uint32_t)original_y;
|
||||
uint32_t x2 = std::min(x1 + 1, image.width - 1);
|
||||
uint32_t y2 = std::min(y1 + 1, image.height - 1);
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
float v1 = *(image.data + y1 * image.width * image.channel + x1 * image.channel + k);
|
||||
@@ -508,23 +428,26 @@ float means[3] = {0.48145466, 0.4578275, 0.40821073};
|
||||
float stds[3] = {0.26862954, 0.26130258, 0.27577711};
|
||||
|
||||
// Function to clip and preprocess sd_image_f32_t
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int size) {
|
||||
float scale = (float)size / fmin(image.width, image.height);
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int target_height) {
|
||||
float width_scale = (float)target_width / image.width;
|
||||
float height_scale = (float)target_height / image.height;
|
||||
|
||||
float scale = std::fmax(width_scale, height_scale);
|
||||
|
||||
// Interpolation
|
||||
int new_width = (int)(scale * image.width);
|
||||
int new_height = (int)(scale * image.height);
|
||||
float* resized_data = (float*)malloc(new_width * new_height * image.channel * sizeof(float));
|
||||
int resized_width = (int)(scale * image.width);
|
||||
int resized_height = (int)(scale * image.height);
|
||||
float* resized_data = (float*)malloc(resized_width * resized_height * image.channel * sizeof(float));
|
||||
|
||||
for (int y = 0; y < new_height; y++) {
|
||||
for (int x = 0; x < new_width; x++) {
|
||||
float original_x = (float)x * image.width / new_width;
|
||||
float original_y = (float)y * image.height / new_height;
|
||||
for (int y = 0; y < resized_height; y++) {
|
||||
for (int x = 0; x < resized_width; x++) {
|
||||
float original_x = (float)x * image.width / resized_width;
|
||||
float original_y = (float)y * image.height / resized_height;
|
||||
|
||||
int x1 = (int)original_x;
|
||||
int y1 = (int)original_y;
|
||||
int x2 = x1 + 1;
|
||||
int y2 = y1 + 1;
|
||||
uint32_t x1 = (uint32_t)original_x;
|
||||
uint32_t y1 = (uint32_t)original_y;
|
||||
uint32_t x2 = std::min(x1 + 1, image.width - 1);
|
||||
uint32_t y2 = std::min(y1 + 1, image.height - 1);
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
float v1 = *(image.data + y1 * image.width * image.channel + x1 * image.channel + k);
|
||||
@@ -537,26 +460,28 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int size) {
|
||||
|
||||
float value = interpolate(v1, v2, v3, v4, x_ratio, y_ratio);
|
||||
|
||||
*(resized_data + y * new_width * image.channel + x * image.channel + k) = value;
|
||||
*(resized_data + y * resized_width * image.channel + x * image.channel + k) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clip and preprocess
|
||||
int h = (new_height - size) / 2;
|
||||
int w = (new_width - size) / 2;
|
||||
int h_offset = std::max((int)(resized_height - target_height) / 2, 0);
|
||||
int w_offset = std::max((int)(resized_width - target_width) / 2, 0);
|
||||
|
||||
sd_image_f32_t result;
|
||||
result.width = size;
|
||||
result.height = size;
|
||||
result.width = target_width;
|
||||
result.height = target_height;
|
||||
result.channel = image.channel;
|
||||
result.data = (float*)malloc(size * size * image.channel * sizeof(float));
|
||||
result.data = (float*)malloc(target_height * target_width * image.channel * sizeof(float));
|
||||
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
*(result.data + i * size * image.channel + j * image.channel + k) =
|
||||
fmin(fmax(*(resized_data + (i + h) * new_width * image.channel + (j + w) * image.channel + k), 0.0f), 255.0f) / 255.0f;
|
||||
for (int i = 0; i < result.height; i++) {
|
||||
for (int j = 0; j < result.width; j++) {
|
||||
int src_y = std::min(i + h_offset, resized_height - 1);
|
||||
int src_x = std::min(j + w_offset, resized_width - 1);
|
||||
*(result.data + i * result.width * image.channel + j * image.channel + k) =
|
||||
fmin(fmax(*(resized_data + src_y * resized_width * image.channel + src_x * image.channel + k), 0.0f), 255.0f) / 255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,10 +491,10 @@ sd_image_f32_t clip_preprocess(sd_image_f32_t image, int size) {
|
||||
|
||||
// Normalize
|
||||
for (int k = 0; k < image.channel; k++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
for (int j = 0; j < size; j++) {
|
||||
for (int i = 0; i < result.height; i++) {
|
||||
for (int j = 0; j < result.width; j++) {
|
||||
// *(result.data + i * size * image.channel + j * image.channel + k) = 0.5f;
|
||||
int offset = i * size * image.channel + j * image.channel + k;
|
||||
int offset = i * result.width * image.channel + j * image.channel + k;
|
||||
float value = *(result.data + offset);
|
||||
value = (value - means[k]) / stds[k];
|
||||
// value = 0.5f;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "stable-diffusion.h"
|
||||
|
||||
#define SAFE_STR(s) ((s) ? (s) : "")
|
||||
#define BOOL_STR(b) ((b) ? "true" : "false")
|
||||
|
||||
bool ends_with(const std::string& str, const std::string& ending);
|
||||
bool starts_with(const std::string& str, const std::string& start);
|
||||
bool contains(const std::string& str, const std::string& substr);
|
||||
@@ -15,18 +18,15 @@ std::string format(const char* fmt, ...);
|
||||
|
||||
void replace_all_chars(std::string& str, char target, char replacement);
|
||||
|
||||
int round_up_to(int value, int base);
|
||||
|
||||
bool file_exists(const std::string& filename);
|
||||
bool is_directory(const std::string& path);
|
||||
std::string get_full_path(const std::string& dir, const std::string& filename);
|
||||
|
||||
std::vector<std::string> get_files_from_dir(const std::string& dir);
|
||||
|
||||
std::u32string utf8_to_utf32(const std::string& utf8_str);
|
||||
std::string utf32_to_utf8(const std::u32string& utf32_str);
|
||||
std::u32string unicode_value_to_utf32(int unicode_value);
|
||||
|
||||
sd_image_t* preprocess_id_image(sd_image_t* img);
|
||||
|
||||
// std::string sd_basename(const std::string& path);
|
||||
|
||||
typedef struct {
|
||||
@@ -42,10 +42,10 @@ sd_image_f32_t sd_image_t_to_sd_image_f32_t(sd_image_t image);
|
||||
|
||||
sd_image_f32_t resize_sd_image_f32_t(sd_image_f32_t image, int target_width, int target_height);
|
||||
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int size);
|
||||
sd_image_f32_t clip_preprocess(sd_image_f32_t image, int target_width, int target_height);
|
||||
|
||||
std::string path_join(const std::string& p1, const std::string& p2);
|
||||
std::vector<std::string> splitString(const std::string& str, char delimiter);
|
||||
std::vector<std::string> split_string(const std::string& str, char delimiter);
|
||||
void pretty_progress(int step, int steps, float time);
|
||||
|
||||
void log_printf(sd_log_level_t level, const char* file, int line, const char* format, ...);
|
||||
|
||||
@@ -163,8 +163,8 @@ public:
|
||||
|
||||
class VideoResnetBlock : public ResnetBlock {
|
||||
protected:
|
||||
void init_params(struct ggml_context* ctx, std::map<std::string, enum ggml_type>& tensor_types, const std::string prefix = "") {
|
||||
enum ggml_type wtype = (tensor_types.find(prefix + "mix_factor") != tensor_types.end()) ? tensor_types[prefix + "mix_factor"] : GGML_TYPE_F32;
|
||||
void init_params(struct ggml_context* ctx, const String2GGMLType& tensor_types = {}, const std::string prefix = "") {
|
||||
enum ggml_type wtype = get_type(prefix + "mix_factor", tensor_types, GGML_TYPE_F32);
|
||||
params["mix_factor"] = ggml_new_tensor_1d(ctx, wtype, 1);
|
||||
}
|
||||
|
||||
@@ -520,20 +520,56 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct AutoEncoderKL : public GGMLRunner {
|
||||
struct VAE : public GGMLRunner {
|
||||
VAE(ggml_backend_t backend, bool offload_params_to_cpu)
|
||||
: GGMLRunner(backend, offload_params_to_cpu) {}
|
||||
virtual void compute(const int n_threads,
|
||||
struct ggml_tensor* z,
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx) = 0;
|
||||
virtual void get_param_tensors(std::map<std::string, struct ggml_tensor*>& tensors, const std::string prefix) = 0;
|
||||
virtual void enable_conv2d_direct(){};
|
||||
virtual void set_conv2d_scale(float scale) { SD_UNUSED(scale); };
|
||||
};
|
||||
|
||||
struct AutoEncoderKL : public VAE {
|
||||
bool decode_only = true;
|
||||
AutoencodingEngine ae;
|
||||
|
||||
AutoEncoderKL(ggml_backend_t backend,
|
||||
std::map<std::string, enum ggml_type>& tensor_types,
|
||||
bool offload_params_to_cpu,
|
||||
const String2GGMLType& tensor_types,
|
||||
const std::string prefix,
|
||||
bool decode_only = false,
|
||||
bool use_video_decoder = false,
|
||||
SDVersion version = VERSION_SD1)
|
||||
: decode_only(decode_only), ae(decode_only, use_video_decoder, version), GGMLRunner(backend) {
|
||||
: decode_only(decode_only), ae(decode_only, use_video_decoder, version), VAE(backend, offload_params_to_cpu) {
|
||||
ae.init(params_ctx, tensor_types, prefix);
|
||||
}
|
||||
|
||||
void enable_conv2d_direct() {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
ae.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->enable_direct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_conv2d_scale(float scale) {
|
||||
std::vector<GGMLBlock*> blocks;
|
||||
ae.get_all_blocks(blocks);
|
||||
for (auto block : blocks) {
|
||||
if (block->get_desc() == "Conv2d") {
|
||||
auto conv_block = (Conv2d*)block;
|
||||
conv_block->set_scale(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_desc() {
|
||||
return "vae";
|
||||
}
|
||||
@@ -559,12 +595,13 @@ struct AutoEncoderKL : public GGMLRunner {
|
||||
bool decode_graph,
|
||||
struct ggml_tensor** output,
|
||||
struct ggml_context* output_ctx = NULL) {
|
||||
GGML_ASSERT(!decode_only || decode_graph);
|
||||
auto get_graph = [&]() -> struct ggml_cgraph* {
|
||||
return build_graph(z, decode_graph);
|
||||
};
|
||||
// ggml_set_f32(z, 0.5f);
|
||||
// print_ggml_tensor(z);
|
||||
GGMLRunner::compute(get_graph, n_threads, true, output, output_ctx);
|
||||
GGMLRunner::compute(get_graph, n_threads, false, output, output_ctx);
|
||||
}
|
||||
|
||||
void test() {
|
||||
|
||||
+139322
File diff suppressed because it is too large
Load Diff
+762304
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user