Compare commits

...

109 Commits

Author SHA1 Message Date
98784a00d8 Merge branch 'master' of https://github.com/DarthAffe/StableDiffusion.NET 2025-12-07 10:55:34 +01:00
8e61dead47 Updated header-file 2025-12-07 10:55:29 +01:00
49626b0cb4
Merge pull request #66 from DarthAffe/sdccp_update
Small naming changes in sd.cpp
2025-12-07 10:17:30 +01:00
8832456b8f Small naming changes in sd.cpp 2025-12-07 10:15:36 +01:00
3ec6acb6ea
Merge pull request #65 from DarthAffe/sdcppUpdate
Updated sd.cpp
2025-11-30 17:47:43 +01:00
66568ffee7 Updated ref-header 2025-11-30 17:46:45 +01:00
1dcbe72e2b Updated sd.cpp 2025-11-30 17:45:49 +01:00
06b97ba3d8
Merge pull request #64 from DarthAffe/sdcppUpdate
updated sd.cpp
2025-11-30 13:44:41 +01:00
1fbe0de7c6 updated sd.cpp 2025-11-23 11:54:20 +01:00
b147a7a63e
Merge pull request #63 from DarthAffe/net10
Added .net 10 target framework
2025-11-16 11:55:32 +01:00
82f25d1fda
Merge pull request #62 from DarthAffe/BuildFix
Fixed backend-build
2025-11-16 11:55:17 +01:00
8780800766
Merge pull request #61 from DarthAffe/Preview
Updated sd.cpp and added preview-event
2025-11-16 11:55:06 +01:00
020a3f02eb Added .net 10 target framework 2025-11-15 11:38:51 +01:00
7d3e8c301d updated reference header 2025-11-15 11:31:50 +01:00
059e4ec299 Fixed backend-build 2025-11-15 11:31:02 +01:00
4b5d4c9b07 Updated sd.cpp and added preview-event 2025-11-15 11:28:04 +01:00
a2827a082d
Merge pull request #60 from DarthAffe/APIRework
Reworked everything to fit the new sd.cpp API
2025-10-31 19:53:04 +01:00
e564a6453c Updated readme, removed cuda 11 from build 2025-10-28 09:16:01 +01:00
dd01e98caa Merge 2025-10-27 20:00:27 +01:00
68064e282d Updated readme 2025-10-27 19:56:48 +01:00
4348323abc Added Params-Extension for FreeParamsImmediately 2025-10-27 19:56:39 +01:00
e6297d485f Mapped FreeParamsImmediately-Parameter 2025-10-27 19:46:46 +01:00
305d0a6f3a Updated backends-build 2025-10-27 19:43:07 +01:00
e25d8ad5c9 Removed unused method 2025-10-27 19:34:14 +01:00
07a65ead6d Finished update to latest sd.cpp 2025-10-25 17:19:11 +02:00
1ec8d67357 Updated readme 2025-08-17 00:06:05 +02:00
76dc851139 Reworked everything to fit the new sd.cpp API 2025-08-16 23:12:24 +02:00
c214f0c5a1
Disabled cuda11-builds for now 2025-07-04 14:38:11 +02:00
8834ab9543
Update backends.yml 2025-07-04 09:59:04 +02:00
dd8e41cca1
Update backends.yml 2025-07-03 23:47:35 +02:00
1db7ae59fb
Update backends.yml 2025-07-03 22:29:26 +02:00
849845b0a2
Merge pull request #58 from DarthAffe/Chroma
Added chroma model parameters
2025-06-30 23:51:51 +02:00
89cf3108df Added chroma model parameters 2025-06-30 23:51:11 +02:00
bbf5743143
Merge pull request #57 from DarthAffe/EditMode
Added edit-mode
2025-06-30 21:31:02 +02:00
78405c9543 Added edit-mode 2025-06-30 21:24:49 +02:00
b740605efa
Merge pull request #56 from DarthAffe/DarthAffe-patch-2
Disabled windows sycl builds
2025-06-29 12:33:48 +02:00
615709b49a
Disabled windows sycl builds 2025-06-29 12:33:27 +02:00
f2476cce35
Update ImageCreationUI.csproj 2025-03-02 22:56:08 +01:00
f233c7b68c
Merge pull request #51 from DarthAffe/sd.cpp_update
Updated for latest sd.cpp
2025-03-02 22:54:07 +01:00
2c7a992692 Updated for latest sd.cpp 2025-03-02 22:53:27 +01:00
67e8a96706
Update backends.yml 2025-03-01 17:57:26 +01:00
68c3bd7b9b
Merge pull request #50 from DarthAffe/backends_cuda
Update cuda verison in backends.yml
2025-03-01 17:50:43 +01:00
363f445033
Update cuda verison in backends.yml 2025-03-01 17:50:28 +01:00
d42883540f
Merge pull request #48 from DarthAffe/Inpainting
Fixed Image2Image and added Inpainting support
2025-01-05 15:06:27 +01:00
489c23395c Fixed Image2Image and added Inpainting support 2025-01-05 15:02:26 +01:00
59133b2f1e
Merge pull request #46 from DarthAffe/net9_build
Updated build for net9
2025-01-03 15:07:34 +01:00
3ee5fb9487
Updated build for net9 2025-01-03 15:06:53 +01:00
fe9ab47378
Merge pull request #45 from DarthAffe/net9
Added .net9 build target
2025-01-03 15:04:30 +01:00
b9a2f98223 Added .net9 build target 2025-01-03 15:03:21 +01:00
527149f3e5
Merge pull request #44 from DarthAffe/SD3_5
Sd3 5
2025-01-03 14:57:32 +01:00
b7e5ee7232 Added flash attention parameter to example 2025-01-03 14:56:27 +01:00
b174c2aeb6 Updated backend in example 2025-01-03 14:56:19 +01:00
f79b74efcc Added flash attention and slg layer skip parameters; added comments for parameters 2025-01-03 14:39:50 +01:00
2d7e66743d
Merge pull request #43 from DarthAffe/backend_cuda_fix-3
Fixed cuda build
2025-01-02 18:12:32 +01:00
9190b76ece
Fixed cuda build 2025-01-02 18:12:06 +01:00
6cb41217c6
Merge pull request #41 from DarthAffe/Backends
Updated build-pipeline for new version
2025-01-01 20:15:16 +01:00
9c7561ec7e Updated build-pipeline for new version 2025-01-01 20:13:15 +01:00
16d60aca7b
Merge pull request #35 from DarthAffe/SD3_5
SD3.5
2024-10-24 21:48:51 +02:00
5ed63e52be Updated example to 3.2 backends 2024-10-24 21:47:26 +02:00
3b0d7e9cf5 Updated for new SD3.5 support 2024-10-24 21:47:02 +02:00
d25eb97168
Merge pull request #33 from DarthAffe/UpscalerFix
Fixed wrong free of upscaled images
2024-10-17 21:23:12 +02:00
46bba39c4a Fixed wrong free of upscaled images 2024-10-17 21:19:38 +02:00
eea9a9afe8
Merge pull request #29 from DarthAffe/DarthAffe-patch-1
Hopefully fixed CUDA 11 build
2024-09-03 21:59:19 +02:00
5e0760715c
Hopefully fixed CUDA 11 build 2024-09-03 21:58:47 +02:00
7bd8fe77f3
Merge pull request #26 from DarthAffe/vulkan_backend
Vulkan backend
2024-08-28 20:06:02 +02:00
74af9cb8bf
Merge pull request #27 from DarthAffe/sampler
Added IPNDM and IPNDM_V sampler; added GITS and exponential schedule
2024-08-28 20:05:19 +02:00
c78f96dd57 Added vulkan backend 2024-08-28 20:02:35 +02:00
bb71986ce7 Changed backend-priority to be changeable 2024-08-28 20:02:29 +02:00
7fbbb70f90 Added vulkan nuget to backends-build 2024-08-28 20:01:42 +02:00
2335d0e596 Added IPNDM and IPNDM_V sampler; added GITS and exponential schedule 2024-08-28 00:10:06 +02:00
ec4d85c0d6 Added vulkan backend build 2024-08-27 19:53:23 +02:00
f9886433b6
Merge pull request #25 from DarthAffe/DarthAffe-patch-4
Update README.md
2024-08-25 16:58:42 +02:00
51deca52c5
Update README.md 2024-08-25 16:58:29 +02:00
4d6bc12b33
Merge pull request #24 from DarthAffe/fluxsupport
Fluxsupport
2024-08-25 16:43:55 +02:00
d8a629c8d1 Updated example for 3.0.0 2024-08-25 16:42:24 +02:00
ae70f7e5f0 Changed vae to be required for flux models and allow all non null inputs 2024-08-25 16:41:19 +02:00
da9f612c03 Made parameters of models public 2024-08-25 16:40:50 +02:00
23fc44485a Renamed SD default parameters 2024-08-25 16:39:33 +02:00
88e7fb973f Updated stablediffusion.cpp with flux flow support, refactored model creation 2024-08-25 14:45:41 +02:00
f7b7cd8a48
Merge pull request #23 from DarthAffe/SYCL
SYCL support
2024-08-10 20:52:49 +02:00
3badc9cf3b Typo 2024-08-10 20:51:24 +02:00
ecac81dd8b Added sycl backend 2024-08-10 20:51:19 +02:00
5c31ce725e Added sycl to build pipeline 2024-08-10 20:51:01 +02:00
c38694fc35 Added sycl-package config 2024-08-10 20:50:52 +02:00
ac5c3185d0
Merge pull request #21 from DarthAffe/DarthAffe-patch-3
Update backends.yml
2024-08-01 23:00:59 +02:00
bccee9a172
Merge pull request #20 from DarthAffe/Backends
Splitted CUDA-backend to prevent them hitting the nuget size limit
2024-08-01 22:46:58 +02:00
5941b20c0a Added new CUDA-nugets to backends-workflow 2024-08-01 22:45:10 +02:00
83f04892ad Splitted CUDA-backend to prevent them hitting the nuget size limit 2024-08-01 22:43:10 +02:00
c3a14c2883
Merge pull request #19 from drasticactions/load-native-library
Allow manually loading SD library
2024-08-01 21:40:16 +02:00
Timothy Miller
cde04582ac Allow manually loading SD library 2024-07-31 17:50:45 +09:00
7ae4c4b9d3
Update backends.yml 2024-07-28 14:31:08 +02:00
f21a3d876c
Merge pull request #18 from DarthAffe/Quantizations
Added new quanization-types
2024-07-28 14:14:48 +02:00
7694153d89 Added new quanization-types 2024-07-28 14:04:40 +02:00
17e3cb20b6
Merge pull request #17 from DarthAffe/DarthAffe-patch-1
Update build.bat
2024-07-28 10:35:15 +02:00
d32c47777e
Update build.bat 2024-07-28 10:35:02 +02:00
3259c31e23
Merge pull request #16 from DarthAffe/Example
Updated example, to v2.0.0; added Image2Image to example
2024-07-22 22:07:51 +02:00
743f7d4c86 Updated example, to v2.0.0; added Image2Image to example 2024-07-22 22:07:06 +02:00
f5fc37cb63
Merge pull request #15 from DarthAffe/DarthAffe-patch-2
Updated backends build-file
2024-07-22 21:44:30 +02:00
0c6b5c6ab0
Update backends.yml 2024-07-22 21:17:34 +02:00
364f07b033
Update backends.yml 2024-07-22 20:39:06 +02:00
2895e0e567
Merge pull request #13 from DarthAffe/HPPH
Updated readme
2024-07-22 19:39:39 +02:00
f813718f0d Updated readme 2024-07-22 19:38:53 +02:00
17f3e347ed
Merge pull request #12 from DarthAffe/HPPH
Changed everything image-related to use HPPH
2024-07-22 19:31:39 +02:00
c4311a1067 Changed everything image-related to use HPPH 2024-07-22 14:59:39 +02:00
dc58d4dca1
Merge pull request #7 from DarthAffe/ParameterValidation
Added Parameter validation
2024-05-06 19:45:46 +02:00
4afbcdbca8 Improved Parameter validation 2024-05-02 23:59:43 +02:00
55a8d3f261 Removed unneccessary null-forgiving operator 2024-05-02 23:54:22 +02:00
c6f99d080d Added validations for parameters 2024-05-02 23:39:00 +02:00
de732763b8 Fixed stupid typo in backend-packages 2024-04-29 21:40:23 +02:00
92 changed files with 3965 additions and 3240 deletions

View File

@ -13,72 +13,30 @@ on:
type: string
jobs:
windows:
windows-cpu:
runs-on: windows-latest
strategy:
matrix:
include:
- build: 'noavx'
defines: '-DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx'
defines: '-DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx2'
defines: '-DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx512'
defines: '-DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'cuda11'
defines: '-DSD_CUBLAS=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'cuda12'
defines: '-DSD_CUBLAS=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'rocm5'
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'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.1.2
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Install cuda-toolkit
id: cuda-toolkit-11
if: ${{ matrix.build == 'cuda11' }}
uses: Jimver/cuda-toolkit@v0.2.14
with:
cuda: '11.7.1'
method: network
sub-packages: '["nvcc", "cudart", "cublas", "cublas_dev", "thrust", "visual_studio_integration"]'
use-github-cache: false
use-local-cache: false
- name: Install cuda-toolkit
id: cuda-toolkit-12
if: ${{ matrix.build == 'cuda12' }}
uses: Jimver/cuda-toolkit@v0.2.14
with:
cuda: '12.2.0'
method: network
sub-packages: '["nvcc", "cudart", "cublas", "cublas_dev", "thrust", "visual_studio_integration"]'
use-github-cache: false
use-local-cache: false
- name: Install rocm-toolkit
id: rocm-toolkit
if: ${{ matrix.build == 'rocm5' }}
uses: Cyberhan123/rocm-toolkit@v0.1.0
with:
rocm: '5.5.0'
- name: Install Ninja
id: install-ninja
if: ${{ matrix.build == 'rocm5' }}
uses: urkle/action-get-ninja@v1
with:
version: 1.11.1
- name: Build
id: cmake_build
run: |
@ -89,67 +47,181 @@ jobs:
- name: Upload artifact
id: upload_artifact
if: ${{ matrix.build != 'rocm5' }}
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.6.1
with:
name: windows-${{ matrix.build }}
path: .\build\bin\Release\stable-diffusion.dll
- name: Upload artifact Rocm
id: upload_artifact_rocm
if: ${{ matrix.build == 'rocm5' }}
uses: actions/upload-artifact@v4.3.1
with:
name: windows-${{ matrix.build }}
path: .\build\bin\stable-diffusion.dll
windows-cuda12:
runs-on: windows-latest
linux:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- build: 'noavx'
defines: '-DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx'
defines: '-DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx2'
defines: '-DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx512'
defines: '-DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'cuda11'
defines: '-DSD_CUBLAS=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'cuda12'
defines: '-DSD_CUBLAS=ON -DSD_BUILD_SHARED_LIBS=ON'
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.1.2
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Install cuda-toolkit
id: cuda-toolkit-11
if: ${{ matrix.build == 'cuda11' }}
uses: Jimver/cuda-toolkit@v0.2.14
- name: Install Cuda Toolkit 12.8
id: cuda-12
run: |
mkdir -p "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8"
choco install unzip -y
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cudart/windows-x86_64/cuda_cudart-windows-x86_64-12.8.57-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvcc/windows-x86_64/cuda_nvcc-windows-x86_64-12.8.61-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvrtc/windows-x86_64/cuda_nvrtc-windows-x86_64-12.8.61-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/libcublas/windows-x86_64/libcublas-windows-x86_64-12.8.3.14-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvtx/windows-x86_64/cuda_nvtx-windows-x86_64-12.8.55-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_profiler_api/windows-x86_64/cuda_profiler_api-windows-x86_64-12.8.55-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/visual_studio_integration/windows-x86_64/visual_studio_integration-windows-x86_64-12.8.55-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_nvprof/windows-x86_64/cuda_nvprof-windows-x86_64-12.8.57-archive.zip"
curl -O "https://developer.download.nvidia.com/compute/cuda/redist/cuda_cccl/windows-x86_64/cuda_cccl-windows-x86_64-12.8.55-archive.zip"
unzip '*.zip' -d "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8"
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_cudart-windows-x86_64-12.8.57-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_nvcc-windows-x86_64-12.8.61-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_nvrtc-windows-x86_64-12.8.61-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\libcublas-windows-x86_64-12.8.3.14-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_nvtx-windows-x86_64-12.8.55-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_profiler_api-windows-x86_64-12.8.55-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\visual_studio_integration-windows-x86_64-12.8.55-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_nvprof-windows-x86_64-12.8.57-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\cuda_cccl-windows-x86_64-12.8.55-archive\*" "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" /E /I /H /Y
xcopy "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\visual_studio_integration\MSBuildExtensions\*" "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\BuildCustomizations" /E /I /H /Y
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8\libnvvp" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "CUDA_PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
echo "CUDA_PATH_V12_8=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.8" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Build
id: cmake_build
shell: cmd
run: |
mkdir build
cd build
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
cmake .. -DSD_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="61;75;86;89;100" -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.6.1
with:
cuda: '11.7.1'
method: network
use-github-cache: false
use-local-cache: false
- name: Install cuda-toolkit
id: cuda-toolkit-12
if: ${{ matrix.build == 'cuda12' }}
uses: Jimver/cuda-toolkit@v0.2.14
name: windows-cuda12
path: .\build\bin\Release\stable-diffusion.dll
windows-hip:
runs-on: windows-latest
steps:
- name: Install
id: depends
run: |
$ErrorActionPreference = "Stop"
write-host "Downloading AMD HIP SDK Installer"
Invoke-WebRequest -Uri "https://download.amd.com/developer/eula/rocm-hub/AMD-Software-PRO-Edition-25.Q3-WinSvr2022-For-HIP.exe" -OutFile "${env:RUNNER_TEMP}\rocm-install.exe"
write-host "Installing AMD HIP SDK"
Start-Process "${env:RUNNER_TEMP}\rocm-install.exe" -ArgumentList '-install' -NoNewWindow -Wait
write-host "Completed AMD HIP SDK installation"
- name: Verify ROCm
id: verify
run: |
# Find and test ROCm installation
$clangPath = Get-ChildItem 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | Select-Object -First 1
if (-not $clangPath) {
Write-Error "ROCm installation not found"
exit 1
}
& $clangPath.FullName --version
- name: Checkout
id: checkout
uses: actions/checkout@v4.2.2
with:
cuda: '12.2.0'
method: network
use-github-cache: false
use-local-cache: false
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Build
id: cmake_build
run: |
$env:HIP_PATH=$(Resolve-Path 'C:\Program Files\AMD\ROCm\*\bin\clang.exe' | split-path | split-path)
$env:CMAKE_PREFIX_PATH="${env:HIP_PATH}"
mkdir build
cd build
cmake -G "Unix Makefiles" .. -DCMAKE_C_COMPILER="${env:HIP_PATH}\bin\clang.exe" -DCMAKE_CXX_COMPILER="${env:HIP_PATH}\bin\clang++.exe" -DGGML_HIP=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS="gfx1100;gfx1101;gfx1030" -DGGML_RPC=ON -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.6.1
with:
name: windows-rocm6
path: .\build\bin\stable-diffusion.dll
windows-vulkan:
runs-on: windows-latest
env:
VULKAN_VERSION: 1.4.328.1
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Install Vulkan SDK
id: get_vulkan
run: |
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"
- name: Build
id: cmake_build
run: |
mkdir build
cd build
cmake .. -DSD_VULKAN=ON -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.6.1
with:
name: windows-vulkan
path: .\build\bin\Release\stable-diffusion.dll
linux-cpu:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- build: 'noavx'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx2'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx512'
defines: '-DGGML_NATIVE=OFF -DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Build
id: cmake_build
run: |
@ -160,14 +232,89 @@ jobs:
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.6.1
with:
name: linux-${{ matrix.build }}
path: ./build/bin/libstable-diffusion.so
linux-cuda12:
runs-on: ubuntu-latest
container: nvidia/cuda:12.8.0-devel-ubuntu24.04
steps:
- name: Install dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
apt update
apt install -y cmake build-essential ninja-build libgomp1 git
- name: Checkout
id: checkout
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Build
id: cmake_build
run: |
mkdir build
cd build
cmake .. -DSD_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="61;75;86;89;100" -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.6.1
with:
name: linux-cuda12
path: ./build/bin/libstable-diffusion.so
linux-sycl:
runs-on: ubuntu-latest
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
submodules: recursive
- name: Install Sycl tools
id: installSyclCompiler
shell: bash
run: |
cd /tmp
wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
rm GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
sudo apt update
sudo apt install intel-oneapi-compiler-dpcpp-cpp intel-oneapi-mkl-devel
- name: Build
id: cmake_build
run: |
source /opt/intel/oneapi/setvars.sh
mkdir build
cd build
cmake .. -DSD_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.6.1
with:
name: linux-sycl
path: ./build/bin/libstable-diffusion.so
linux-hip:
runs-on: ubuntu-22.04
container: rocm/dev-ubuntu-22.04:6.0.2
container: rocm/dev-ubuntu-22.04:6.4.4
steps:
- name: Dependencies
@ -179,7 +326,7 @@ jobs:
- name: Clone
id: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
@ -190,35 +337,35 @@ jobs:
run: |
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=hipcc -DCMAKE_CXX_COMPILER=hipcc -DSD_HIPBLAS=ON -DCMAKE_BUILD_TYPE=Release -DAMDGPU_TARGETS="gfx1100;gfx1102;gfx1030" -DSD_BUILD_SHARED_LIBS=ON
cmake .. -DCMAKE_HIP_COMPILER="$(hipconfig -l)/clang" -DGGML_HIP=ON -DSD_BUILD_SHARED_LIBS=ON
cmake --build . --config Release
- name: Upload artifact Rocm
id: upload_artifact_rocm
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.6.1
with:
name: linux-rocm6
path: ./build/bin/libstable-diffusion.so
osx:
osx-cpu:
runs-on: macos-latest
strategy:
matrix:
include:
- build: 'noavx'
defines: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx'
defines: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx2'
defines: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON'
- build: 'avx512'
defines: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
defines: '-DGGML_NATIVE=OFF -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON'
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.1.2
uses: actions/checkout@v4.2.2
with:
repository: 'leejet/stable-diffusion.cpp'
ref: '${{ github.event.inputs.commit }}'
@ -234,28 +381,34 @@ jobs:
- name: Upload artifact
id: upload_artifact
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.6.1
with:
name: osx-${{ matrix.build }}
path: ./build/bin/libstable-diffusion.dylib
release:
runs-on: ubuntu-latest
runs-on: windows-latest
needs:
- windows
- linux
- windows-cpu
- windows-cuda12
# - windows-sycl
- windows-hip
- windows-vulkan
- linux-cpu
- linux-cuda12
- linux-sycl
- linux-hip
- osx
- osx-cpu
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4.1.2
uses: actions/checkout@v4.2.2
- name: Download artifacts
id: download_artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v4.1.9
with:
path: Backends
@ -264,22 +417,25 @@ jobs:
- name: Setup nuget
id: setup_nuget
uses: NuGet/setup-nuget@v2.0.0
uses: NuGet/setup-nuget@v2.0.1
- name: Pack
id: pack
run: |
nuget pack ./Backends/StableDiffusion.NET.Backend.Cpu.nuspec -version ${{ github.event.inputs.version }}
nuget pack ./Backends/StableDiffusion.NET.Backend.Cuda12.Windows.nuspec -version ${{ github.event.inputs.version }}
nuget pack ./Backends/StableDiffusion.NET.Backend.Cuda12.Linux.nuspec -version ${{ github.event.inputs.version }}
nuget pack ./Backends/StableDiffusion.NET.Backend.Cuda.nuspec -version ${{ github.event.inputs.version }}
nuget pack ./Backends/StableDiffusion.NET.Backend.Rocm.nuspec -version ${{ github.event.inputs.version }}
nuget pack ./Backends/StableDiffusion.NET.Backend.Vulkan.nuspec -version ${{ github.event.inputs.version }}
- name: Upload artifacts
id: upload_artifacts
uses: actions/upload-artifact@v4.3.1
uses: actions/upload-artifact@v4.6.1
with:
name: StableDiffusion.NET.Backend-Nugets
path: ./*.nupkg
- name: Nuget Push
id: nuget_push
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json

View File

@ -19,9 +19,12 @@ jobs:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
dotnet-version: 8.0.x
dotnet-version: |
10.0.x
9.0.x
8.0.x
- name: Restore dependencies
run: dotnet restore StableDiffusion.NET
@ -47,7 +50,7 @@ jobs:
with:
tag_name: ${{ github.event.inputs.version }}
generate_release_notes: true
files: StableDiffusion.NET/bin/Release/net8.0/StableDiffusion.NET.dll
files: StableDiffusion.NET/bin/Release/net10.0/StableDiffusion.NET.dll
- name: Nuget Push
id: nuget_push

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDifffusion.NET.Backend.Cpu</id>
<id>StableDiffusion.NET.Backend.Cpu</id>
<version>$version$</version>
<title>StableDifffusion.NET.Backend.Cpu</title>
<title>StableDiffusion.NET.Backend.Cpu</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>

View File

@ -1,32 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDifffusion.NET.Backend.Cuda</id>
<id>StableDiffusion.NET.Backend.Cuda</id>
<version>$version$</version>
<title>StableDifffusion.NET.Backend.Cuda</title>
<title>StableDiffusion.NET.Backend.Cuda</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>sd_net_cuda.png</icon>
<projectUrl>https://github.com/DarthAffe/StableDiffusion.NET</projectUrl>
<description>CUDA-Backend (11 and 12) for StableDiffusion.NET.</description>
<description>CUDA-Backend (12) for StableDiffusion.NET.</description>
<releaseNotes></releaseNotes>
<copyright>Copyright © Darth Affe 2024</copyright>
<readme>readme.md</readme>
<dependencies>
<dependency id="StableDiffusion.NET.Backend.Cuda12.Windows" version="$version$" />
<dependency id="StableDiffusion.NET.Backend.Cuda12.Linux" version="$version$" />
</dependencies>
</metadata>
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Cuda.props" />
<file src="windows-cuda11/stable-diffusion.dll" target="runtimes\win-x64\native\cuda11\stable-diffusion.dll" />
<file src="windows-cuda12/stable-diffusion.dll" target="runtimes\win-x64\native\cuda12\stable-diffusion.dll" />
<file src="linux-cuda11/libstable-diffusion.so" target="runtimes\linux-x64\native\cuda11\libstable-diffusion.so" />
<file src="linux-cuda12/libstable-diffusion.so" target="runtimes\linux-x64\native\cuda12\libstable-diffusion.so" />
<file src="sd_net_cuda.png" target="sd_net_cuda.png" />
<file src="readme.md" target="readme.md" />
<file src="ggml.txt" target="ggml.txt" />
<file src="stable-diffusion.cpp.txt" target="stable-diffusion.cpp.txt" />
</files>
</package>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDiffusion.NET.Backend.Cuda12.Linux</id>
<version>$version$</version>
<title>StableDiffusion.NET.Backend.Cuda12.Linux</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>sd_net_cuda.png</icon>
<projectUrl>https://github.com/DarthAffe/StableDiffusion.NET</projectUrl>
<description>CUDA 12 Linux Backend for StableDiffusion.NET.</description>
<releaseNotes></releaseNotes>
<copyright>Copyright © Darth Affe 2024</copyright>
<readme>readme.md</readme>
</metadata>
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Cuda.props" />
<file src="linux-cuda12/libstable-diffusion.so" target="runtimes\linux-x64\native\cuda12\libstable-diffusion.so" />
<file src="sd_net_cuda.png" target="sd_net_cuda.png" />
<file src="readme.md" target="readme.md" />
<file src="ggml.txt" target="ggml.txt" />
<file src="stable-diffusion.cpp.txt" target="stable-diffusion.cpp.txt" />
</files>
</package>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDiffusion.NET.Backend.Cuda12.Windows</id>
<version>$version$</version>
<title>StableDiffusion.NET.Backend.Cuda12.Windows</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>sd_net_cuda.png</icon>
<projectUrl>https://github.com/DarthAffe/StableDiffusion.NET</projectUrl>
<description>CUDA 12 Windows Backend for StableDiffusion.NET.</description>
<releaseNotes></releaseNotes>
<copyright>Copyright © Darth Affe 2024</copyright>
<readme>readme.md</readme>
</metadata>
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Cuda.props" />
<file src="windows-cuda12/stable-diffusion.dll" target="runtimes\win-x64\native\cuda12\stable-diffusion.dll" />
<file src="sd_net_cuda.png" target="sd_net_cuda.png" />
<file src="readme.md" target="readme.md" />
<file src="ggml.txt" target="ggml.txt" />
<file src="stable-diffusion.cpp.txt" target="stable-diffusion.cpp.txt" />
</files>
</package>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDifffusion.NET.Backend.Rocm</id>
<id>StableDiffusion.NET.Backend.Rocm</id>
<version>$version$</version>
<title>StableDifffusion.NET.Backend.Rocm</title>
<title>StableDiffusion.NET.Backend.Rocm</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
@ -18,8 +18,8 @@
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Rocm.props" />
<file src="windows-rocm5/stable-diffusion.dll" target="runtimes\win-x64\native\rocm5\stable-diffusion.dll" />
<file src="windows-rocm6/stable-diffusion.dll" target="runtimes\win-x64\native\rocm6\stable-diffusion.dll" />
<file src="linux-rocm6/libstable-diffusion.so" target="runtimes\linux-x64\native\rocm6\libstable-diffusion.so" />
<file src="sd_net_rocm.png" target="sd_net_rocm.png" />

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDiffusion.NET.Backend.Sycl</id>
<version>$version$</version>
<title>StableDiffusion.NET.Backend.Sycl</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>sd_net_sycl.png</icon>
<projectUrl>https://github.com/DarthAffe/StableDiffusion.NET</projectUrl>
<description>SYCL-Backend for StableDiffusion.NET.</description>
<releaseNotes></releaseNotes>
<copyright>Copyright © Darth Affe 2024</copyright>
<readme>readme.md</readme>
</metadata>
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Sycl.props" />
<file src="windows-sycl/stable-diffusion.dll" target="runtimes\win-x64\native\sycl\stable-diffusion.dll" />
<file src="linux-sycl/libstable-diffusion.so" target="runtimes\linux-x64\native\sycl\libstable-diffusion.so" />
<file src="sd_net_sycl.png" target="sd_net_sycl.png" />
<file src="readme.md" target="readme.md" />
<file src="ggml.txt" target="ggml.txt" />
<file src="stable-diffusion.cpp.txt" target="stable-diffusion.cpp.txt" />
</files>
</package>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<package>
<metadata>
<id>StableDiffusion.NET.Backend.Vulkan</id>
<version>$version$</version>
<title>StableDiffusion.NET.Backend.Vulkan</title>
<authors>Darth Affe &amp; stable-diffusion.cpp Authors</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<icon>sd_net_vulkan.png</icon>
<projectUrl>https://github.com/DarthAffe/StableDiffusion.NET</projectUrl>
<description>Vulkan-Backend for StableDiffusion.NET.</description>
<releaseNotes></releaseNotes>
<copyright>Copyright © Darth Affe 2024</copyright>
<readme>readme.md</readme>
</metadata>
<files>
<file src="StableDiffusion.NET.Backend.props" target="build/net8.0/StableDiffusion.NET.Backend.Vulkan.props" />
<file src="windows-vulkan/stable-diffusion.dll" target="runtimes\win-x64\native\vulkan\stable-diffusion.dll" />
<file src="sd_net_vulkan.png" target="sd_net_vulkan.png" />
<file src="readme.md" target="readme.md" />
<file src="ggml.txt" target="ggml.txt" />
<file src="stable-diffusion.cpp.txt" target="stable-diffusion.cpp.txt" />
</files>
</package>

View File

@ -0,0 +1,19 @@
:: MIT license
:: Copyright (C) 2024 Intel Corporation
:: SPDX-License-Identifier: MIT
set URL=%1
set COMPONENTS=%2
curl.exe --output %TEMP%\webimage.exe --url %URL% --retry 5 --retry-delay 5
start /b /wait %TEMP%\webimage.exe -s -x -f webimage_extracted --log extract.log
del %TEMP%\webimage.exe
if "%COMPONENTS%"=="" (
webimage_extracted\bootstrapper.exe -s --action install --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=.
) else (
webimage_extracted\bootstrapper.exe -s --action install --components=%COMPONENTS% --eula=accept -p=NEED_VS2017_INTEGRATION=0 -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=.
)
set installer_exit_code=%ERRORLEVEL%
rd /s/q "webimage_extracted"
exit /b %installer_exit_code%

BIN
Backends/sd_net_sycl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

BIN
Backends/sd_net_vulkan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

View File

@ -0,0 +1,24 @@
:: MIT license
:: Copyright (C) 2024 Intel Corporation
:: SPDX-License-Identifier: MIT
IF not exist build (mkdir build)
cd build
if %errorlevel% neq 0 goto ERROR
@call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" intel64 --force
if %errorlevel% neq 0 goto ERROR
cmake -G "Ninja" .. -DGGML_SYCL=ON -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=icx -DSD_BUILD_SHARED_LIBS=ON
if %errorlevel% neq 0 goto ERROR
cmake --build . -j --config Release
if %errorlevel% neq 0 goto ERROR
cd ..
exit /B 0
:ERROR
echo comomand error: %errorlevel%
exit /B %errorlevel%

View File

@ -1,42 +0,0 @@
using System.Windows.Input;
namespace ImageCreationUI;
public class ActionCommand(Action command, Func<bool>? canExecute = null) : ICommand
{
#region Events
public event EventHandler? CanExecuteChanged;
#endregion
#region Methods
public bool CanExecute(object? parameter) => canExecute?.Invoke() ?? true;
public void Execute(object? parameter) => command.Invoke();
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
#endregion
}
public class ActionCommand<T>(Action<T> command, Func<T, bool>? canExecute = null) : ICommand
where T : class
{
#region Events
public event EventHandler? CanExecuteChanged;
#endregion
#region Methods
public bool CanExecute(object? parameter) => canExecute?.Invoke((T)parameter!) ?? true;
public void Execute(object? parameter) => command.Invoke((T)parameter!);
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
#endregion
}

View File

@ -1,4 +0,0 @@
<Application x:Class="ImageCreationUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" />

View File

@ -1,14 +0,0 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace ImageCreationUI
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -1,33 +0,0 @@
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using StableDiffusion.NET.Helper.Images;
namespace ImageCreationUI.Converter;
[ValueConversion(typeof(IImage), typeof(ImageSource))]
public class ImageToImageSourceConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
Bitmap? bitmap = (value as IImage)?.ToBitmap();
if (bitmap == null) return null;
using MemoryStream ms = new();
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0;
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ms;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
return bitmapImage;
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotSupportedException();
}

View File

@ -1,77 +0,0 @@
using System.Buffers;
using System.Drawing;
using System.Drawing.Imaging;
using StableDiffusion.NET.Helper.Images.Colors;
using StableDiffusion.NET.Helper.Images;
using System.IO;
using System.Runtime.InteropServices;
namespace ImageCreationUI;
public static class ImageExtension
{
public static Bitmap ToBitmap(this IImage image) => image.AsRefImage<ColorRGB>().ToBitmap();
public static Bitmap ToBitmap(this Image<ColorRGB> image) => image.AsRefImage<ColorRGB>().ToBitmap();
public static unsafe Bitmap ToBitmap(this RefImage<ColorRGB> image)
{
Bitmap output = new(image.Width, image.Height, PixelFormat.Format24bppRgb);
Rectangle rect = new(0, 0, image.Width, image.Height);
BitmapData bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);
nint ptr = bmpData.Scan0;
foreach (ReadOnlyRefEnumerable<ColorRGB> row in image.Rows)
{
Span<ColorBGR> target = new((void*)ptr, bmpData.Stride);
for (int i = 0; i < row.Length; i++)
{
ColorRGB srcColor = row[i];
target[i] = new ColorBGR(srcColor.B, srcColor.G, srcColor.R);
}
ptr += bmpData.Stride;
}
output.UnlockBits(bmpData);
return output;
}
public static byte[] ToPng(this IImage image)
{
using Bitmap bitmap = image.ToBitmap();
using MemoryStream ms = new();
bitmap.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
public static unsafe Image<ColorRGB> ToImage(this Bitmap bitmap)
{
int width = bitmap.Width;
int height = bitmap.Height;
byte[] buffer = new byte[height * width * ColorRGB.ColorFormat.BytesPerPixel];
Span<ColorRGB> colorBuffer = MemoryMarshal.Cast<byte, ColorRGB>(buffer);
Rectangle rect = new(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
nint ptr = bmpData.Scan0;
for (int y = 0; y < height; y++)
{
Span<ColorBGR> source = new((void*)ptr, bmpData.Stride);
Span<ColorRGB> target = colorBuffer.Slice(y * width, width);
for (int x = 0; x < width; x++)
{
ColorBGR srcColor = source[x];
target[x] = new ColorRGB(srcColor.R, srcColor.G, srcColor.B);
}
ptr += bmpData.Stride;
}
bitmap.UnlockBits(bmpData);
return new Image<ColorRGB>(buffer, 0, 0, width, height, width);
}
}

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StableDifffusion.NET.Backend.Cpu" Version="1.2.0" />
<PackageReference Include="StableDifffusion.NET.Backend.Cuda" Version="1.2.0" />
<PackageReference Include="StableDifffusion.NET.Backend.Rocm" Version="1.2.0" />
<PackageReference Include="StableDifffusion.NET" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.4" />
</ItemGroup>
</Project>

View File

@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,115 +0,0 @@
<Window x:Class="ImageCreationUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImageCreationUI"
xmlns:converter="clr-namespace:ImageCreationUI.Converter"
xmlns:sys="clr-namespace:System;assembly=System.Runtime"
xmlns:sd="clr-namespace:StableDiffusion.NET;assembly=StableDiffusion.NET"
mc:Ignorable="d"
Title="StableDiffusion.NET" Width="1280" Height="800">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<converter:ImageToImageSourceConverter x:Key="ImageToImageSourceConverter" />
<ObjectDataProvider x:Key="ScheduleDataSource"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="sd:Schedule" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="SamplerDataSource"
ObjectType="{x:Type sys:Enum}"
MethodName="GetValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="sd:Sampler" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<StackPanel Margin="4,0" Orientation="Vertical">
<TextBlock Margin="8" Text="This is just an example - inputs are not validated!&#10;Make sure everything is correct before pressing 'Create Image'." />
<Separator />
<Label Content="Model-Path" />
<DockPanel>
<Button DockPanel.Dock="Right" Width="24" Margin="2,0,0,0" Content="..." Command="{Binding SelectModelCommand}" IsEnabled="{Binding IsReady}" />
<TextBox Text="{Binding ModelPath}" />
</DockPanel>
<Label Content="Vae-Path (Optional)" />
<DockPanel>
<Button DockPanel.Dock="Right" Width="24" Margin="2,0,0,0" Content="..." Command="{Binding SelectVaeCommand}" IsEnabled="{Binding IsReady}" />
<TextBox Text="{Binding VaePath}" />
</DockPanel>
<Label Content="Schedule" />
<ComboBox ItemsSource="{Binding Source={StaticResource ScheduleDataSource}}" SelectedItem="{Binding Schedule}" />
<Button Margin="0,8" Content="Load Model" Command="{Binding LoadModelCommand}" IsEnabled="{Binding IsReady}" />
<Separator />
<Label Margin="0,8,0,0" Content="Prompt" />
<TextBox Height="80" TextWrapping="Wrap" Text="{Binding Prompt}" />
<Label Content="AntiPrompt" />
<TextBox Height="80" TextWrapping="Wrap" Text="{Binding AntiPrompt}" />
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Width="50" Content="Width" />
<TextBox HorizontalAlignment="Left" Width="60" Text="{Binding Width}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Width="50" Content="Height" />
<TextBox HorizontalAlignment="Left" Width="60" Text="{Binding Height}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Width="50" Content="Cfg" />
<TextBox HorizontalAlignment="Left" Width="60" Text="{Binding Cfg}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Width="50" Content="Steps" />
<TextBox HorizontalAlignment="Left" Width="60" Text="{Binding Steps}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Label Width="50" Content="Seed" />
<TextBox HorizontalAlignment="Left" Width="60" Text="{Binding Seed}" />
</StackPanel>
<Label Content="Sample-Method" />
<ComboBox ItemsSource="{Binding Source={StaticResource SamplerDataSource}}" SelectedItem="{Binding SampleMethod}" />
<Button Margin="0,16,0,0" Content="Create Image" Command="{Binding CreateImageCommand}" IsEnabled="{Binding IsReady}" />
<Button Margin="0,16,0,0" Content="Save Image" Command="{Binding SaveImageCommand}" IsEnabled="{Binding IsReady}" />
</StackPanel>
<GridSplitter Grid.Column="1" Margin="2,0" Width="2" Background="DimGray" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<Image Grid.Column="2" Source="{Binding Image, Converter={StaticResource ImageToImageSourceConverter}}" />
<GridSplitter Grid.Column="3" Margin="2,0" Width="2" Background="DimGray" VerticalAlignment="Stretch" HorizontalAlignment="Center" />
<TextBox Grid.Column="4" IsReadOnly="True" BorderThickness="0" AcceptsReturn="True" Text="{Binding Log}" TextChanged="TextBoxBase_OnTextChanged" />
</Grid>
</Window>

View File

@ -1,11 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace ImageCreationUI;
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs args) => (sender as TextBox)?.ScrollToEnd();
}

View File

@ -1,264 +0,0 @@
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Win32;
using StableDiffusion.NET;
using StableDiffusion.NET.Helper.Images;
namespace ImageCreationUI;
public class MainWindowViewModel : INotifyPropertyChanged
{
#region Properties & Fields
private StableDiffusionModel? _model;
private string _modelPath = string.Empty;
public string ModelPath
{
get => _modelPath;
set => SetProperty(ref _modelPath, value);
}
private string _vaePath = string.Empty;
public string VaePath
{
get => _vaePath;
set => SetProperty(ref _vaePath, value);
}
private Schedule _schedule = Schedule.Default;
public Schedule Schedule
{
get => _schedule;
set => SetProperty(ref _schedule, value);
}
private string _prompt = string.Empty;
public string Prompt
{
get => _prompt;
set => SetProperty(ref _prompt, value);
}
private string _antiPrompt = string.Empty;
public string AntiPrompt
{
get => _antiPrompt;
set => SetProperty(ref _antiPrompt, value);
}
private int _width = 1024;
public int Width
{
get => _width;
set => SetProperty(ref _width, value);
}
private int _height = 1024;
public int Height
{
get => _height;
set => SetProperty(ref _height, value);
}
private float _cfg = 5f;
public float Cfg
{
get => _cfg;
set => SetProperty(ref _cfg, value);
}
private int _steps = 28;
public int Steps
{
get => _steps;
set => SetProperty(ref _steps, value);
}
private int _seed = -1;
public int Seed
{
get => _seed;
set => SetProperty(ref _seed, value);
}
private Sampler _sampleMethod = Sampler.Euler_A;
public Sampler SampleMethod
{
get => _sampleMethod;
set => SetProperty(ref _sampleMethod, value);
}
private IImage? _image;
public IImage? Image
{
get => _image;
set => SetProperty(ref _image, value);
}
private string _log = string.Empty;
public string Log
{
get => _log;
set => SetProperty(ref _log, value);
}
private bool _isReady = true;
public bool IsReady
{
get => _isReady;
set => SetProperty(ref _isReady, value);
}
#endregion
#region Commands
private ActionCommand? _loadModelCommand;
public ActionCommand LoadModelCommand => _loadModelCommand ??= new ActionCommand(LoadModel);
private ActionCommand? _createImageCommand;
public ActionCommand CreateImageCommand => _createImageCommand ??= new ActionCommand(CreateImage);
private ActionCommand? _saveImageCommand;
public ActionCommand SaveImageCommand => _saveImageCommand ??= new ActionCommand(SaveImage);
private ActionCommand? _selectModelCommand;
public ActionCommand SelectModelCommand => _selectModelCommand ??= new ActionCommand(SelectModel);
private ActionCommand? _selectVaeCommand;
public ActionCommand SelectVaeCommand => _selectVaeCommand ??= new ActionCommand(SelectVae);
#endregion
#region Constructors
public MainWindowViewModel()
{
try
{
StableDiffusionModel.Log += (_, args) => LogLine($"LOG [{args.Level}]: {args.Text}", false);
StableDiffusionModel.Progress += (_, args) => LogLine($"PROGRESS {args.Step} / {args.Steps} ({(args.Progress * 100):N2} %) {args.IterationsPerSecond:N2} it/s ({args.Time})");
}
catch (Exception ex)
{
LogLine($"Failed to load stable-diffussion.cpp libraries!{Environment.NewLine}{ex.Message}");
}
}
#endregion
#region Methods
private async void LoadModel()
{
try
{
IsReady = false;
_model?.Dispose();
LogLine($"Loading model '{ModelPath}'");
_model = await Task.Run(() => new StableDiffusionModel(ModelPath, new ModelParameter { VaePath = VaePath, Schedule = Schedule }));
}
catch (Exception ex)
{
LogLine($"Failed to load model ...{Environment.NewLine}{ex.Message}");
}
finally
{
IsReady = true;
}
}
private async void CreateImage()
{
try
{
IsReady = false;
LogLine("Creating image ...");
using StableDiffusionImage? image = await Task.Run(() => _model?.TextToImage(Prompt, new StableDiffusionParameter
{
NegativePrompt = AntiPrompt,
Width = Width,
Height = Height,
CfgScale = Cfg,
SampleSteps = Steps,
Seed = Seed,
SampleMethod = SampleMethod
}));
Image = image?.ToImage();
LogLine("done!");
}
catch (Exception ex)
{
LogLine($"Failed to create image ...{Environment.NewLine}{ex.Message}");
}
finally
{
IsReady = true;
}
}
private void SaveImage()
{
try
{
if (Image == null) return;
SaveFileDialog saveFileDialog = new() { Filter = "PNG File (*.png)|*.png" };
if (saveFileDialog.ShowDialog() == true)
{
File.WriteAllBytes(saveFileDialog.FileName, Image.ToPng());
LogLine($"Image saved to '{saveFileDialog.FileName}'!");
}
}
catch (Exception ex)
{
LogLine($"Failed to save image ...{Environment.NewLine}{ex.Message}");
}
}
private void SelectModel()
{
OpenFileDialog openFileDialog = new() { Filter = "Stable Diffusion Model|*.*" };
if (openFileDialog.ShowDialog() == true)
ModelPath = openFileDialog.FileName;
}
private void SelectVae()
{
OpenFileDialog openFileDialog = new() { Filter = "Stable Diffusion VAE|*.*" };
if (openFileDialog.ShowDialog() == true)
VaePath = openFileDialog.FileName;
}
private void LogLine(string line, bool appendNewLine = true)
{
if (appendNewLine)
Log += line + Environment.NewLine;
else
Log += line;
}
#endregion
#region NotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
}

360
Header/stable-diffusion.h Normal file
View File

@ -0,0 +1,360 @@
#ifndef __STABLE_DIFFUSION_H__
#define __STABLE_DIFFUSION_H__
#if defined(_WIN32) || defined(__CYGWIN__)
#ifndef SD_BUILD_SHARED_LIB
#define SD_API
#else
#ifdef SD_BUILD_DLL
#define SD_API __declspec(dllexport)
#else
#define SD_API __declspec(dllimport)
#endif
#endif
#else
#if __GNUC__ >= 4
#define SD_API __attribute__((visibility("default")))
#else
#define SD_API
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
enum rng_type_t {
STD_DEFAULT_RNG,
CUDA_RNG,
CPU_RNG,
RNG_TYPE_COUNT
};
enum sample_method_t {
EULER_SAMPLE_METHOD,
EULER_A_SAMPLE_METHOD,
HEUN_SAMPLE_METHOD,
DPM2_SAMPLE_METHOD,
DPMPP2S_A_SAMPLE_METHOD,
DPMPP2M_SAMPLE_METHOD,
DPMPP2Mv2_SAMPLE_METHOD,
IPNDM_SAMPLE_METHOD,
IPNDM_V_SAMPLE_METHOD,
LCM_SAMPLE_METHOD,
DDIM_TRAILING_SAMPLE_METHOD,
TCD_SAMPLE_METHOD,
SAMPLE_METHOD_COUNT
};
enum scheduler_t {
DISCRETE_SCHEDULER,
KARRAS_SCHEDULER,
EXPONENTIAL_SCHEDULER,
AYS_SCHEDULER,
GITS_SCHEDULER,
SGM_UNIFORM_SCHEDULER,
SIMPLE_SCHEDULER,
SMOOTHSTEP_SCHEDULER,
LCM_SCHEDULER,
SCHEDULER_COUNT
};
enum prediction_t {
EPS_PRED,
V_PRED,
EDM_V_PRED,
FLOW_PRED,
FLUX_FLOW_PRED,
FLUX2_FLOW_PRED,
PREDICTION_COUNT
};
// same as enum ggml_type
enum sd_type_t {
SD_TYPE_F32 = 0,
SD_TYPE_F16 = 1,
SD_TYPE_Q4_0 = 2,
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, 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,
};
enum sd_log_level_t {
SD_LOG_DEBUG,
SD_LOG_INFO,
SD_LOG_WARN,
SD_LOG_ERROR
};
enum preview_t {
PREVIEW_NONE,
PREVIEW_PROJ,
PREVIEW_TAE,
PREVIEW_VAE,
PREVIEW_COUNT
};
enum lora_apply_mode_t {
LORA_APPLY_AUTO,
LORA_APPLY_IMMEDIATELY,
LORA_APPLY_AT_RUNTIME,
LORA_APPLY_MODE_COUNT,
};
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* llm_path;
const char* llm_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;
const char* tensor_type_rules;
bool vae_decode_only;
bool free_params_immediately;
int n_threads;
enum sd_type_t wtype;
enum rng_type_t rng_type;
enum rng_type_t sampler_rng_type;
enum prediction_t prediction;
enum lora_apply_mode_t lora_apply_mode;
bool offload_params_to_cpu;
bool keep_clip_on_cpu;
bool keep_control_net_on_cpu;
bool keep_vae_on_cpu;
bool diffusion_flash_attn;
bool tae_preview_only;
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 {
bool enabled;
float reuse_threshold;
float start_percent;
float end_percent;
} sd_easycache_params_t;
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_easycache_params_t easycache;
} 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_easycache_params_t easycache;
} 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);
typedef void (*sd_preview_cb_t)(int step, int frame_count, sd_image_t* frames, bool is_noisy, void* data);
SD_API void sd_set_log_callback(sd_log_cb_t sd_log_cb, void* data);
SD_API void sd_set_progress_callback(sd_progress_cb_t cb, void* data);
SD_API void sd_set_preview_callback(sd_preview_cb_t cb, enum preview_t mode, int interval, bool denoised, bool noisy, void* data);
SD_API int32_t sd_get_num_physical_cores();
SD_API const char* sd_get_system_info();
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_scheduler_name(enum scheduler_t scheduler);
SD_API enum scheduler_t str_to_scheduler(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);
SD_API const char* sd_preview_name(enum preview_t preview);
SD_API enum preview_t str_to_preview(const char* str);
SD_API const char* sd_lora_apply_mode_name(enum lora_apply_mode_t mode);
SD_API enum lora_apply_mode_t str_to_lora_apply_mode(const char* str);
SD_API void sd_easycache_params_init(sd_easycache_params_t* easycache_params);
SD_API void sd_ctx_params_init(sd_ctx_params_t* sd_ctx_params);
SD_API char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params);
SD_API sd_ctx_t* new_sd_ctx(const sd_ctx_params_t* sd_ctx_params);
SD_API void free_sd_ctx(sd_ctx_t* sd_ctx);
SD_API void 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 enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx);
SD_API enum scheduler_t sd_get_default_scheduler(const sd_ctx_t* sd_ctx);
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 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 int get_upscale_factor(upscaler_ctx_t* upscaler_ctx);
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
}
#endif
#endif // __STABLE_DIFFUSION_H__

View File

@ -9,11 +9,61 @@ Based on https://github.com/leejet/stable-diffusion.cpp
## Usage
### Setup
Install the [StableDiffusion.NET](https://www.nuget.org/packages/StableDiffusion.NET)-Nuget and at least one of the [Backend-Packages](https://www.nuget.org/packages?q=StableDiffusion.NET.Backend).
StableDiffusion.NET is using semantic versioning. Backend-packages are compatible as long as the version does only differ in the last digit.
If GPU-support is available it will prefer this over CPU.
If you want to add your own native-libraries or need more control over which backend to load, check the static `Backends` class.
### Example
#### 1. Create a model
stable diffusion:
```csharp
using StableDiffusionModel sd = new(@"<path_to_model>", new ModelParameter());
using StableDiffusionImage image = sd.TextToImage("<prompt>", new StableDiffusionParameter());
// Enable the Log- and Progress-events
StableDiffusionCpp.InitializeEvents();
// Register the Log and Progress-events to capture stable-diffusion.cpp output
StableDiffusionCpp.Log += (_, args) => Console.WriteLine($"LOG [{args.Level}]: {args.Text}");
StableDiffusionCpp.Progress += (_, args) => Console.WriteLine($"PROGRESS {args.Step} / {args.Steps} ({(args.Progress * 100):N2} %) {args.IterationsPerSecond:N2} it/s ({args.Time})");
Image<ColorRGB>? treeWithTiger;
// Load a StableDiffusion model in a using block to unload it again after the two images are created
using (DiffusionModel sd = new(DiffusionModelParameter.Create()
.WithModelPath(@"<path to model>")
// .WithVae(@"<optional path to vae>")
.WithMultithreading()
.WithFlashAttention()))
{
// Create a image from a prompt
Image<ColorRGB>? tree = sd.GenerateImage(ImageGenerationParameter.TextToImage("A beautiful tree standing on a small hill").WithSDXLDefaults());
// (optional) Save the image (requires the HPPH System.Dawing or SkiaSharp extension)
File.WriteAllBytes("image1.png", tree.ToPng());
// Use the previously created image for an image-to-image creation
treeWithTiger = sd.GenerateImage(ImageGenerationParameter.ImageToImage("A cute tiger in front of a tree on a small hill", tree).WithSDXLDefaults());
File.WriteAllBytes("image2.png", treeWithTiger.ToPng());
}
// Load the qwen image edit model
using DiffusionModel qwenContext = new(DiffusionModelParameter.Create()
.WithDiffusionModelPath(@"<Qwen-Image-Edit-2509-path>")
.WithQwen2VLPath(@"<Qwen2.5-VL-7B-Instruct-path>")
.WithQwen2VLVisionPath(@"<Qwen2.5-VL-7B-Instruct.mmproj-path>")
.WithVae(@"<qwen_image_vae-path>")
.WithMultithreading()
.WithFlashAttention()
.WithFlowShift(3)
.WithOffloadedParamsToCPU()
.WithImmediatelyFreedParams());
// Perform an edit on the previously created image
Image<ColorRGB>? tigerOnMoon = qwenContext.GenerateImage(ImageGenerationParameter.TextToImage("Remove the background and place the tree and the tiger on the moon.")
.WithSize(1024, 1024)
.WithCfg(2.5f)
.WithSampler(Sampler.Euler)
.WithRefImages(treeWithTiger));
File.WriteAllBytes("image3.png", tigerOnMoon.ToPng());
```
To process the resulting image further you can write your own extensions or install one of the [HPPH](https://github.com/DarthAffe/HPPH)-extension sets:
[HPPH.System.Drawing](https://www.nuget.org/packages/HPPH.System.Drawing)
[HPPH.SkiaSharp](https://www.nuget.org/packages/HPPH.SkiaSharp)

Binary file not shown.

View File

@ -5,10 +5,7 @@ VisualStudioVersion = 17.8.34322.80
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StableDiffusion.NET", "StableDiffusion.NET\StableDiffusion.NET.csproj", "{ED9336F9-7C5F-47DD-A120-272F4835E95F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{8961F5D8-3F50-4027-8862-635FCA1E1568}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageCreationUI", "Examples\ImageCreationUI\ImageCreationUI.csproj", "{EB73E250-D2F7-469A-80BF-02C5078546A7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -19,17 +16,10 @@ Global
{ED9336F9-7C5F-47DD-A120-272F4835E95F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED9336F9-7C5F-47DD-A120-272F4835E95F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED9336F9-7C5F-47DD-A120-272F4835E95F}.Release|Any CPU.Build.0 = Release|Any CPU
{EB73E250-D2F7-469A-80BF-02C5078546A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB73E250-D2F7-469A-80BF-02C5078546A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB73E250-D2F7-469A-80BF-02C5078546A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB73E250-D2F7-469A-80BF-02C5078546A7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EB73E250-D2F7-469A-80BF-02C5078546A7} = {8961F5D8-3F50-4027-8862-635FCA1E1568}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8DD53974-8766-4659-97B6-4AEDF4323F65}
EndGlobalSection

View File

@ -14,11 +14,13 @@ public static class Backends
public static CpuBackend CpuBackend { get; } = new();
public static CudaBackend CudaBackend { get; } = new();
public static RocmBackend RocmBackend { get; } = new();
public static SyclBackend SyclBackend { get; } = new();
public static VulkanBackend VulkanBackend { get; } = new();
private static readonly List<IBackend> CUSTOM_BACKENDS = [];
public static IReadOnlyList<IBackend> CustomBackends => CUSTOM_BACKENDS.AsReadOnly();
public static IEnumerable<IBackend> RegisteredBackends => [CpuBackend, CudaBackend, RocmBackend, .. CUSTOM_BACKENDS];
public static IEnumerable<IBackend> RegisteredBackends => [CpuBackend, CudaBackend, RocmBackend, SyclBackend, VulkanBackend, .. CUSTOM_BACKENDS];
public static IEnumerable<IBackend> AvailableBackends => RegisteredBackends.Where(x => x.IsAvailable);
public static IEnumerable<IBackend> ActiveBackends => AvailableBackends.Where(x => x.IsEnabled);
@ -36,7 +38,7 @@ public static class Backends
public static bool RegisterBackend(IBackend backend)
{
if (backend is NET.CpuBackend or NET.CudaBackend or NET.RocmBackend)
if (backend is NET.CpuBackend or NET.CudaBackend or NET.RocmBackend or NET.SyclBackend or NET.VulkanBackend)
throw new ArgumentException("Default backends can't be registered again.");
if (CUSTOM_BACKENDS.Contains(backend))

View File

@ -13,7 +13,7 @@ public class CpuBackend : IBackend
public bool IsEnabled { get; set; } = true;
public int Priority => 0;
public int Priority { get; set; } = 0;
public bool IsAvailable => (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|| RuntimeInformation.IsOSPlatform(OSPlatform.Linux)

View File

@ -22,12 +22,12 @@ public partial class CudaBackend : IBackend
public bool IsEnabled { get; set; } = true;
public int Priority => 10;
public int Priority { get; set; } = 10;
public bool IsAvailable => (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|| RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
&& (RuntimeInformation.OSArchitecture == Architecture.X64)
&& CudaVersion is 11 or 12;
&& CudaVersion is 12;
public string PathPart => CudaVersion switch
{

View File

@ -6,7 +6,7 @@ namespace StableDiffusion.NET;
public interface IBackend
{
bool IsEnabled { get; set; }
public int Priority { get; }
public int Priority { get; set; }
bool IsAvailable { get; }
string PathPart { get; }
}

View File

@ -2,7 +2,6 @@
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using StableDiffusion.NET.Helper;
namespace StableDiffusion.NET;
@ -13,13 +12,9 @@ public partial class RocmBackend : IBackend
public bool IsEnabled { get; set; } = true;
public int Priority => 10;
public int Priority { get; set; } = 10;
public bool IsAvailable => ((RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
&& RocmVersion is 5)
|| (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
&& RocmVersion is 6))
&& (RuntimeInformation.OSArchitecture == Architecture.X64);
public bool IsAvailable => (RocmVersion == 6) && (RuntimeInformation.OSArchitecture == Architecture.X64);
public string PathPart => RocmVersion switch
{

View File

@ -0,0 +1,30 @@
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public class SyclBackend : IBackend
{
#region Properties & Fields
//TODO DarthAffe 10.08.2024: tbh I'm not really sure how to detect a sycl-compatible system so for now it's disabled by default
public bool IsEnabled { get; set; } = false;
public int Priority { get; set; } = 5;
public bool IsAvailable => (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|| RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
&& (RuntimeInformation.OSArchitecture == Architecture.X64);
public string PathPart => "sycl";
#endregion
#region Constructors
internal SyclBackend()
{ }
#endregion
}

View File

@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public class VulkanBackend : IBackend
{
#region Properties & Fields
//TODO DarthAffe 28.08.2024: Find a way to detect vulkan compatibility
public bool IsEnabled { get; set; } = false;
public int Priority { get; set; } = 5;
public bool IsAvailable => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
&& (RuntimeInformation.OSArchitecture == Architecture.X64);
public string PathPart => "vulkan";
#endregion
#region Constructors
internal VulkanBackend()
{ }
#endregion
}

View File

@ -0,0 +1,8 @@
namespace StableDiffusion.NET;
public enum LoraApplyMode
{
Auto,
Immediately,
AtRuntime
}

View File

@ -0,0 +1,12 @@
namespace StableDiffusion.NET;
public enum Prediction
{
EPS,
V,
EDM_V,
Flow,
FluxFlow,
Flux2Flow,
Default
}

View File

@ -0,0 +1,9 @@
namespace StableDiffusion.NET;
public enum Preview
{
None,
Proj,
TAE,
VAE
}

View File

@ -8,7 +8,7 @@ public enum Quantization
Q4_0 = 2,
Q4_1 = 3,
// Q4_2 = 4, support has been removed
// Q4_3 (5) support has been removed
// Q4_3 = 5, support has been removed
Q5_0 = 6,
Q5_1 = 7,
Q8_0 = 8,
@ -18,7 +18,7 @@ public enum Quantization
Q4_K = 12,
Q5_K = 13,
Q6_K = 14,
Q8_K = 15,
Q8_K = 15,
IQ2_XXS = 16,
IQ2_XS = 17,
IQ3_XXS = 18,
@ -27,7 +27,22 @@ public enum Quantization
IQ3_S = 21,
IQ2_S = 22,
IQ4_XS = 23,
I8,
I16,
I32,
I8 = 24,
I16 = 25,
I32 = 26,
I64 = 27,
F64 = 28,
IQ1_M = 29,
BF16 = 30,
//Q4_0_4_4 = 31, support has been removed from gguf files
//Q4_0_4_8 = 32,
//Q4_0_8_8 = 33,
TQ1_0 = 34,
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,
Unspecified = 40
}

View File

@ -3,5 +3,6 @@
public enum RngType
{
Standard,
Cuda
Cuda,
Cpu
}

View File

@ -2,13 +2,17 @@
public enum Sampler
{
Euler_A,
Euler,
Euler_A,
Heun,
DPM2,
DPMPP2SA,
DPMPP2M,
DPMPP2Mv2,
IPNDM,
IPNDM_V,
LCM,
N_Sample_Methods
DDIM_Trailing,
TCD,
Default
}

View File

@ -1,10 +1,15 @@
namespace StableDiffusion.NET;
public enum Schedule
public enum Scheduler
{
Default,
Discrete,
Karras,
Exponential,
AYS,
N_Schedules
GITS,
SGM_Uniform,
Simple,
Smoothstep,
LCM,
Default
}

View File

@ -0,0 +1,15 @@
using System;
using HPPH;
namespace StableDiffusion.NET;
public sealed class StableDiffusionPreviewEventArgs(int step, bool isNoisy, Image<ColorRGB> image) : EventArgs
{
#region Properties & Fields
public int Step { get; } = step;
public bool IsNoisy { get; } = isNoisy;
public Image<ColorRGB> Image { get; } = image;
#endregion
}

View File

@ -1,9 +0,0 @@
using StableDiffusion.NET.Helper.Images;
using StableDiffusion.NET.Helper.Images.Colors;
namespace StableDiffusion.NET;
public static class ImageExtension
{
public static Image<ColorRGB> ToImage(this StableDiffusionImage image) => new(image.Data.ToArray(), 0, 0, image.Width, image.Height, image.Width);
}

View File

@ -0,0 +1,123 @@
#pragma warning disable CA2208
using JetBrains.Annotations;
using System;
namespace StableDiffusion.NET;
[PublicAPI]
public static class ParameterExtension
{
public static void Validate(this UpscaleModelParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(parameter.ModelPath, nameof(UpscaleModelParameter.ModelPath));
}
public static void Validate(this DiffusionModelParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.ThreadCount, nameof(DiffusionModelParameter.ThreadCount));
ArgumentNullException.ThrowIfNull(parameter.VaePath, nameof(DiffusionModelParameter.VaePath));
ArgumentNullException.ThrowIfNull(parameter.TaesdPath, nameof(DiffusionModelParameter.TaesdPath));
ArgumentNullException.ThrowIfNull(parameter.LoraModelDirectory, nameof(DiffusionModelParameter.LoraModelDirectory));
ArgumentNullException.ThrowIfNull(parameter.ControlNetPath, nameof(DiffusionModelParameter.ControlNetPath));
ArgumentNullException.ThrowIfNull(parameter.EmbeddingsDirectory, nameof(DiffusionModelParameter.EmbeddingsDirectory));
ArgumentNullException.ThrowIfNull(parameter.StackedIdEmbeddingsDirectory, nameof(DiffusionModelParameter.StackedIdEmbeddingsDirectory));
if (!string.IsNullOrWhiteSpace(parameter.VaePath) && !string.IsNullOrWhiteSpace(parameter.TaesdPath)) throw new ArgumentException("VAE and TAESD are mutually exclusive.");
if (!Enum.IsDefined(parameter.RngType)) throw new ArgumentOutOfRangeException(nameof(DiffusionModelParameter.RngType));
}
public static void Validate(this ImageGenerationParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(parameter.ControlNet, nameof(ImageGenerationParameter.ControlNet));
ArgumentNullException.ThrowIfNull(parameter.PhotoMaker, nameof(ImageGenerationParameter.PhotoMaker));
ArgumentNullException.ThrowIfNull(parameter.Prompt, nameof(ImageGenerationParameter.Prompt));
ArgumentNullException.ThrowIfNull(parameter.NegativePrompt, nameof(ImageGenerationParameter.NegativePrompt));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.Width, nameof(ImageGenerationParameter.Width));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.Height, nameof(ImageGenerationParameter.Height));
parameter.SampleParameter.Validate();
parameter.ControlNet.Validate();
parameter.PhotoMaker.Validate();
}
public static void Validate(this VideoGenerationParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(parameter.Prompt, nameof(VideoGenerationParameter.Prompt));
ArgumentNullException.ThrowIfNull(parameter.NegativePrompt, nameof(VideoGenerationParameter.NegativePrompt));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.Width, nameof(VideoGenerationParameter.Width));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.Height, nameof(VideoGenerationParameter.Height));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.FrameCount, nameof(VideoGenerationParameter.FrameCount));
parameter.SampleParameter.Validate();
parameter.HighNoiseSampleParameter.Validate();
}
public static void Validate(this ControlNetParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(ImageGenerationParameter.ControlNet));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.Strength, nameof(ControlNetParameter.Strength));
}
public static void Validate(this PhotoMakerParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(ImageGenerationParameter.PhotoMaker));
ArgumentNullException.ThrowIfNull(parameter.IdEmbedPath, nameof(PhotoMakerParameter.IdEmbedPath));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.StyleStrength, nameof(PhotoMakerParameter.StyleStrength));
}
public static void Validate(this SampleParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parameter.SampleSteps, nameof(ImageGenerationParameter.SampleParameter.SampleSteps));
if (!Enum.IsDefined(parameter.Scheduler)) throw new ArgumentOutOfRangeException(nameof(ImageGenerationParameter.SampleParameter.Scheduler));
if (!Enum.IsDefined(parameter.SampleMethod)) throw new ArgumentOutOfRangeException(nameof(ImageGenerationParameter.SampleParameter.SampleMethod));
parameter.Guidance.Validate();
}
public static void Validate(this GuidanceParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.ImgCfg);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.DistilledGuidance);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.MinCfg);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.TxtCfg);
parameter.Slg.Validate();
}
public static void Validate(this SlgParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(parameter.Layers);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.Scale);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.SkipLayerStart);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.SkipLayerEnd);
}
public static void Validate(this CannyParameter parameter)
{
ArgumentNullException.ThrowIfNull(parameter, nameof(parameter));
ArgumentNullException.ThrowIfNull(parameter.Image);
ArgumentOutOfRangeException.ThrowIfNegative(parameter.HighThreshold, nameof(CannyParameter.HighThreshold));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.LowThreshold, nameof(CannyParameter.LowThreshold));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.Weak, nameof(CannyParameter.Weak));
ArgumentOutOfRangeException.ThrowIfNegative(parameter.Strong, nameof(CannyParameter.Strong));
}
}

View File

@ -0,0 +1,139 @@
using HPPH;
using System;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET;
internal static class ImageHelper
{
public static unsafe Image<ColorRGB> ToImage(this Native.Types.sd_image_t sdImage)
{
int width = (int)sdImage.width;
int height = (int)sdImage.height;
int bpp = (int)sdImage.channel;
switch (bpp)
{
case 3:
return Image<ColorRGB>.Create(new ReadOnlySpan<byte>(sdImage.data, width * height * bpp), width, height, width * bpp);
case 1:
{
ColorRGB[] pixels = new ColorRGB[width * height];
Span<byte> sdData = new(sdImage.data, pixels.Length);
for (int i = 0; i < pixels.Length; i++)
{
byte c = sdData[i];
pixels[i] = new ColorRGB(c, c, c);
}
Image<ColorRGB> image = Image<ColorRGB>.Create(pixels, width, height);
return image;
}
default:
throw new ArgumentOutOfRangeException($"Image-BPP of {bpp} is not supported");
}
}
public static unsafe Native.Types.sd_image_t ToSdImage(this IImage image, bool monochrome = false)
{
if (monochrome)
{
int sizeInBytes = image.Width * image.Height;
byte* dataPtr = (byte*)NativeMemory.Alloc((nuint)sizeInBytes);
Span<byte> data = new(dataPtr, sizeInBytes);
// DarthAffe 16.08.2025: HPPH does currently not support monochrome images, that's why we need to convert it here. We're going for the simple conversion as the source image is supposed to be monochrome anyway.
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
{
IColor color = image[x, y];
data[(image.Width * y) + x] = (byte)Math.Round((color.R + color.G + color.B) / 3.0);
}
return new Native.Types.sd_image_t
{
width = (uint)image.Width,
height = (uint)image.Height,
channel = 1,
data = dataPtr
};
}
else
{
IImage<ColorRGB> img = image as IImage<ColorRGB> ?? image.ConvertTo<ColorRGB>();
int sizeInBytes = img.SizeInBytes;
byte* dataPtr = (byte*)NativeMemory.Alloc((nuint)sizeInBytes);
img.CopyTo(new Span<byte>(dataPtr, sizeInBytes));
return new Native.Types.sd_image_t
{
width = (uint)img.Width,
height = (uint)img.Height,
channel = (uint)img.ColorFormat.BytesPerPixel,
data = dataPtr
};
}
}
public static unsafe void Free(this Native.Types.sd_image_t sdImage)
{
if (sdImage.data == null) return;
NativeMemory.Free(sdImage.data);
}
public static unsafe Image<ColorRGB>[] ToImageArray(Native.Types.sd_image_t* sdImage, int count)
{
if (sdImage == null) return [];
Image<ColorRGB>[] images = new Image<ColorRGB>[count];
for (int i = 0; i < images.Length; i++)
images[i] = GetImage(sdImage, i);
return images;
}
internal static unsafe IImage[] ToImageArrayIFace(Native.Types.sd_image_t* sdImage, int count)
{
if (sdImage == null) return [];
IImage[] images = new IImage[count];
for (int i = 0; i < images.Length; i++)
images[i] = GetImage(sdImage, i);
return images;
}
public static unsafe Image<ColorRGB> GetImage(Native.Types.sd_image_t* sdImage, int index) => sdImage[index].ToImage();
public static unsafe Native.Types.sd_image_t* ToSdImage(this IImage[] images, bool monochrome = false)
{
int count = images.Length;
Native.Types.sd_image_t* imagePtr = (Native.Types.sd_image_t*)NativeMemory.Alloc((nuint)count, (nuint)Marshal.SizeOf<Native.Types.sd_image_t>());
for (int i = 0; i < count; i++)
imagePtr[i] = images[i].ToSdImage(monochrome);
return imagePtr;
}
public static unsafe void Free(Native.Types.sd_image_t* sdImage, int count)
{
if (sdImage == null) return;
for (int i = 0; i < count; i++)
Free(sdImage[i]);
NativeMemory.Free(sdImage);
}
}

View File

@ -1,66 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 32 bit ABGR-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorABGR : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.ABGR;
private readonly byte _a;
private readonly byte _b;
private readonly byte _g;
private readonly byte _r;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte A => _a;
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte R => _r;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorABGR"/> class.
/// </summary>
/// <param name="a">The alpha-component of the color.</param>
/// <param name="b">The blue-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="r">The red-component of the color.</param>
public ColorABGR(byte a, byte b, byte g, byte r)
{
this._a = a;
this._b = b;
this._g = g;
this._r = r;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {_a}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,66 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 32 bit ARGB-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorARGB : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.ARGB;
private readonly byte _a;
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte A => _a;
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte B => _b;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorARGB"/> class.
/// </summary>
/// <param name="a">The alpha-component of the color.</param>
/// <param name="r">The red-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="b">The blue-component of the color.</param>
public ColorARGB(byte a, byte r, byte g, byte b)
{
this._a = a;
this._r = r;
this._g = g;
this._b = b;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {_a}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,63 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 24 bit BGR-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorBGR : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.BGR;
private readonly byte _b;
private readonly byte _g;
private readonly byte _r;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte A => byte.MaxValue;
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte R => _r;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorBGR"/> class.
/// </summary>
/// <param name="b">The blue-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="r">The red-component of the color.</param>
public ColorBGR(byte b, byte g, byte r)
{
this._b = b;
this._g = g;
this._r = r;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {A}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,66 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 32 bit BGRA-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorBGRA : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.BGRA;
private readonly byte _b;
private readonly byte _g;
private readonly byte _r;
private readonly byte _a;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte A => _a;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorBGRA"/> class.
/// </summary>
/// <param name="b">The blue-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="r">The red-component of the color.</param>
/// <param name="a">The alpha-component of the color.</param>
public ColorBGRA(byte b, byte g, byte r, byte a)
{
this._b = b;
this._g = g;
this._r = r;
this._a = a;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {_a}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,58 +0,0 @@
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color format.
/// </summary>
public readonly struct ColorFormat
{
#region Instances
public static readonly ColorFormat BGRA = new(1, 4);
public static readonly ColorFormat ABGR = new(2, 4);
public static readonly ColorFormat RGBA = new(3, 4);
public static readonly ColorFormat ARGB = new(4, 4);
public static readonly ColorFormat BGR = new(5, 3);
public static readonly ColorFormat RGB = new(6, 3);
#endregion
#region Properties & Fields
/// <summary>
/// Gets the Id of the color-format.
/// </summary>
public readonly int Id;
/// <summary>
/// Gets the Bytes per pixel for this color-format.
/// </summary>
public readonly int BytesPerPixel;
#endregion
#region Constructors
private ColorFormat(int id, int bytesPerPixel)
{
this.Id = id;
this.BytesPerPixel = bytesPerPixel;
}
#endregion
#region Methods
public bool Equals(ColorFormat other) => Id == other.Id;
public override bool Equals(object? obj) => obj is ColorFormat other && Equals(other);
public override int GetHashCode() => Id;
#endregion
#region Operators
public static bool operator ==(ColorFormat left, ColorFormat right) => left.Equals(right);
public static bool operator !=(ColorFormat left, ColorFormat right) => !(left == right);
#endregion
}

View File

@ -1,63 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 24 bit RGB-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorRGB : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.RGB;
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte A => byte.MaxValue;
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte B => _b;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorRGB"/> class.
/// </summary>
/// <param name="r">The red-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="b">The blue-component of the color.</param>
public ColorRGB(byte r, byte g, byte b)
{
this._r = r;
this._g = g;
this._b = b;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {A}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,66 +0,0 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a color in 32 bit RGBA-format.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorRGBA : IColor
{
#region Properties & Fields
/// <inheritdoc />
public static ColorFormat ColorFormat => ColorFormat.RGBA;
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;
private readonly byte _a;
// ReSharper disable ConvertToAutoPropertyWhenPossible
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte A => _a;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ColorRGBA"/> class.
/// </summary>
/// <param name="r">The red-component of the color.</param>
/// <param name="g">The green-component of the color.</param>
/// <param name="b">The blue-component of the color.</param>
/// <param name="a">The alpha-component of the color.</param>
public ColorRGBA(byte r, byte g, byte b, byte a)
{
this._r = r;
this._g = g;
this._b = b;
this._a = a;
}
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {_a}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -1,34 +0,0 @@
using System;
namespace StableDiffusion.NET.Helper.Images.Colors;
/// <summary>
/// Represents a generic color made of 4 bytes (alpha, red, green and blue)
/// </summary>
public interface IColor
{
/// <summary>
/// Gets the red-component of this color.
/// </summary>
byte R { get; }
/// <summary>
/// Gets the green-component of this color.
/// </summary>
byte G { get; }
/// <summary>
/// Gets the blue-component of this color.
/// </summary>
byte B { get; }
/// <summary>
/// Gets the alpha-component of this color.
/// </summary>
byte A { get; }
/// <summary>
/// Gets the color-format of this color.
/// </summary>
public static virtual ColorFormat ColorFormat => throw new NotSupportedException();
}

View File

@ -1,193 +0,0 @@
using System;
using System.Collections.Generic;
using StableDiffusion.NET.Helper.Images.Colors;
namespace StableDiffusion.NET.Helper.Images;
/// <summary>
/// Represents a image.
/// </summary>
public interface IImage : IEnumerable<IColor>
{
/// <summary>
/// Gets the color format used in this image.
/// </summary>
ColorFormat ColorFormat { get; }
/// <summary>
/// Gets the width of this image.
/// </summary>
int Width { get; }
/// <summary>
/// Gets the height of this image.
/// </summary>
int Height { get; }
/// <summary>
/// Gets the size in bytes of this image.
/// </summary>
int SizeInBytes { get; }
/// <summary>
/// Gets the color at the specified location.
/// </summary>
/// <param name="x">The X-location to read.</param>
/// <param name="y">The Y-location to read.</param>
/// <returns>The color at the specified location.</returns>
IColor this[int x, int y] { get; }
/// <summary>
/// Gets an image representing the specified location.
/// </summary>
/// <param name="x">The X-location of the image.</param>
/// <param name="y">The Y-location of the image.</param>
/// <param name="width">The width of the sub-image.</param>
/// <param name="height"></param>
/// <returns></returns>
IImage this[int x, int y, int width, int height] { get; }
/// <summary>
/// Gets a list of all rows of this image.
/// </summary>
IImageRows Rows { get; }
/// <summary>
/// Gets a list of all columns of this image.
/// </summary>
IImageColumns Columns { get; }
/// <summary>
/// Gets an <see cref="RefImage{TColor}"/> representing this <see cref="IImage"/>.
/// </summary>
/// <typeparam name="TColor">The color-type of the iamge.</typeparam>
/// <returns>The <inheritdoc cref="RefImage{TColor}"/>.</returns>
RefImage<TColor> AsRefImage<TColor>() where TColor : struct, IColor;
/// <summary>
/// Copies the contents of this <see cref="IImage"/> into a destination <see cref="Span{T}"/> instance.
/// </summary>
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImage"/> instance.
/// </exception>
void CopyTo(in Span<byte> destination);
/// <summary>
/// Allocates a new array and copies this <see cref="IImage"/> into it.
/// </summary>
/// <returns>The new array containing the data of this <see cref="IImage"/>.</returns>
byte[] ToArray();
/// <summary>
/// Represents a list of rows of an image.
/// </summary>
public interface IImageRows : IEnumerable<IImageRow>
{
/// <summary>
/// Gets the amount of rows in this list.
/// </summary>
int Count { get; }
/// <summary>
/// Gets a specific <see cref="IImageRow"/>.
/// </summary>
/// <param name="column">The ´row to get.</param>
/// <returns>The requested <see cref="IImageRow"/>.</returns>
IImageRow this[int column] { get; }
}
/// <summary>
/// Represents a list of columns of an image.
/// </summary>
public interface IImageColumns : IEnumerable<IImageColumn>
{
/// <summary>
/// Gets the amount of columns in this list.
/// </summary>
int Count { get; }
/// <summary>
/// Gets a specific <see cref="IImageColumn"/>.
/// </summary>
/// <param name="column">The column to get.</param>
/// <returns>The requested <see cref="IImageColumn"/>.</returns>
IImageColumn this[int column] { get; }
}
/// <summary>
/// Represents a single row of an image.
/// </summary>
public interface IImageRow : IEnumerable<IColor>
{
/// <summary>
/// Gets the length of the row.
/// </summary>
int Length { get; }
/// <summary>
/// Gets the size in bytes of this row.
/// </summary>
int SizeInBytes { get; }
/// <summary>
/// Gets the <see cref="IColor"/> at the specified location.
/// </summary>
/// <param name="x">The location to get the color from.</param>
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
IColor this[int x] { get; }
/// <summary>
/// Copies the contents of this <see cref="IImageRow"/> into a destination <see cref="Span{T}"/> instance.
/// </summary>
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImageRow"/> instance.
/// </exception>
void CopyTo(in Span<byte> destination);
/// <summary>
/// Allocates a new array and copies this <see cref="IImageRow"/> into it.
/// </summary>
/// <returns>The new array containing the data of this <see cref="IImageRow"/>.</returns>
byte[] ToArray();
}
/// <summary>
/// Represents a single column of an image.
/// </summary>
public interface IImageColumn : IEnumerable<IColor>
{
/// <summary>
/// Gets the length of the column.
/// </summary>
int Length { get; }
/// <summary>
/// Gets the size in bytes of this column.
/// </summary>
int SizeInBytes { get; }
/// <summary>
/// Gets the <see cref="IColor"/> at the specified location.
/// </summary>
/// <param name="y">The location to get the color from.</param>
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
IColor this[int y] { get; }
/// <summary>
/// Copies the contents of this <see cref="IImageColumn"/> into a destination <see cref="Span{T}"/> instance.
/// </summary>
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImageColumn"/> instance.
/// </exception>
void CopyTo(in Span<byte> destination);
/// <summary>
/// Allocates a new array and copies this <see cref="IImageColumn"/> into it.
/// </summary>
/// <returns>The new array containing the data of this <see cref="IImageColumn"/>.</returns>
byte[] ToArray();
}
}

View File

@ -1,431 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using StableDiffusion.NET.Helper.Images.Colors;
namespace StableDiffusion.NET.Helper.Images;
/// <inheritdoc />
public sealed class Image<TColor> : IImage
where TColor : struct, IColor
{
#region Properties & Fields
private readonly byte[] _buffer;
private readonly int _x;
private readonly int _y;
private readonly int _stride;
/// <inheritdoc />
public ColorFormat ColorFormat
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => TColor.ColorFormat;
}
/// <inheritdoc />
public int Width { get; }
/// <inheritdoc />
public int Height { get; }
/// <inheritdoc />
public int SizeInBytes => Width * Height * ColorFormat.BytesPerPixel;
#endregion
#region Indexer
/// <inheritdoc />
public IColor this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((x < 0) || (y < 0) || (x >= Width) || (y >= Height)) throw new IndexOutOfRangeException();
return MemoryMarshal.Cast<byte, TColor>(_buffer)[((_y + y) * _stride) + (_x + x)];
}
}
/// <inheritdoc />
public IImage this[int x, int y, int width, int height]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
return new Image<TColor>(_buffer, _x + x, _y + y, width, height, _stride);
}
}
/// <inheritdoc />
public IImage.IImageRows Rows
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new ImageRows(_buffer, _x, _y, Width, Height, _stride);
}
/// <inheritdoc />
public IImage.IImageColumns Columns
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new ImageColumns(_buffer, _x, _y, Width, Height, _stride);
}
#endregion
#region Constructors
public Image(byte[] buffer, int x, int y, int width, int height, int stride)
{
this._buffer = buffer;
this._x = x;
this._y = y;
this.Width = width;
this.Height = height;
this._stride = stride;
}
#endregion
#region Methods
/// <inheritdoc />
public void CopyTo(in Span<byte> destination)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
int targetStride = Width * ColorFormat.BytesPerPixel;
IImage.IImageRows rows = Rows;
Span<byte> target = destination;
foreach (IImage.IImageRow row in rows)
{
row.CopyTo(target);
target = target[targetStride..];
}
}
/// <inheritdoc />
public byte[] ToArray()
{
byte[] array = new byte[SizeInBytes];
CopyTo(array);
return array;
}
/// <inheritdoc />
public RefImage<T> AsRefImage<T>()
where T : struct, IColor
{
if (typeof(T) != typeof(TColor)) throw new ArgumentException("The requested color format does not fit this image.", nameof(T));
return new RefImage<T>(MemoryMarshal.Cast<byte, T>(_buffer), _x, _y, Width, Height, _stride);
}
/// <inheritdoc />
public IEnumerator<IColor> GetEnumerator()
{
for (int y = 0; y < Height; y++)
for (int x = 0; x < Width; x++)
yield return this[x, y];
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
#region Indexer-Classes
/// <inheritdoc />
private sealed class ImageRows : IImage.IImageRows
{
#region Properties & Fields
private readonly byte[] _buffer;
private readonly int _x;
private readonly int _y;
private readonly int _width;
private readonly int _height;
private readonly int _stride;
/// <inheritdoc />
public int Count => _height;
#endregion
#region Indexer
/// <inheritdoc />
public IImage.IImageRow this[int row]
{
get
{
if ((row < 0) || (row >= _height)) throw new IndexOutOfRangeException();
return new ImageRow(_buffer, (((row + _y) * _stride) + _x), _width);
}
}
#endregion
#region Constructors
internal ImageRows(byte[] buffer, int x, int y, int width, int height, int stride)
{
this._buffer = buffer;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._stride = stride;
}
#endregion
#region Methods
/// <inheritdoc />
public IEnumerator<IImage.IImageRow> GetEnumerator()
{
for (int y = 0; y < _height; y++)
yield return this[y];
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
}
/// <inheritdoc />
private sealed class ImageRow : IImage.IImageRow
{
#region Properties & Fields
private readonly byte[] _buffer;
private readonly int _start;
private readonly int _length;
/// <inheritdoc />
public int Length => _length;
/// <inheritdoc />
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
#endregion
#region Indexer
/// <inheritdoc />
public IColor this[int x]
{
get
{
if ((x < 0) || (x >= _length)) throw new IndexOutOfRangeException();
ReadOnlySpan<TColor> row = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
return row[x];
}
}
#endregion
#region Constructors
internal ImageRow(byte[] buffer, int start, int length)
{
this._buffer = buffer;
this._start = start;
this._length = length;
}
#endregion
#region Methods
/// <inheritdoc />
public void CopyTo(in Span<byte> destination)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
MemoryMarshal.Cast<byte, TColor>(_buffer).Slice(_start, _length).CopyTo(MemoryMarshal.Cast<byte, TColor>(destination));
}
/// <inheritdoc />
public byte[] ToArray()
{
byte[] array = new byte[SizeInBytes];
CopyTo(array);
return array;
}
/// <inheritdoc />
public IEnumerator<IColor> GetEnumerator()
{
for (int x = 0; x < _length; x++)
yield return this[x];
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
}
/// <inheritdoc />
private sealed class ImageColumns : IImage.IImageColumns
{
#region Properties & Fields
private readonly byte[] _buffer;
private readonly int _x;
private readonly int _y;
private readonly int _width;
private readonly int _height;
private readonly int _stride;
/// <inheritdoc />
public int Count => _width;
#endregion
#region Indexer
/// <inheritdoc />
public IImage.IImageColumn this[int column]
{
get
{
if ((column < 0) || (column >= _width)) throw new IndexOutOfRangeException();
return new ImageColumn(_buffer, (_y * _stride) + _x + column, _height, _stride);
}
}
#endregion
#region Constructors
internal ImageColumns(byte[] buffer, int x, int y, int width, int height, int stride)
{
this._buffer = buffer;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._stride = stride;
}
#endregion
#region Methods
/// <inheritdoc />
public IEnumerator<IImage.IImageColumn> GetEnumerator()
{
for (int y = 0; y < _height; y++)
yield return this[y];
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
}
/// <inheritdoc />
private sealed class ImageColumn : IImage.IImageColumn
{
#region Properties & Fields
private readonly byte[] _buffer;
private readonly int _start;
private readonly int _length;
private readonly int _step;
/// <inheritdoc />
public int Length => _length;
/// <inheritdoc />
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
#endregion
#region Indexer
/// <inheritdoc />
public IColor this[int y]
{
get
{
if ((y < 0) || (y >= _length)) throw new IndexOutOfRangeException();
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
return data[y * _step];
}
}
#endregion
#region Constructors
internal ImageColumn(byte[] buffer, int start, int length, int step)
{
this._buffer = buffer;
this._start = start;
this._length = length;
this._step = step;
}
#endregion
#region Methods
/// <inheritdoc />
public void CopyTo(in Span<byte> destination)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
if (_step == 1)
_buffer.AsSpan(_start, SizeInBytes).CopyTo(destination);
else
{
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
Span<TColor> target = MemoryMarshal.Cast<byte, TColor>(destination);
for (int i = 0; i < Length; i++)
target[i] = data[i * _step];
}
}
/// <inheritdoc />
public byte[] ToArray()
{
byte[] array = new byte[SizeInBytes];
CopyTo(array);
return array;
}
/// <inheritdoc />
public IEnumerator<IColor> GetEnumerator()
{
for (int y = 0; y < _length; y++)
yield return this[y];
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
}
#endregion
}

View File

@ -1,266 +0,0 @@
// DarthAffe 05.09.2023: Based on https://github.com/CommunityToolkit/dotnet/blob/b0d6c4f9c0cfb5d860400abb00b0ca1b3e94dfa4/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable%7BT%7D.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET.Helper.Images;
/// <summary>
/// A <see langword="ref"/> <see langword="struct"/> that iterates readonly items from arbitrary memory locations.
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
public readonly ref struct ReadOnlyRefEnumerable<T>
{
#region Properties & Fields
/// <summary>
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
/// </summary>
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
private readonly ReadOnlySpan<T> _span;
/// <summary>
/// The distance between items in the sequence to enumerate.
/// </summary>
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
private readonly int _step;
/// <summary>
/// Gets the total available length for the sequence.
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _span.Length;
}
/// <summary>
/// Gets the element at the specified zero-based index.
/// </summary>
/// <param name="index">The zero-based index of the element.</param>
/// <returns>A reference to the element at the specified index.</returns>
/// <exception cref="IndexOutOfRangeException">
/// Thrown when <paramref name="index"/> is invalid.
/// </exception>
public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((uint)index >= (uint)Length) throw new IndexOutOfRangeException();
ref T r0 = ref MemoryMarshal.GetReference(_span);
nint offset = (nint)(uint)index * (nint)(uint)_step;
ref T ri = ref Unsafe.Add(ref r0, offset);
return ref ri;
}
}
/// <summary>
/// Gets the element at the specified zero-based index.
/// </summary>
/// <param name="index">The zero-based index of the element.</param>
/// <returns>A reference to the element at the specified index.</returns>
/// <exception cref="IndexOutOfRangeException">
/// Thrown when <paramref name="index"/> is invalid.
/// </exception>
public ref readonly T this[Index index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref this[index.GetOffset(Length)];
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
/// </summary>
/// <param name="reference">A reference to the first item of the sequence.</param>
/// <param name="length">The number of items in the sequence.</param>
/// <param name="step">The distance between items in the sequence to enumerate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
{
this._step = step;
_span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new(_span, _step);
public T[] ToArray()
{
int length = _span.Length;
// Empty array if no data is mapped
if (length == 0)
return Array.Empty<T>();
T[] array = new T[length];
CopyTo(array);
return array;
}
/// <summary>
/// Copies the contents of this <see cref="ReadOnlyRefEnumerable{T}"/> into a destination <see cref="Span{T}"/> instance.
/// </summary>
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="ReadOnlyRefEnumerable{T}"/> instance.
/// </exception>
public void CopyTo(Span<T> destination)
{
if (_step == 1)
{
_span.CopyTo(destination);
return;
}
ref T sourceRef = ref MemoryMarshal.GetReference(_span);
int length = _span.Length;
if ((uint)destination.Length < (uint)length)
throw new ArgumentException("The target span is too short to copy all the current items to.");
ref T destinationRef = ref MemoryMarshal.GetReference(destination);
CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)_step);
}
/// <summary>
/// Attempts to copy the current <see cref="ReadOnlyRefEnumerable{T}"/> instance to a destination <see cref="Span{T}"/>.
/// </summary>
/// <param name="destination">The target <see cref="Span{T}"/> of the copy operation.</param>
/// <returns>Whether or not the operation was successful.</returns>
public bool TryCopyTo(Span<T> destination)
{
if (destination.Length >= _span.Length)
{
CopyTo(destination);
return true;
}
return false;
}
private static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
{
nint sourceOffset = 0;
nint destinationOffset = 0;
while (length >= 8)
{
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 8;
sourceOffset += sourceStep;
destinationOffset += 8;
}
if (length >= 4)
{
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
length -= 4;
sourceOffset += sourceStep;
destinationOffset += 4;
}
while (length > 0)
{
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
length -= 1;
sourceOffset += sourceStep;
destinationOffset += 1;
}
}
#endregion
/// <summary>
/// A custom enumerator type to traverse items within a <see cref="ReadOnlyRefEnumerable{T}"/> instance.
/// </summary>
public ref struct Enumerator
{
#region Properties & Fields
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._span"/>
private readonly ReadOnlySpan<T> _span;
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._step"/>
private readonly int _step;
/// <summary>
/// The current position in the sequence.
/// </summary>
private int _position;
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
public readonly ref readonly T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ref T r0 = ref MemoryMarshal.GetReference(_span);
nint offset = (nint)(uint)_position * (nint)(uint)_step;
ref T ri = ref Unsafe.Add(ref r0, offset);
return ref ri;
}
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance with the info on the items to traverse.</param>
/// <param name="step">The distance between items in the sequence to enumerate.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(ReadOnlySpan<T> span, int step)
{
this._span = span;
this._step = step;
_position = -1;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_position < _span.Length;
#endregion
}
}

View File

@ -1,368 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using StableDiffusion.NET.Helper.Images.Colors;
namespace StableDiffusion.NET.Helper.Images;
public readonly ref struct RefImage<TColor>
where TColor : struct, IColor
{
#region Properties & Fields
private readonly ReadOnlySpan<TColor> _pixels;
private readonly int _x;
private readonly int _y;
/// <summary>
/// Gets the width of the image.
/// </summary>
public int Width { get; }
/// <summary>
/// Gets the height of the image.
/// </summary>
public int Height { get; }
/// <summary>
/// Gets the stride (entries per row) of the underlying buffer.
/// Only useful if you want to work with a pinned buffer.
/// </summary>
public int RawStride { get; }
#endregion
#region Indexer
public ref readonly TColor this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((x < 0) || (y < 0) || (x >= Width) || (y >= Height)) throw new IndexOutOfRangeException();
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
nint offset = (nint)(uint)((_y + y) * RawStride) + (_x + x);
return ref Unsafe.Add(ref r0, offset);
}
}
public RefImage<TColor> this[int x, int y, int width, int height]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
return new RefImage<TColor>(_pixels, _x + x, _y + y, width, height, RawStride);
}
}
public ImageRows Rows
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_pixels, _x, _y, Width, Height, RawStride);
}
public ImageColumns Columns
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(_pixels, _x, _y, Width, Height, RawStride);
}
#endregion
#region Constructors
public RefImage(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
{
this._pixels = pixels;
this._x = x;
this._y = y;
this.Width = width;
this.Height = height;
this.RawStride = stride;
}
#endregion
#region Methods
/// <summary>
/// Copies the contents of this <see cref="RefImage{TColor}"/> into a destination <see cref="Span{T}"/> instance.
/// </summary>
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="RefImage{TColor}"/> instance.
/// </exception>
public void CopyTo(in Span<TColor> destination)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (destination.Length < (Width * Height)) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
ImageRows rows = Rows;
Span<TColor> target = destination;
foreach (ReadOnlyRefEnumerable<TColor> row in rows)
{
row.CopyTo(target);
target = target[Width..];
}
}
/// <summary>
/// Allocates a new array and copies this <see cref="RefImage{TColor}"/> into it.
/// </summary>
/// <returns>The new array containing the data of this <see cref="RefImage{TColor}"/>.</returns>
public TColor[] ToArray()
{
TColor[] array = new TColor[Width * Height];
CopyTo(array);
return array;
}
/// <summary>
/// Returns a reference to the first element of this image inside the full image buffer.
/// </summary>
public ref readonly TColor GetPinnableReference()
{
if (_pixels.Length == 0)
return ref Unsafe.NullRef<TColor>();
int offset = (_y * RawStride) + _x;
return ref MemoryMarshal.GetReference(_pixels[offset..]);
}
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImageEnumerator GetEnumerator() => new(_pixels);
#endregion
public ref struct ImageEnumerator
{
#region Properties & Fields
private readonly ReadOnlySpan<TColor> _pixels;
private int _position;
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
public TColor Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _pixels[_position];
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ImageEnumerator(ReadOnlySpan<TColor> pixels)
{
this._pixels = pixels;
_position = -1;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_position < _pixels.Length;
#endregion
}
#region Indexer-Structs
public readonly ref struct ImageRows
{
#region Properties & Fields
private readonly ReadOnlySpan<TColor> _pixels;
private readonly int _x;
private readonly int _y;
private readonly int _width;
private readonly int _height;
private readonly int _stride;
public int Count => _height;
#endregion
#region Indexer
public readonly ReadOnlyRefEnumerable<TColor> this[int row]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((row < 0) || (row > _height)) throw new IndexOutOfRangeException();
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
ref TColor rr = ref Unsafe.Add(ref r0, (nint)(uint)(((row + _y) * _stride) + _x));
return new ReadOnlyRefEnumerable<TColor>(rr, _width, 1);
}
}
#endregion
#region Constructors
public ImageRows(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
{
this._pixels = pixels;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._stride = stride;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImageRowsEnumerator GetEnumerator() => new(this);
#endregion
public ref struct ImageRowsEnumerator
{
#region Properties & Fields
private readonly ImageRows _rows;
private int _position;
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
public ReadOnlyRefEnumerable<TColor> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _rows[_position];
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ImageRowsEnumerator(ImageRows rows)
{
this._rows = rows;
_position = -1;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_position < _rows._height;
#endregion
}
}
public readonly ref struct ImageColumns
{
#region Properties & Fields
private readonly ReadOnlySpan<TColor> _pixels;
private readonly int _x;
private readonly int _y;
private readonly int _width;
private readonly int _height;
private readonly int _stride;
public int Count => _width;
#endregion
#region Indexer
public ReadOnlyRefEnumerable<TColor> this[int column]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if ((column < 0) || (column > _width)) throw new IndexOutOfRangeException();
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
ref TColor rc = ref Unsafe.Add(ref r0, (nint)(uint)((_y * _stride) + (column + _x)));
return new ReadOnlyRefEnumerable<TColor>(rc, _height, _stride);
}
}
#endregion
#region Constructors
public ImageColumns(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
{
this._pixels = pixels;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._stride = stride;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImageColumnsEnumerator GetEnumerator() => new(this);
#endregion
public ref struct ImageColumnsEnumerator
{
#region Properties & Fields
private readonly ImageColumns _columns;
private int _position;
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
public ReadOnlyRefEnumerable<TColor> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _columns[_position];
}
#endregion
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ImageColumnsEnumerator(ImageColumns columns)
{
this._columns = columns;
this._position = -1;
}
#endregion
#region Methods
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_position < _columns._width;
#endregion
}
}
#endregion
}

View File

@ -1,6 +1,6 @@
using System.Diagnostics;
namespace StableDiffusion.NET.Helper;
namespace StableDiffusion.NET;
internal static class ProcessHelper
{

View File

@ -1,30 +0,0 @@
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public class ModelParameter
{
#region Properties & Fields
public int ThreadCount { get; set; } = 8;
public bool VaeDecodeOnly { get; set; } = false;
public bool VaeTiling { get; set; } = false;
public string TaesdPath { get; set; } = string.Empty;
public string LoraModelDir { get; set; } = string.Empty;
public RngType RngType { get; set; } = RngType.Standard;
public string VaePath { get; set; } = string.Empty;
public string ControlNetPath { get; set; } = string.Empty;
public string EmbeddingsDirectory { get; set; } = string.Empty;
public string StackedIdEmbeddingsDirectory { get; set; } = string.Empty;
public bool KeepControlNetOnCPU { get; set; } = false;
public bool KeepClipOnCPU { get; set; } = false;
public bool KeepVaeOnCPU { get; set; } = false;
//TODO DarthAffe 01.01.2024: K-Quants doesn't seem to work so far
public Quantization Quantization { get; set; } = Quantization.F16;
public Schedule Schedule { get; set; } = Schedule.Default;
#endregion
}

View File

@ -0,0 +1,98 @@
using HPPH;
using JetBrains.Annotations;
using System;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed unsafe class DiffusionModel : IDisposable
{
#region Properties & Fields
private bool _disposed;
public DiffusionModelParameter ModelParameter { get; }
private Native.Types.sd_ctx_t* _ctx;
#endregion
#region Constructors
public DiffusionModel(DiffusionModelParameter modelParameter)
{
ArgumentNullException.ThrowIfNull(modelParameter, nameof(modelParameter));
this.ModelParameter = modelParameter;
Initialize();
}
~DiffusionModel() => Dispose();
#endregion
#region Methods
private void Initialize()
{
ModelParameter.Validate();
_ctx = Native.new_sd_ctx(ModelParameter);
if (_ctx == null) throw new NullReferenceException("Failed to initialize diffusion-model.");
}
public Image<ColorRGB>? GenerateImage(ImageGenerationParameter parameter)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (_ctx == null) throw new NullReferenceException("The model is not initialized.");
parameter.Validate();
Native.Types.sd_image_t* result = Native.generate_image(_ctx, parameter);
if (result == null) return null;
try
{
return ImageHelper.GetImage(result, 0);
}
finally
{
ImageHelper.Free(result, 1);
}
}
public Image<ColorRGB>[] GenerateVideo(VideoGenerationParameter parameter)
{
ObjectDisposedException.ThrowIf(_disposed, this);
parameter.Validate();
Native.Types.sd_image_t* result = Native.generate_video(_ctx, parameter, out int frameCount);
if (result == null) return [];
try
{
return ImageHelper.ToImageArray(result, frameCount);
}
finally
{
ImageHelper.Free(result, frameCount);
}
}
public void Dispose()
{
if (_disposed) return;
if (_ctx != null)
Native.free_sd_ctx(_ctx);
GC.SuppressFinalize(this);
_disposed = true;
}
#endregion
}

View File

@ -0,0 +1,40 @@
using HPPH;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class CannyParameter
{
/// <summary>
/// the image to process
/// </summary>
public IImage? Image { get; set; } = null;
/// <summary>
///
/// </summary>
public float HighThreshold { get; set; } = 0.08f;
/// <summary>
///
/// </summary>
public float LowThreshold { get; set; } = 0.08f;
/// <summary>
///
/// </summary>
public float Weak { get; set; } = 0.8f;
/// <summary>
///
/// </summary>
public float Strong { get; set; } = 1.0f;
/// <summary>
///
/// </summary>
public bool Inverse { get; set; } = false;
public static CannyParameter Create() => new();
}

View File

@ -0,0 +1,19 @@
using HPPH;
namespace StableDiffusion.NET;
public sealed class ControlNetParameter
{
/// <summary>
/// image condition, control net
/// </summary>
public IImage? Image { get; set; } = null;
/// <summary>
/// strength to apply Control Net (default: 0.9)
/// 1.0 corresponds to full destruction of information in init image
/// </summary>
public float Strength { get; set; } = 0.9f;
internal ControlNetParameter() { }
}

View File

@ -0,0 +1,159 @@
using System;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class DiffusionModelParameter
{
/// <summary>
/// path to vae
/// </summary>
public string VaePath { get; set; } = string.Empty;
/// <summary>
/// path to taesd. Using Tiny AutoEncoder for fast decoding (low quality)
/// </summary>
public string TaesdPath { get; set; } = string.Empty;
/// <summary>
/// lora model directory
/// </summary>
public string LoraModelDirectory { get; set; } = string.Empty;
/// <summary>
/// path to embeddings
/// </summary>
public string EmbeddingsDirectory { get; set; } = string.Empty;
/// <summary>
/// path to control net model
/// </summary>
public string ControlNetPath { get; set; } = string.Empty;
/// <summary>
/// number of threads to use during computation (default: -1)
/// If threads = -1, then threads will be set to the number of CPU physical cores
/// </summary>
public int ThreadCount { get; set; } = 1;
/// <summary>
///
/// </summary>
public bool VaeDecodeOnly { get; set; } = false;
public bool FreeParamsImmediately { get; set; } = false;
/// <summary>
/// process vae in tiles to reduce memory usage
/// </summary>
public bool VaeTiling { get; set; } = false;
public bool OffloadParamsToCPU { get; set; } = false;
/// <summary>
/// keep clip in cpu (for low vram)
/// </summary>
public bool KeepClipOnCPU { get; set; } = false;
/// <summary>
/// keep controlnet in cpu
/// </summary>
public bool KeepControlNetOnCPU { get; set; } = false;
/// <summary>
/// keep vae in cpu (for low vram)
/// </summary>
public bool KeepVaeOnCPU { get; set; } = false;
/// <summary>
/// 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.
/// </summary>
public bool FlashAttention { get; set; } = false;
public bool TaePreviewOnly { get; set; } = false;
/// <summary>
/// use Conv2d direct in the diffusion model
/// This might crash if it is not supported by the backend.
/// </summary>
public bool DiffusionConvDirect { get; set; } = false;
/// <summary>
/// use Conv2d direct in the vae model (should improve the performance)
/// This might crash if it is not supported by the backend.
/// </summary>
public bool VaeConvDirect { get; set; } = false;
/// <summary>
/// RNG (default: Standard)
/// </summary>
public RngType RngType { get; set; } = RngType.Standard;
public RngType SamplerRngType { get; set; } = RngType.Standard;
public Prediction Prediction { get; set; } = Prediction.Default;
public LoraApplyMode LoraApplyMode { get; set; } = LoraApplyMode.Auto;
/// <summary>
/// quantizes on load
/// not really useful in most cases
/// </summary>
public Quantization Quantization { get; set; } = Quantization.Unspecified;
public float FlowShift { get; set; } = 0;
public bool ForceSdxlVaeConvScale { get; set; } = false;
/// <summary>
/// path to PHOTOMAKER stacked id embeddings
/// </summary>
public string StackedIdEmbeddingsDirectory { get; set; } = string.Empty;
public string TensorTypeRules { get; set; } = string.Empty;
/// <summary>
/// path to full model
/// </summary>
public string ModelPath { get; set; } = string.Empty;
/// <summary>
/// path to the standalone diffusion model
/// </summary>
public string DiffusionModelPath { get; set; } = string.Empty;
/// <summary>
/// path to the clip-l text encoder
/// </summary>
public string ClipLPath { get; set; } = string.Empty;
/// <summary>
/// path to the clip-g text encoder
/// </summary>
public string ClipGPath { get; set; } = string.Empty;
/// <summary>
/// path to the the t5xxl text encoder
/// </summary>
public string T5xxlPath { get; set; } = string.Empty;
[Obsolete("Use LLMPath instead")]
public string Qwen2VLPath { get => LLMPath; set => LLMPath = value; }
public string LLMPath { get; set; } = string.Empty;
[Obsolete("Use LLMVisionPath instead")]
public string Qwen2VLVisionPath { get => LLMVisionPath; set => LLMVisionPath = value; }
public string LLMVisionPath { get; set; } = string.Empty;
public string ClipVisionPath { get; set; } = string.Empty;
public string HighNoiseDiffusionModelPath { get; set; } = string.Empty;
public bool ChromaUseDitMap { get; set; } = true;
public bool ChromaEnableT5Map { get; set; } = false;
public int ChromaT5MaskPad { get; set; } = 1;
public static DiffusionModelParameter Create() => new();
}

View File

@ -0,0 +1,11 @@
namespace StableDiffusion.NET;
public sealed class EasyCache
{
public bool IsEnabled { get; set; }
public float ReuseThreshold { get; set; }
public float StartPercent { get; set; }
public float EndPercent { get; set; }
internal EasyCache() { }
}

View File

@ -0,0 +1,291 @@
using System;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public static class DiffusionModelBuilderExtension
{
public static DiffusionModelParameter WithVae(this DiffusionModelParameter parameter, string vaePath)
{
ArgumentNullException.ThrowIfNull(vaePath);
if (!string.IsNullOrWhiteSpace(parameter.TaesdPath)) throw new ArgumentException("TAESD is already enabled. VAE and TAESD are mutually exclusive.", nameof(vaePath));
parameter.VaePath = vaePath;
return parameter;
}
public static DiffusionModelParameter WithTaesd(this DiffusionModelParameter parameter, string taesdPath)
{
ArgumentNullException.ThrowIfNull(taesdPath);
if (!string.IsNullOrWhiteSpace(parameter.VaePath)) throw new ArgumentException("VAE is already enabled. TAESD and VAE are mutually exclusive.", nameof(taesdPath));
parameter.TaesdPath = taesdPath;
return parameter;
}
public static DiffusionModelParameter WithLoraSupport(this DiffusionModelParameter parameter, string loraModelDirectory)
{
ArgumentNullException.ThrowIfNull(loraModelDirectory);
parameter.LoraModelDirectory = loraModelDirectory;
return parameter;
}
public static DiffusionModelParameter WithEmbeddingSupport(this DiffusionModelParameter parameter, string embeddingsDirectory)
{
ArgumentNullException.ThrowIfNull(embeddingsDirectory);
parameter.EmbeddingsDirectory = embeddingsDirectory;
return parameter;
}
public static DiffusionModelParameter WithControlNet(this DiffusionModelParameter parameter, string controlNetPath)
{
ArgumentNullException.ThrowIfNull(controlNetPath);
parameter.ControlNetPath = controlNetPath;
return parameter;
}
public static DiffusionModelParameter WithoutMultithreading(this DiffusionModelParameter parameter)
{
parameter.ThreadCount = 1;
return parameter;
}
public static DiffusionModelParameter WithMultithreading(this DiffusionModelParameter parameter, int threadCount = 0)
{
ArgumentOutOfRangeException.ThrowIfLessThan(threadCount, 0, nameof(threadCount));
if (threadCount == 0) threadCount = Environment.ProcessorCount;
parameter.ThreadCount = threadCount;
return parameter;
}
public static DiffusionModelParameter WithVaeDecodeOnly(this DiffusionModelParameter parameter, bool vaeDecodeOnly = true)
{
parameter.VaeDecodeOnly = vaeDecodeOnly;
return parameter;
}
public static DiffusionModelParameter WithImmediatelyFreedParams(this DiffusionModelParameter parameter, bool immediatelyFreedParams = true)
{
parameter.FreeParamsImmediately = immediatelyFreedParams;
return parameter;
}
public static DiffusionModelParameter WithVaeTiling(this DiffusionModelParameter parameter, bool vaeTiling = true)
{
parameter.VaeTiling = vaeTiling;
return parameter;
}
public static DiffusionModelParameter WithOffloadedParamsToCPU(this DiffusionModelParameter parameter, bool offloadParamsToCPU = true)
{
parameter.OffloadParamsToCPU = offloadParamsToCPU;
return parameter;
}
public static DiffusionModelParameter WithClipNetOnCpu(this DiffusionModelParameter parameter, bool keepClipNetOnCpu = true)
{
parameter.KeepClipOnCPU = keepClipNetOnCpu;
return parameter;
}
public static DiffusionModelParameter WithControlNetOnCpu(this DiffusionModelParameter parameter, bool keepControlNetOnCpu = true)
{
parameter.KeepControlNetOnCPU = keepControlNetOnCpu;
return parameter;
}
public static DiffusionModelParameter WithVaeOnCpu(this DiffusionModelParameter parameter, bool keepVaeOnCpu = true)
{
parameter.KeepVaeOnCPU = keepVaeOnCpu;
return parameter;
}
public static DiffusionModelParameter WithFlashAttention(this DiffusionModelParameter parameter, bool flashAttention = true)
{
parameter.FlashAttention = flashAttention;
return parameter;
}
public static DiffusionModelParameter WithDiffusionConvDirect(this DiffusionModelParameter parameter, bool diffusionConvDirect = true)
{
parameter.DiffusionConvDirect = diffusionConvDirect;
return parameter;
}
public static DiffusionModelParameter WithVaeConvDirect(this DiffusionModelParameter parameter, bool vaeConvDirect = true)
{
parameter.VaeConvDirect = vaeConvDirect;
return parameter;
}
public static DiffusionModelParameter WithRngType(this DiffusionModelParameter parameter, RngType rngType)
{
if (!Enum.IsDefined(rngType)) throw new ArgumentOutOfRangeException(nameof(rngType));
parameter.RngType = rngType;
return parameter;
}
public static DiffusionModelParameter WithPrediction(this DiffusionModelParameter parameter, Prediction prediction)
{
parameter.Prediction = prediction;
return parameter;
}
public static DiffusionModelParameter WithQuantization(this DiffusionModelParameter parameter, Quantization quantization)
{
if (!Enum.IsDefined(quantization)) throw new ArgumentOutOfRangeException(nameof(quantization));
parameter.Quantization = quantization;
return parameter;
}
public static DiffusionModelParameter WithFlowShift(this DiffusionModelParameter parameter, float flowShift)
{
parameter.FlowShift = flowShift;
return parameter;
}
public static DiffusionModelParameter WithForcedSdxlVaeConvScale(this DiffusionModelParameter parameter, bool forcedScale = true)
{
parameter.ForceSdxlVaeConvScale = forcedScale;
return parameter;
}
public static DiffusionModelParameter WithPhotomaker(this DiffusionModelParameter parameter, string stackedIdEmbeddingsDirectory)
{
ArgumentException.ThrowIfNullOrWhiteSpace(stackedIdEmbeddingsDirectory, nameof(stackedIdEmbeddingsDirectory));
parameter.StackedIdEmbeddingsDirectory = stackedIdEmbeddingsDirectory;
return parameter;
}
public static DiffusionModelParameter WithModelPath(this DiffusionModelParameter parameter, string modelPath)
{
ArgumentNullException.ThrowIfNull(modelPath);
parameter.ModelPath = modelPath;
return parameter;
}
public static DiffusionModelParameter WithDiffusionModelPath(this DiffusionModelParameter parameter, string diffusionModelPath)
{
ArgumentNullException.ThrowIfNull(diffusionModelPath);
parameter.DiffusionModelPath = diffusionModelPath;
return parameter;
}
public static DiffusionModelParameter WithClipLPath(this DiffusionModelParameter parameter, string clipLPath)
{
ArgumentNullException.ThrowIfNull(clipLPath);
parameter.ClipLPath = clipLPath;
return parameter;
}
public static DiffusionModelParameter WithClipGPath(this DiffusionModelParameter parameter, string clipGPath)
{
ArgumentNullException.ThrowIfNull(clipGPath);
parameter.ClipGPath = clipGPath;
return parameter;
}
public static DiffusionModelParameter WithT5xxlPath(this DiffusionModelParameter parameter, string t5xxlPath)
{
ArgumentNullException.ThrowIfNull(t5xxlPath);
parameter.T5xxlPath = t5xxlPath;
return parameter;
}
[Obsolete("Use WithLLMPath instead")]
public static DiffusionModelParameter WithQwen2VLPath(this DiffusionModelParameter parameter, string qwen2VLPath) => parameter.WithLLMPath(qwen2VLPath);
public static DiffusionModelParameter WithLLMPath(this DiffusionModelParameter parameter, string llmPath)
{
parameter.LLMPath = llmPath;
return parameter;
}
[Obsolete("Use WithLLMVisionPath instead")]
public static DiffusionModelParameter WithQwen2VLVisionPath(this DiffusionModelParameter parameter, string qwen2VLVisionPath) => parameter.WithLLMVisionPath(qwen2VLVisionPath);
public static DiffusionModelParameter WithLLMVisionPath(this DiffusionModelParameter parameter, string llmVisionPath)
{
parameter.LLMVisionPath = llmVisionPath;
return parameter;
}
public static DiffusionModelParameter WithClipVisionPath(this DiffusionModelParameter parameter, string clipVisionPath)
{
parameter.ClipVisionPath = clipVisionPath;
return parameter;
}
public static DiffusionModelParameter WithHighNoiseDiffusionModelPath(this DiffusionModelParameter parameter, string highNoiseDiffusionModelPath)
{
parameter.HighNoiseDiffusionModelPath = highNoiseDiffusionModelPath;
return parameter;
}
public static DiffusionModelParameter UseChromaDitMap(this DiffusionModelParameter parameter, bool useChromaDitMap = true)
{
parameter.ChromaUseDitMap = useChromaDitMap;
return parameter;
}
public static DiffusionModelParameter EnableChromaT5Map(this DiffusionModelParameter parameter, bool enableChromaT5Map = true)
{
parameter.ChromaEnableT5Map = enableChromaT5Map;
return parameter;
}
public static DiffusionModelParameter WithChromaT5MaskPad(this DiffusionModelParameter parameter, int chromaT5MaskPad)
{
parameter.ChromaT5MaskPad = chromaT5MaskPad;
return parameter;
}
}

View File

@ -0,0 +1,302 @@
using HPPH;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public static class ImageGenerationParameterExtension
{
public static ImageGenerationParameter WithPrompt(this ImageGenerationParameter parameter, string prompt)
{
parameter.Prompt = prompt;
return parameter;
}
public static ImageGenerationParameter WithNegativePrompt(this ImageGenerationParameter parameter, string negativePrompt)
{
parameter.NegativePrompt = negativePrompt;
return parameter;
}
public static ImageGenerationParameter WithClipSkip(this ImageGenerationParameter parameter, int clipSkip)
{
parameter.ClipSkip = clipSkip;
return parameter;
}
public static ImageGenerationParameter WithInitImage(this ImageGenerationParameter parameter, IImage image)
{
parameter.InitImage = image;
return parameter;
}
public static ImageGenerationParameter WithRefImages(this ImageGenerationParameter parameter, params IImage[] images)
{
parameter.RefImages = images;
return parameter;
}
public static ImageGenerationParameter WithMaskImage(this ImageGenerationParameter parameter, IImage image)
{
parameter.MaskImage = image;
return parameter;
}
public static ImageGenerationParameter WithRefIndexIncrease(this ImageGenerationParameter parameter, bool refIndexIncrease = true)
{
parameter.IncreaseRefIndex = refIndexIncrease;
return parameter;
}
public static ImageGenerationParameter WithRefImageAutoResize(this ImageGenerationParameter parameter, bool refImageAutoResize = true)
{
parameter.AutoResizeRefImage = refImageAutoResize;
return parameter;
}
public static ImageGenerationParameter WithSize(this ImageGenerationParameter parameter, int? width = null, int? height = null)
{
if (width != null)
parameter.Width = width.Value;
if (height != null)
parameter.Height = height.Value;
return parameter;
}
#region SampleParameter
#region Guidance
public static ImageGenerationParameter WithCfg(this ImageGenerationParameter parameter, float cfg)
{
parameter.WithTxtCfg(cfg);
parameter.WithImgCfg(cfg);
return parameter;
}
public static ImageGenerationParameter WithTxtCfg(this ImageGenerationParameter parameter, float txtCfg)
{
parameter.SampleParameter.Guidance.TxtCfg = txtCfg;
return parameter;
}
public static ImageGenerationParameter WithImgCfg(this ImageGenerationParameter parameter, float imgCfg)
{
parameter.SampleParameter.Guidance.ImgCfg = imgCfg;
return parameter;
}
public static ImageGenerationParameter WithMinCfg(this ImageGenerationParameter parameter, float minCfg)
{
parameter.SampleParameter.Guidance.MinCfg = minCfg;
return parameter;
}
public static ImageGenerationParameter WithGuidance(this ImageGenerationParameter parameter, float guidance)
{
parameter.SampleParameter.Guidance.DistilledGuidance = guidance;
return parameter;
}
#region Slg
public static ImageGenerationParameter WithSkipLayers(this ImageGenerationParameter parameter, int[] layers)
{
parameter.SampleParameter.Guidance.Slg.Layers = layers;
return parameter;
}
public static ImageGenerationParameter WithSkipLayerStart(this ImageGenerationParameter parameter, float skipLayerStart)
{
parameter.SampleParameter.Guidance.Slg.SkipLayerStart = skipLayerStart;
return parameter;
}
public static ImageGenerationParameter WithSkipLayerEnd(this ImageGenerationParameter parameter, float skipLayerEnd)
{
parameter.SampleParameter.Guidance.Slg.SkipLayerEnd = skipLayerEnd;
return parameter;
}
public static ImageGenerationParameter WithSlgScale(this ImageGenerationParameter parameter, float slgScale)
{
parameter.SampleParameter.Guidance.Slg.Scale = slgScale;
return parameter;
}
#endregion
#endregion
public static ImageGenerationParameter WithScheduler(this ImageGenerationParameter parameter, Scheduler scheduler)
{
parameter.SampleParameter.Scheduler = scheduler;
return parameter;
}
public static ImageGenerationParameter WithSampler(this ImageGenerationParameter parameter, Sampler sampler)
{
parameter.SampleParameter.SampleMethod = sampler;
return parameter;
}
public static ImageGenerationParameter WithSteps(this ImageGenerationParameter parameter, int steps)
{
parameter.SampleParameter.SampleSteps = steps;
return parameter;
}
public static ImageGenerationParameter WithEta(this ImageGenerationParameter parameter, float eta)
{
parameter.SampleParameter.Eta = eta;
return parameter;
}
public static ImageGenerationParameter WithShiftedTimestep(this ImageGenerationParameter parameter, int shiftedTimestep)
{
parameter.SampleParameter.ShiftedTimestep = shiftedTimestep;
return parameter;
}
#endregion
public static ImageGenerationParameter WithStrength(this ImageGenerationParameter parameter, float strength)
{
parameter.Strength = strength;
return parameter;
}
public static ImageGenerationParameter WithSeed(this ImageGenerationParameter parameter, long seed)
{
parameter.Seed = seed;
return parameter;
}
public static ImageGenerationParameter WithControlNet(this ImageGenerationParameter parameter, IImage image, float? strength = null)
{
parameter.ControlNet.Image = image;
if (strength != null)
parameter.ControlNet.Strength = strength.Value;
return parameter;
}
public static ImageGenerationParameter WithPhotomaker(this ImageGenerationParameter parameter, string inputIdImageDirectory, float? styleStrength = null, bool? normalizeInput = null)
{
//todo
//parameter.PhotoMaker.InputIdImageDirectory = inputIdImageDirectory;
//if (styleStrength != null)
// parameter.PhotoMaker.StyleStrength = styleStrength.Value;
//if (normalizeInput != null)
// parameter.PhotoMaker.NormalizeInput = normalizeInput.Value;
return parameter;
}
#region VaeTiling
public static ImageGenerationParameter WithVaeTiling(this ImageGenerationParameter parameter, bool tiling = true)
{
parameter.VaeTiling.IsEnabled = tiling;
return parameter;
}
public static ImageGenerationParameter WithVaeTileSizeX(this ImageGenerationParameter parameter, int tileSizeX)
{
parameter.VaeTiling.TileSizeX = tileSizeX;
return parameter;
}
public static ImageGenerationParameter WithVaeTileSizeY(this ImageGenerationParameter parameter, int tileSizeY)
{
parameter.VaeTiling.TileSizeY = tileSizeY;
return parameter;
}
public static ImageGenerationParameter WithVaeTargetOverlap(this ImageGenerationParameter parameter, float targetOverlap)
{
parameter.VaeTiling.TargetOverlap = targetOverlap;
return parameter;
}
public static ImageGenerationParameter WithVaeRelSizeX(this ImageGenerationParameter parameter, float relSizeX)
{
parameter.VaeTiling.RelSizeX = relSizeX;
return parameter;
}
public static ImageGenerationParameter WithVaeRelSizeY(this ImageGenerationParameter parameter, float relSizeY)
{
parameter.VaeTiling.RelSizeY = relSizeY;
return parameter;
}
#endregion
#region Defaults
public static ImageGenerationParameter WithSd1Defaults(this ImageGenerationParameter parameter)
=> parameter.WithSize(512, 512)
.WithCfg(7.5f)
.WithGuidance(1f)
.WithSteps(25)
.WithSampler(Sampler.Euler_A);
public static ImageGenerationParameter WithSDXLDefaults(this ImageGenerationParameter parameter)
=> parameter.WithSize(1024, 1024)
.WithCfg(7f)
.WithGuidance(1f)
.WithSteps(30)
.WithSampler(Sampler.Euler_A);
public static ImageGenerationParameter WithSD3_5Defaults(this ImageGenerationParameter parameter)
=> parameter.WithSize(1024, 1024)
.WithCfg(4.5f)
.WithGuidance(1f)
.WithSteps(20)
.WithSampler(Sampler.Euler);
public static ImageGenerationParameter WithFluxDefaults(this ImageGenerationParameter parameter)
=> parameter.WithSize(1024, 1024)
.WithCfg(1f)
.WithGuidance(3.5f)
.WithSteps(20)
.WithSampler(Sampler.Euler);
#endregion
}

View File

@ -0,0 +1,49 @@
using System;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public static class UpscaleModelBuilderExtension
{
public static UpscaleModelParameter WithModelPath(this UpscaleModelParameter parameter, string modelPath)
{
ArgumentNullException.ThrowIfNull(modelPath);
parameter.ModelPath = modelPath;
return parameter;
}
public static UpscaleModelParameter WithOffloadedParamsToCPU(this UpscaleModelParameter parameter, bool offloadParamsToCPU = true)
{
parameter.OffloadParamsToCPU = offloadParamsToCPU;
return parameter;
}
public static UpscaleModelParameter WithoutMultithreading(this UpscaleModelParameter parameter)
{
parameter.ThreadCount = 1;
return parameter;
}
public static UpscaleModelParameter WithMultithreading(this UpscaleModelParameter parameter, int threadCount = 0)
{
ArgumentOutOfRangeException.ThrowIfLessThan(threadCount, 0, nameof(threadCount));
if (threadCount == 0) threadCount = Environment.ProcessorCount;
parameter.ThreadCount = threadCount;
return parameter;
}
public static UpscaleModelParameter WithConvDirect(this UpscaleModelParameter parameter, bool convDirect = true)
{
parameter.ConvDirect = convDirect;
return parameter;
}
}

View File

@ -0,0 +1,318 @@
using HPPH;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public static class VideoGenerationParameterExtension
{
public static VideoGenerationParameter WithPrompt(this VideoGenerationParameter parameter, string prompt)
{
parameter.Prompt = prompt;
return parameter;
}
public static VideoGenerationParameter WithNegativePrompt(this VideoGenerationParameter parameter, string negativePrompt)
{
parameter.NegativePrompt = negativePrompt;
return parameter;
}
public static VideoGenerationParameter WithClipSkip(this VideoGenerationParameter parameter, int clipSkip)
{
parameter.ClipSkip = clipSkip;
return parameter;
}
public static VideoGenerationParameter WithSize(this VideoGenerationParameter parameter, int? width = null, int? height = null)
{
if (width != null)
parameter.Width = width.Value;
if (height != null)
parameter.Height = height.Value;
return parameter;
}
public static VideoGenerationParameter WithInitImage(this VideoGenerationParameter parameter, IImage image)
{
parameter.InitImage = image;
return parameter;
}
public static VideoGenerationParameter WithEndImage(this VideoGenerationParameter parameter, IImage image)
{
parameter.EndImage = image;
return parameter;
}
public static VideoGenerationParameter WithControlFrames(this VideoGenerationParameter parameter, params IImage[] images)
{
parameter.ControlFrames = images;
return parameter;
}
#region SampleParameter
#region Guidance
public static VideoGenerationParameter WithCfg(this VideoGenerationParameter parameter, float cfg)
{
parameter.WithTxtCfg(cfg);
parameter.WithImgCfg(cfg);
return parameter;
}
public static VideoGenerationParameter WithTxtCfg(this VideoGenerationParameter parameter, float txtCfg)
{
parameter.SampleParameter.Guidance.TxtCfg = txtCfg;
return parameter;
}
public static VideoGenerationParameter WithImgCfg(this VideoGenerationParameter parameter, float imgCfg)
{
parameter.SampleParameter.Guidance.ImgCfg = imgCfg;
return parameter;
}
public static VideoGenerationParameter WithMinCfg(this VideoGenerationParameter parameter, float minCfg)
{
parameter.SampleParameter.Guidance.MinCfg = minCfg;
return parameter;
}
public static VideoGenerationParameter WithGuidance(this VideoGenerationParameter parameter, float guidance)
{
parameter.SampleParameter.Guidance.DistilledGuidance = guidance;
return parameter;
}
#region Slg
public static VideoGenerationParameter WithSkipLayers(this VideoGenerationParameter parameter, int[] layers)
{
parameter.SampleParameter.Guidance.Slg.Layers = layers;
return parameter;
}
public static VideoGenerationParameter WithSkipLayerStart(this VideoGenerationParameter parameter, float skipLayerStart)
{
parameter.SampleParameter.Guidance.Slg.SkipLayerStart = skipLayerStart;
return parameter;
}
public static VideoGenerationParameter WithSkipLayerEnd(this VideoGenerationParameter parameter, float skipLayerEnd)
{
parameter.SampleParameter.Guidance.Slg.SkipLayerEnd = skipLayerEnd;
return parameter;
}
public static VideoGenerationParameter WithSlgScale(this VideoGenerationParameter parameter, float slgScale)
{
parameter.SampleParameter.Guidance.Slg.Scale = slgScale;
return parameter;
}
#endregion
#endregion
public static VideoGenerationParameter WithScheduler(this VideoGenerationParameter parameter, Scheduler scheduler)
{
parameter.SampleParameter.Scheduler = scheduler;
return parameter;
}
public static VideoGenerationParameter WithSampler(this VideoGenerationParameter parameter, Sampler sampler)
{
parameter.SampleParameter.SampleMethod = sampler;
return parameter;
}
public static VideoGenerationParameter WithSteps(this VideoGenerationParameter parameter, int steps)
{
parameter.SampleParameter.SampleSteps = steps;
return parameter;
}
public static VideoGenerationParameter WithEta(this VideoGenerationParameter parameter, float eta)
{
parameter.SampleParameter.Eta = eta;
return parameter;
}
public static VideoGenerationParameter WithShiftedTimestep(this VideoGenerationParameter parameter, int shiftedTimestep)
{
parameter.SampleParameter.ShiftedTimestep = shiftedTimestep;
return parameter;
}
#endregion
#region HighNoiseSampleParameter
#region Guidance
public static VideoGenerationParameter WithHighNoiseCfg(this VideoGenerationParameter parameter, float cfg)
{
parameter.WithTxtCfg(cfg);
parameter.WithImgCfg(cfg);
return parameter;
}
public static VideoGenerationParameter WithHighNoiseTxtCfg(this VideoGenerationParameter parameter, float txtCfg)
{
parameter.HighNoiseSampleParameter.Guidance.TxtCfg = txtCfg;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseImgCfg(this VideoGenerationParameter parameter, float imgCfg)
{
parameter.HighNoiseSampleParameter.Guidance.ImgCfg = imgCfg;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseMinCfg(this VideoGenerationParameter parameter, float minCfg)
{
parameter.HighNoiseSampleParameter.Guidance.MinCfg = minCfg;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseGuidance(this VideoGenerationParameter parameter, float guidance)
{
parameter.HighNoiseSampleParameter.Guidance.DistilledGuidance = guidance;
return parameter;
}
#region Slg
public static VideoGenerationParameter WithHighNoiseSkipLayers(this VideoGenerationParameter parameter, int[] layers)
{
parameter.HighNoiseSampleParameter.Guidance.Slg.Layers = layers;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseSkipLayerStart(this VideoGenerationParameter parameter, float skipLayerStart)
{
parameter.HighNoiseSampleParameter.Guidance.Slg.SkipLayerStart = skipLayerStart;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseSkipLayerEnd(this VideoGenerationParameter parameter, float skipLayerEnd)
{
parameter.HighNoiseSampleParameter.Guidance.Slg.SkipLayerEnd = skipLayerEnd;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseSlgScale(this VideoGenerationParameter parameter, float slgScale)
{
parameter.HighNoiseSampleParameter.Guidance.Slg.Scale = slgScale;
return parameter;
}
#endregion
#endregion
public static VideoGenerationParameter WithHighNoiseScheduler(this VideoGenerationParameter parameter, Scheduler scheduler)
{
parameter.HighNoiseSampleParameter.Scheduler = scheduler;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseSampler(this VideoGenerationParameter parameter, Sampler sampler)
{
parameter.HighNoiseSampleParameter.SampleMethod = sampler;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseSteps(this VideoGenerationParameter parameter, int steps)
{
parameter.HighNoiseSampleParameter.SampleSteps = steps;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseEta(this VideoGenerationParameter parameter, float eta)
{
parameter.HighNoiseSampleParameter.Eta = eta;
return parameter;
}
public static VideoGenerationParameter WithHighNoiseShiftedTimestep(this VideoGenerationParameter parameter, int shiftedTimestep)
{
parameter.HighNoiseSampleParameter.ShiftedTimestep = shiftedTimestep;
return parameter;
}
#endregion
public static VideoGenerationParameter WithMoeBoundry(this VideoGenerationParameter parameter, float moeBoundry)
{
parameter.MoeBoundry = moeBoundry;
return parameter;
}
public static VideoGenerationParameter WithStrength(this VideoGenerationParameter parameter, float strength)
{
parameter.Strength = strength;
return parameter;
}
public static VideoGenerationParameter WithSeed(this VideoGenerationParameter parameter, long seed)
{
parameter.Seed = seed;
return parameter;
}
public static VideoGenerationParameter WithFrameCount(this VideoGenerationParameter parameter, int frameCount)
{
parameter.FrameCount = frameCount;
return parameter;
}
public static VideoGenerationParameter WithVaceStrength(this VideoGenerationParameter parameter, float vaceStrength)
{
parameter.VaceStrength = vaceStrength;
return parameter;
}
}

View File

@ -0,0 +1,13 @@
namespace StableDiffusion.NET;
public sealed class GuidanceParameter
{
public float TxtCfg { get; set; } = 7.0f;
public float ImgCfg { get; set; } = 7.0f;
public float MinCfg { get; set; } = 1.0f;
public float DistilledGuidance { get; set; } = 3.5f;
public SlgParameter Slg { get; } = new();
internal GuidanceParameter() { }
}

View File

@ -0,0 +1,70 @@
using HPPH;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class ImageGenerationParameter
{
#region Properties & Fields
public string Prompt { get; set; } = string.Empty;
/// <summary>
/// the negative prompt (default: "");
/// </summary>
public string NegativePrompt { get; set; } = string.Empty;
/// <summary>
/// ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer (default: -1)
/// -1 represents unspecified, will be 1 for SD1.x, 2 for SD2.x
/// </summary>
public int ClipSkip { get; set; } = -1;
public IImage? InitImage { get; set; }
public IImage[]? RefImages { get; set; }
public bool IncreaseRefIndex { get; set; } = false;
public bool AutoResizeRefImage { get; set; } = false;
public IImage? MaskImage { get; set; }
/// <summary>
/// image width, in pixel space (default: 512)
/// </summary>
public int Width { get; set; } = 512;
/// <summary>
/// image height, in pixel space (default: 512)
/// </summary>
public int Height { get; set; } = 512;
public SampleParameter SampleParameter { get; internal init; } = new();
/// <summary>
/// strength for noising/unnoising (default: 0.7)
/// </summary>
public float Strength { get; set; } = 0.7f;
/// <summary>
/// RNG seed. use -1 for a random seed (default: -1)
/// </summary>
public long Seed { get; set; } = -1;
public ControlNetParameter ControlNet { get; } = new();
public PhotoMakerParameter PhotoMaker { get; } = new();
public TilingParameter VaeTiling { get; } = new();
public EasyCache EasyCache { get; } = new();
#endregion
public static ImageGenerationParameter Create() => new();
public static ImageGenerationParameter TextToImage(string prompt) => Create().WithPrompt(prompt);
public static ImageGenerationParameter ImageToImage(string prompt, IImage image) => Create().WithPrompt(prompt).WithInitImage(image);
}

View File

@ -0,0 +1,17 @@
using HPPH;
namespace StableDiffusion.NET;
public sealed class PhotoMakerParameter
{
public IImage[]? IdImages { get; set; }
public string IdEmbedPath { get; set; } = string.Empty;
/// <summary>
/// strength for keeping input identity (default: 20)
/// </summary>
public float StyleStrength { get; set; } = 20f;
internal PhotoMakerParameter() { }
}

View File

@ -0,0 +1,30 @@
namespace StableDiffusion.NET;
public sealed class SampleParameter
{
public GuidanceParameter Guidance { get; } = new();
/// <summary>
/// Denoiser sigma schedule (default: Default)
/// </summary>
public Scheduler Scheduler { get; set; } = Scheduler.Default;
/// <summary>
/// sampling method (default: Euler_A)
/// </summary>
public Sampler SampleMethod { get; set; } = Sampler.Euler_A;
/// <summary>
/// number of sample steps (default: 25)
/// </summary>
public int SampleSteps { get; set; } = 25;
/// <summary>
/// eta in DDIM, only for DDIM and TCD (default: 0)
/// </summary>
public float Eta { get; set; } = 0f;
public int ShiftedTimestep { get; set; } = 0;
internal SampleParameter() { }
}

View File

@ -0,0 +1,27 @@
namespace StableDiffusion.NET;
public sealed class SlgParameter
{
/// <summary>
/// Layers to skip for SLG steps: (default: [7,8,9])
/// </summary>
public int[] Layers { get; set; } = [7, 8, 9];
/// <summary>
/// SLG enabling point: (default: 0.01)
/// </summary>
public float SkipLayerStart { get; set; } = 0.01f;
/// <summary>
/// SLG disabling point: (default: 0.2)
/// </summary>
public float SkipLayerEnd { get; set; } = 0.2f;
/// <summary>
/// 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
/// </summary>
public float Scale { get; set; } = 0f;
internal SlgParameter() { }
}

View File

@ -0,0 +1,14 @@
namespace StableDiffusion.NET;
public sealed class TilingParameter
{
public bool IsEnabled { get; set; }
public int TileSizeX { get; set; }
public int TileSizeY { get; set; }
public float TargetOverlap { get; set; }
public float RelSizeX { get; set; }
public float RelSizeY { get; set; }
internal TilingParameter() { }
}

View File

@ -0,0 +1,28 @@
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class UpscaleModelParameter
{
/// <summary>
/// path to esrgan model. Upscale images after generate, just RealESRGAN_x4plus_anime_6B supported by now
/// </summary>
public string ModelPath { get; set; } = string.Empty;
public bool OffloadParamsToCPU { get; set; } = false;
/// <summary>
/// number of threads to use during computation (default: -1)
/// If threads = -1, then threads will be set to the number of CPU physical cores
/// </summary>
public int ThreadCount { get; set; } = 1;
/// <summary>
/// use Conv2d direct in the diffusion model
/// This might crash if it is not supported by the backend.
/// </summary>
public bool ConvDirect { get; set; } = false;
public static UpscaleModelParameter Create() => new();
}

View File

@ -0,0 +1,48 @@
using HPPH;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class VideoGenerationParameter
{
#region Properties & Fields
public string Prompt { get; set; } = string.Empty;
public string NegativePrompt { get; set; } = string.Empty;
public int ClipSkip { get; set; } = -1;
public IImage? InitImage { get; set; }
public IImage? EndImage { get; set; }
public IImage[]? ControlFrames { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public SampleParameter SampleParameter { get; internal init; } = new();
public SampleParameter HighNoiseSampleParameter { get; internal init; } = new();
public float MoeBoundry { get; set; }
public float Strength { get; set; }
public long Seed { get; set; }
public int FrameCount { get; set; }
public float VaceStrength { get; set; }
public EasyCache EasyCache { get; } = new();
#endregion
public static VideoGenerationParameter Create() => new();
public static VideoGenerationParameter TextToVideo(string prompt) => Create().WithPrompt(prompt);
}

View File

@ -0,0 +1,71 @@
using HPPH;
using JetBrains.Annotations;
using System;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed unsafe class UpscaleModel : IDisposable
{
#region Properties & Fields
private bool _disposed;
public UpscaleModelParameter ModelParameter { get; }
private Native.Types.upscaler_ctx_t* _ctx;
#endregion
#region Constructors
public UpscaleModel(UpscaleModelParameter modelParameter)
{
ArgumentNullException.ThrowIfNull(modelParameter, nameof(modelParameter));
modelParameter.Validate();
this.ModelParameter = modelParameter;
Initialize();
}
~UpscaleModel() => Dispose();
#endregion
#region Methods
private void Initialize()
{
_ctx = Native.new_upscaler_ctx(ModelParameter.ModelPath,
ModelParameter.OffloadParamsToCPU,
ModelParameter.ConvDirect,
ModelParameter.ThreadCount);
if (_ctx == null) throw new NullReferenceException("Failed to initialize upscale-model.");
}
public Image<ColorRGB> Upscale(IImage image, int upscaleFactor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(upscaleFactor, 0, nameof(upscaleFactor));
if (_ctx == null) throw new NullReferenceException("The model is not initialized.");
return Native.upscale(_ctx, image, (uint)upscaleFactor);
}
public void Dispose()
{
if (_disposed) return;
if (_ctx != null)
Native.free_upscaler_ctx(_ctx);
GC.SuppressFinalize(this);
_disposed = true;
}
#endregion
}

View File

@ -0,0 +1,110 @@
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
[CustomMarshaller(typeof(DiffusionModelParameter), MarshalMode.ManagedToUnmanagedIn, typeof(DiffusionModelParameterMarshaller))]
[CustomMarshaller(typeof(DiffusionModelParameter), MarshalMode.ManagedToUnmanagedRef, typeof(DiffusionModelParameterMarshaller))]
internal static unsafe class DiffusionModelParameterMarshaller
{
public static Native.Types.sd_ctx_params_t ConvertToUnmanaged(DiffusionModelParameter managed)
=> new()
{
model_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.ModelPath),
clip_l_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.ClipLPath),
clip_g_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.ClipGPath),
clip_vision_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.ClipVisionPath),
t5xxl_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.T5xxlPath),
llm_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.LLMPath),
llm_vision_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.LLMVisionPath),
diffusion_model_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.DiffusionModelPath),
high_noise_diffusion_model_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.HighNoiseDiffusionModelPath),
vae_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.VaePath),
taesd_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.TaesdPath),
control_net_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.ControlNetPath),
lora_model_dir = AnsiStringMarshaller.ConvertToUnmanaged(managed.LoraModelDirectory),
embedding_dir = AnsiStringMarshaller.ConvertToUnmanaged(managed.EmbeddingsDirectory),
photo_maker_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.StackedIdEmbeddingsDirectory),
tensor_type_rules = AnsiStringMarshaller.ConvertToUnmanaged(managed.TensorTypeRules),
vae_decode_only = (sbyte)(managed.VaeDecodeOnly ? 1 : 0),
free_params_immediately = (sbyte)(managed.FreeParamsImmediately ? 1 : 0),
n_threads = managed.ThreadCount,
wtype = managed.Quantization,
rng_type = managed.RngType,
sampler_rng_type = managed.SamplerRngType,
prediction = managed.Prediction,
lora_apply_mode = managed.LoraApplyMode,
offload_params_to_cpu = (sbyte)(managed.OffloadParamsToCPU ? 1 : 0),
keep_clip_on_cpu = (sbyte)(managed.KeepClipOnCPU ? 1 : 0),
keep_control_net_on_cpu = (sbyte)(managed.KeepControlNetOnCPU ? 1 : 0),
keep_vae_on_cpu = (sbyte)(managed.KeepVaeOnCPU ? 1 : 0),
diffusion_flash_attn = (sbyte)(managed.FlashAttention ? 1 : 0),
tae_preview_only = (sbyte)(managed.TaePreviewOnly ? 1 : 0),
diffusion_conv_direct = (sbyte)(managed.DiffusionConvDirect ? 1 : 0),
vae_conv_direct = (sbyte)(managed.VaeConvDirect ? 1 : 0),
force_sdxl_vae_conv_scale = (sbyte)(managed.ForceSdxlVaeConvScale ? 1 : 0),
chroma_use_dit_mask = (sbyte)(managed.ChromaUseDitMap ? 1 : 0),
chroma_use_t5_mask = (sbyte)(managed.ChromaEnableT5Map ? 1 : 0),
chroma_t5_mask_pad = managed.ChromaT5MaskPad,
flow_shift = managed.FlowShift
};
public static DiffusionModelParameter ConvertToManaged(Native.Types.sd_ctx_params_t unmanaged)
=> new()
{
ModelPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.model_path) ?? string.Empty,
ClipLPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.clip_l_path) ?? string.Empty,
ClipGPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.clip_g_path) ?? string.Empty,
ClipVisionPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.clip_vision_path) ?? string.Empty,
T5xxlPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.t5xxl_path) ?? string.Empty,
LLMPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.llm_path) ?? string.Empty,
LLMVisionPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.llm_vision_path) ?? string.Empty,
DiffusionModelPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.diffusion_model_path) ?? string.Empty,
HighNoiseDiffusionModelPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.high_noise_diffusion_model_path) ?? string.Empty,
VaePath = AnsiStringMarshaller.ConvertToManaged(unmanaged.vae_path) ?? string.Empty,
TaesdPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.taesd_path) ?? string.Empty,
ControlNetPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.control_net_path) ?? string.Empty,
LoraModelDirectory = AnsiStringMarshaller.ConvertToManaged(unmanaged.lora_model_dir) ?? string.Empty,
EmbeddingsDirectory = AnsiStringMarshaller.ConvertToManaged(unmanaged.embedding_dir) ?? string.Empty,
StackedIdEmbeddingsDirectory = AnsiStringMarshaller.ConvertToManaged(unmanaged.photo_maker_path) ?? string.Empty,
TensorTypeRules = AnsiStringMarshaller.ConvertToManaged(unmanaged.tensor_type_rules) ?? string.Empty,
VaeDecodeOnly = unmanaged.vae_decode_only == 1,
FreeParamsImmediately = unmanaged.free_params_immediately == 1,
ThreadCount = unmanaged.n_threads,
Quantization = unmanaged.wtype,
RngType = unmanaged.rng_type,
SamplerRngType = unmanaged.sampler_rng_type,
Prediction = unmanaged.prediction,
LoraApplyMode = unmanaged.lora_apply_mode,
OffloadParamsToCPU = unmanaged.offload_params_to_cpu == 1,
KeepClipOnCPU = unmanaged.keep_clip_on_cpu == 1,
KeepControlNetOnCPU = unmanaged.keep_control_net_on_cpu == 1,
KeepVaeOnCPU = unmanaged.keep_vae_on_cpu == 1,
FlashAttention = unmanaged.diffusion_flash_attn == 1,
TaePreviewOnly = unmanaged.tae_preview_only == 1,
DiffusionConvDirect = unmanaged.diffusion_conv_direct == 1,
VaeConvDirect = unmanaged.vae_conv_direct == 1,
ForceSdxlVaeConvScale = unmanaged.force_sdxl_vae_conv_scale == 1,
ChromaUseDitMap = unmanaged.chroma_use_dit_mask == 1,
ChromaEnableT5Map = unmanaged.chroma_use_t5_mask == 1,
ChromaT5MaskPad = unmanaged.chroma_t5_mask_pad,
FlowShift = unmanaged.flow_shift
};
public static void Free(Native.Types.sd_ctx_params_t unmanaged)
{
AnsiStringMarshaller.Free(unmanaged.model_path);
AnsiStringMarshaller.Free(unmanaged.clip_l_path);
AnsiStringMarshaller.Free(unmanaged.clip_g_path);
AnsiStringMarshaller.Free(unmanaged.t5xxl_path);
AnsiStringMarshaller.Free(unmanaged.llm_path);
AnsiStringMarshaller.Free(unmanaged.llm_vision_path);
AnsiStringMarshaller.Free(unmanaged.diffusion_model_path);
AnsiStringMarshaller.Free(unmanaged.vae_path);
AnsiStringMarshaller.Free(unmanaged.taesd_path);
AnsiStringMarshaller.Free(unmanaged.control_net_path);
AnsiStringMarshaller.Free(unmanaged.lora_model_dir);
AnsiStringMarshaller.Free(unmanaged.embedding_dir);
AnsiStringMarshaller.Free(unmanaged.photo_maker_path);
AnsiStringMarshaller.Free(unmanaged.tensor_type_rules);
}
}

View File

@ -0,0 +1,203 @@
// ReSharper disable MemberCanBeMadeStatic.Global
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
[CustomMarshaller(typeof(ImageGenerationParameter), MarshalMode.ManagedToUnmanagedIn, typeof(ImageGenerationParameterMarshallerIn))]
[CustomMarshaller(typeof(ImageGenerationParameter), MarshalMode.ManagedToUnmanagedOut, typeof(ImageGenerationParameterMarshaller))]
[CustomMarshaller(typeof(ImageGenerationParameter), MarshalMode.ManagedToUnmanagedRef, typeof(ImageGenerationParameterMarshallerRef))]
internal static class ImageGenerationParameterMarshaller
{
public static unsafe ImageGenerationParameter ConvertToManaged(Native.Types.sd_img_gen_params_t unmanaged)
{
ImageGenerationParameter parameter = new()
{
Prompt = AnsiStringMarshaller.ConvertToManaged(unmanaged.prompt) ?? string.Empty,
NegativePrompt = AnsiStringMarshaller.ConvertToManaged(unmanaged.negative_prompt) ?? string.Empty,
ClipSkip = unmanaged.clip_skip,
SampleParameter = SampleParameterMarshaller.ConvertToManaged(unmanaged.sample_params),
InitImage = unmanaged.init_image.data == null ? null : unmanaged.init_image.ToImage(),
RefImages = unmanaged.ref_images == null ? null : ImageHelper.ToImageArrayIFace(unmanaged.ref_images, unmanaged.ref_images_count),
AutoResizeRefImage = unmanaged.auto_resize_ref_image == 1,
MaskImage = unmanaged.mask_image.data == null ? null : unmanaged.mask_image.ToImage(),
Width = unmanaged.width,
Height = unmanaged.height,
Strength = unmanaged.strength,
Seed = unmanaged.seed,
ControlNet =
{
Image = unmanaged.control_image.ToImage(),
Strength = unmanaged.control_strength,
},
PhotoMaker =
{
IdImages = unmanaged.pm_params.id_images == null ? null : ImageHelper.ToImageArrayIFace(unmanaged.pm_params.id_images, unmanaged.pm_params.id_images_count),
IdEmbedPath = AnsiStringMarshaller.ConvertToManaged(unmanaged.pm_params.id_embed_path) ?? string.Empty,
StyleStrength = unmanaged.pm_params.style_strength,
},
VaeTiling =
{
IsEnabled = unmanaged.vae_tiling_params.enabled == 1,
TileSizeX = unmanaged.vae_tiling_params.tile_size_x,
TileSizeY = unmanaged.vae_tiling_params.tile_size_y,
TargetOverlap = unmanaged.vae_tiling_params.target_overlap,
RelSizeX = unmanaged.vae_tiling_params.rel_size_x,
RelSizeY = unmanaged.vae_tiling_params.rel_size_y
},
EasyCache =
{
IsEnabled = unmanaged.easycache.enabled == 1,
ReuseThreshold = unmanaged.easycache.reuse_threshold,
StartPercent = unmanaged.easycache.start_percent,
EndPercent = unmanaged.easycache.end_percent
}
};
return parameter;
}
public static unsafe void Free(Native.Types.sd_img_gen_params_t unmanaged)
{
AnsiStringMarshaller.Free(unmanaged.prompt);
AnsiStringMarshaller.Free(unmanaged.negative_prompt);
unmanaged.init_image.Free();
unmanaged.mask_image.Free();
unmanaged.control_image.Free();
if (unmanaged.ref_images != null)
ImageHelper.Free(unmanaged.ref_images, unmanaged.ref_images_count);
if (unmanaged.pm_params.id_images != null)
ImageHelper.Free(unmanaged.pm_params.id_images, unmanaged.pm_params.id_images_count);
SampleParameterMarshaller.Free(unmanaged.sample_params);
}
internal unsafe ref struct ImageGenerationParameterMarshallerIn
{
private SampleParameterMarshaller.SampleParameterMarshallerIn _sampleParameterMarshaller = new();
private Native.Types.sd_img_gen_params_t _imgGenParams;
private Native.Types.sd_image_t _initImage;
private Native.Types.sd_image_t _maskImage;
private Native.Types.sd_image_t _controlNetImage;
private Native.Types.sd_image_t* _refImages;
private Native.Types.sd_image_t* _pmIdImages;
public ImageGenerationParameterMarshallerIn() { }
public void FromManaged(ImageGenerationParameter managed)
{
_sampleParameterMarshaller.FromManaged(managed.SampleParameter);
_initImage = managed.InitImage?.ToSdImage() ?? new Native.Types.sd_image_t();
_controlNetImage = managed.ControlNet.Image?.ToSdImage() ?? new Native.Types.sd_image_t();
_refImages = managed.RefImages == null ? null : managed.RefImages.ToSdImage();
_pmIdImages = managed.PhotoMaker.IdImages == null ? null : managed.PhotoMaker.IdImages.ToSdImage();
if (managed.MaskImage != null)
_maskImage = managed.MaskImage.ToSdImage(true);
else if (managed.InitImage != null)
{
// DarthAffe 16.08.2025: Mask needs to be a 1 channel all max value image when it's not used - I really don't like this concept as it adds unnecessary allocations, but that's how it is :(
uint maskImageByteSize = _initImage.width * _initImage.height;
_maskImage = new Native.Types.sd_image_t
{
width = _initImage.width,
height = _initImage.height,
channel = 1,
data = (byte*)NativeMemory.Alloc(maskImageByteSize)
};
new Span<byte>(_maskImage.data, (int)maskImageByteSize).Fill(byte.MaxValue);
}
Native.Types.sd_pm_params_t photoMakerParams = new()
{
id_images = _pmIdImages,
id_images_count = managed.PhotoMaker.IdImages?.Length ?? 0,
id_embed_path = AnsiStringMarshaller.ConvertToUnmanaged(managed.PhotoMaker.IdEmbedPath),
style_strength = managed.PhotoMaker.StyleStrength
};
Native.Types.sd_tiling_params_t tilingParams = new()
{
enabled = (sbyte)(managed.VaeTiling.IsEnabled ? 1 : 0),
tile_size_x = managed.VaeTiling.TileSizeX,
tile_size_y = managed.VaeTiling.TileSizeY,
target_overlap = managed.VaeTiling.TargetOverlap,
rel_size_x = managed.VaeTiling.RelSizeX,
rel_size_y = managed.VaeTiling.RelSizeY
};
Native.Types.sd_easycache_params_t easyCache = new()
{
enabled = (sbyte)(managed.EasyCache.IsEnabled ? 1 : 0),
reuse_threshold = managed.EasyCache.ReuseThreshold,
start_percent = managed.EasyCache.StartPercent,
end_percent = managed.EasyCache.EndPercent,
};
_imgGenParams = new Native.Types.sd_img_gen_params_t
{
prompt = AnsiStringMarshaller.ConvertToUnmanaged(managed.Prompt),
negative_prompt = AnsiStringMarshaller.ConvertToUnmanaged(managed.NegativePrompt),
clip_skip = managed.ClipSkip,
sample_params = _sampleParameterMarshaller.ToUnmanaged(),
init_image = _initImage,
ref_images = _refImages,
ref_images_count = managed.RefImages?.Length ?? 0,
auto_resize_ref_image = (sbyte)(managed.AutoResizeRefImage ? 1 : 0),
mask_image = _maskImage,
width = managed.Width,
height = managed.Height,
strength = managed.Strength,
seed = managed.Seed,
batch_count = 1,
control_image = _controlNetImage,
control_strength = managed.ControlNet.Strength,
pm_params = photoMakerParams,
vae_tiling_params = tilingParams,
easycache = easyCache
};
}
public Native.Types.sd_img_gen_params_t ToUnmanaged() => _imgGenParams;
public void Free()
{
AnsiStringMarshaller.Free(_imgGenParams.prompt);
AnsiStringMarshaller.Free(_imgGenParams.negative_prompt);
AnsiStringMarshaller.Free(_imgGenParams.pm_params.id_embed_path);
_initImage.Free();
_maskImage.Free();
_controlNetImage.Free();
if (_refImages != null)
ImageHelper.Free(_refImages, _imgGenParams.ref_images_count);
if (_pmIdImages != null)
ImageHelper.Free(_pmIdImages, _imgGenParams.pm_params.id_images_count);
_sampleParameterMarshaller.Free();
}
}
internal ref struct ImageGenerationParameterMarshallerRef()
{
private ImageGenerationParameterMarshallerIn _inMarshaller = new();
private ImageGenerationParameter? _parameter;
public void FromManaged(ImageGenerationParameter managed) => _inMarshaller.FromManaged(managed);
public Native.Types.sd_img_gen_params_t ToUnmanaged() => _inMarshaller.ToUnmanaged();
public void FromUnmanaged(Native.Types.sd_img_gen_params_t unmanaged) => _parameter = ConvertToManaged(unmanaged);
public ImageGenerationParameter ToManaged() => _parameter!;
public void Free() => _inMarshaller.Free();
}
}

View File

@ -0,0 +1,24 @@
using HPPH;
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
[CustomMarshaller(typeof(IImage), MarshalMode.ManagedToUnmanagedIn, typeof(ImageMarshallerIn))]
[CustomMarshaller(typeof(Image<ColorRGB>), MarshalMode.ManagedToUnmanagedOut, typeof(ImageMarshaller))]
internal static class ImageMarshaller
{
public static Image<ColorRGB> ConvertToManaged(Native.Types.sd_image_t unmanaged) => unmanaged.ToImage();
public static void Free(Native.Types.sd_image_t unmanaged) => unmanaged.Free();
internal ref struct ImageMarshallerIn
{
private Native.Types.sd_image_t _image;
public void FromManaged(IImage managed) => _image = managed.ToSdImage();
public Native.Types.sd_image_t ToUnmanaged() => _image;
public void Free() => _image.Free();
}
}

View File

@ -0,0 +1,113 @@
// ReSharper disable MemberCanBeMadeStatic.Global
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
[CustomMarshaller(typeof(SampleParameter), MarshalMode.ManagedToUnmanagedIn, typeof(SampleParameterMarshallerIn))]
[CustomMarshaller(typeof(SampleParameter), MarshalMode.ManagedToUnmanagedOut, typeof(SampleParameterMarshaller))]
[CustomMarshaller(typeof(SampleParameter), MarshalMode.ManagedToUnmanagedRef, typeof(SampleParameterMarshallerRef))]
internal static class SampleParameterMarshaller
{
public static unsafe SampleParameter ConvertToManaged(Native.Types.sd_sample_params_t unmanaged)
{
SampleParameter parameter = new()
{
Guidance =
{
TxtCfg = unmanaged.guidance.txt_cfg,
ImgCfg = unmanaged.guidance.img_cfg,
MinCfg = unmanaged.guidance.min_cfg,
DistilledGuidance = unmanaged.guidance.distilled_guidance,
Slg =
{
Layers = new int[unmanaged.guidance.slg.layer_count],
SkipLayerStart = unmanaged.guidance.slg.layer_start,
SkipLayerEnd = unmanaged.guidance.slg.layer_end,
Scale = unmanaged.guidance.slg.scale
}
},
Scheduler = unmanaged.scheduler,
SampleMethod = unmanaged.sample_method,
SampleSteps = unmanaged.sample_steps,
Eta = unmanaged.eta,
ShiftedTimestep = unmanaged.shifted_timestep
};
if (unmanaged.guidance.slg.layers != null)
new Span<int>(unmanaged.guidance.slg.layers, (int)unmanaged.guidance.slg.layer_count).CopyTo(parameter.Guidance.Slg.Layers);
return parameter;
}
public static unsafe void Free(Native.Types.sd_sample_params_t unmanaged)
{
if (unmanaged.guidance.slg.layers != null)
NativeMemory.Free(unmanaged.guidance.slg.layers);
}
internal unsafe ref struct SampleParameterMarshallerIn
{
private Native.Types.sd_sample_params_t _sampleParams;
private int* _slgLayers;
public void FromManaged(SampleParameter managed)
{
_slgLayers = (int*)NativeMemory.Alloc((nuint)managed.Guidance.Slg.Layers.Length, (nuint)Marshal.SizeOf<int>());
managed.Guidance.Slg.Layers.AsSpan().CopyTo(new Span<int>(_slgLayers, managed.Guidance.Slg.Layers.Length));
Native.Types.sd_slg_params_t slg = new()
{
layers = _slgLayers,
layer_count = (uint)managed.Guidance.Slg.Layers.Length,
layer_start = managed.Guidance.Slg.SkipLayerStart,
layer_end = managed.Guidance.Slg.SkipLayerEnd,
scale = managed.Guidance.Slg.Scale,
};
Native.Types.sd_guidance_params_t guidance = new()
{
txt_cfg = managed.Guidance.TxtCfg,
img_cfg = managed.Guidance.ImgCfg,
min_cfg = managed.Guidance.MinCfg,
distilled_guidance = managed.Guidance.DistilledGuidance,
slg = slg
};
_sampleParams = new Native.Types.sd_sample_params_t
{
guidance = guidance,
scheduler = managed.Scheduler,
sample_method = managed.SampleMethod,
sample_steps = managed.SampleSteps,
eta = managed.Eta,
shifted_timestep = managed.ShiftedTimestep
};
}
public Native.Types.sd_sample_params_t ToUnmanaged() => _sampleParams;
public void Free()
{
if (_slgLayers != null)
NativeMemory.Free(_slgLayers);
}
}
internal ref struct SampleParameterMarshallerRef()
{
private SampleParameterMarshallerIn _inMarshaller = new();
private SampleParameter? _parameter;
public void FromManaged(SampleParameter managed) => _inMarshaller.FromManaged(managed);
public Native.Types.sd_sample_params_t ToUnmanaged() => _inMarshaller.ToUnmanaged();
public void FromUnmanaged(Native.Types.sd_sample_params_t unmanaged) => _parameter = ConvertToManaged(unmanaged);
public SampleParameter ToManaged() => _parameter ?? throw new NullReferenceException($"{nameof(FromUnmanaged)} needs to be called before ToManaged.");
public void Free() => _inMarshaller.Free();
}
}

View File

@ -0,0 +1,140 @@
// ReSharper disable MemberCanBeMadeStatic.Global
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
[CustomMarshaller(typeof(VideoGenerationParameter), MarshalMode.ManagedToUnmanagedIn, typeof(VideoGenerationParameterMarshallerIn))]
[CustomMarshaller(typeof(VideoGenerationParameter), MarshalMode.ManagedToUnmanagedOut, typeof(VideoGenerationParameterMarshaller))]
[CustomMarshaller(typeof(VideoGenerationParameter), MarshalMode.ManagedToUnmanagedRef, typeof(VideoGenerationParameterMarshallerRef))]
internal static class VideoGenerationParameterMarshaller
{
public static unsafe VideoGenerationParameter ConvertToManaged(Native.Types.sd_vid_gen_params_t unmanaged)
{
VideoGenerationParameter parameter = new()
{
Prompt = AnsiStringMarshaller.ConvertToManaged(unmanaged.prompt) ?? string.Empty,
NegativePrompt = AnsiStringMarshaller.ConvertToManaged(unmanaged.negative_prompt) ?? string.Empty,
ClipSkip = unmanaged.clip_skip,
InitImage = unmanaged.init_image.data == null ? null : unmanaged.init_image.ToImage(),
EndImage = unmanaged.end_image.data == null ? null : unmanaged.end_image.ToImage(),
ControlFrames = unmanaged.control_frames == null ? null : ImageHelper.ToImageArrayIFace(unmanaged.control_frames, unmanaged.control_frames_size),
Width = unmanaged.width,
Height = unmanaged.height,
SampleParameter = SampleParameterMarshaller.ConvertToManaged(unmanaged.sample_params),
HighNoiseSampleParameter = SampleParameterMarshaller.ConvertToManaged(unmanaged.high_noise_sample_params),
MoeBoundry = unmanaged.moe_boundary,
Strength = unmanaged.strength,
Seed = unmanaged.seed,
FrameCount = unmanaged.video_frames,
VaceStrength = unmanaged.vace_strength,
EasyCache =
{
IsEnabled = unmanaged.easycache.enabled == 1,
ReuseThreshold = unmanaged.easycache.reuse_threshold,
StartPercent = unmanaged.easycache.start_percent,
EndPercent = unmanaged.easycache.end_percent
}
};
return parameter;
}
public static unsafe void Free(Native.Types.sd_vid_gen_params_t unmanaged)
{
AnsiStringMarshaller.Free(unmanaged.prompt);
AnsiStringMarshaller.Free(unmanaged.negative_prompt);
unmanaged.init_image.Free();
unmanaged.end_image.Free();
if (unmanaged.control_frames != null)
ImageHelper.Free(unmanaged.control_frames, unmanaged.control_frames_size);
SampleParameterMarshaller.Free(unmanaged.sample_params);
SampleParameterMarshaller.Free(unmanaged.high_noise_sample_params);
}
internal unsafe ref struct VideoGenerationParameterMarshallerIn
{
private SampleParameterMarshaller.SampleParameterMarshallerIn _sampleParameterMarshaller = new();
private SampleParameterMarshaller.SampleParameterMarshallerIn _highNoiseSampleParameterMarshaller = new();
private Native.Types.sd_vid_gen_params_t _vidGenParams;
private Native.Types.sd_image_t _initImage;
private Native.Types.sd_image_t _endImage;
private Native.Types.sd_image_t* _controlFrames;
public VideoGenerationParameterMarshallerIn() { }
public void FromManaged(VideoGenerationParameter managed)
{
_sampleParameterMarshaller.FromManaged(managed.SampleParameter);
_highNoiseSampleParameterMarshaller.FromManaged(managed.HighNoiseSampleParameter);
_initImage = managed.InitImage?.ToSdImage() ?? new Native.Types.sd_image_t();
_endImage = managed.EndImage?.ToSdImage() ?? new Native.Types.sd_image_t();
_controlFrames = managed.ControlFrames == null ? null : managed.ControlFrames.ToSdImage();
Native.Types.sd_easycache_params_t easyCache = new()
{
enabled = (sbyte)(managed.EasyCache.IsEnabled ? 1 : 0),
reuse_threshold = managed.EasyCache.ReuseThreshold,
start_percent = managed.EasyCache.StartPercent,
end_percent = managed.EasyCache.EndPercent,
};
_vidGenParams = new Native.Types.sd_vid_gen_params_t
{
prompt = AnsiStringMarshaller.ConvertToUnmanaged(managed.Prompt),
negative_prompt = AnsiStringMarshaller.ConvertToUnmanaged(managed.NegativePrompt),
clip_skip = managed.ClipSkip,
init_image = _initImage,
end_image = _endImage,
control_frames = _controlFrames,
control_frames_size = managed.ControlFrames?.Length ?? 0,
width = managed.Width,
height = managed.Height,
sample_params = _sampleParameterMarshaller.ToUnmanaged(),
high_noise_sample_params = _highNoiseSampleParameterMarshaller.ToUnmanaged(),
moe_boundary = managed.MoeBoundry,
strength = managed.Strength,
seed = managed.Seed,
video_frames = managed.FrameCount,
vace_strength = managed.VaceStrength,
easycache = easyCache,
};
}
public Native.Types.sd_vid_gen_params_t ToUnmanaged() => _vidGenParams;
public void Free()
{
AnsiStringMarshaller.Free(_vidGenParams.prompt);
AnsiStringMarshaller.Free(_vidGenParams.negative_prompt);
_initImage.Free();
_endImage.Free();
if (_controlFrames != null)
ImageHelper.Free(_controlFrames, _vidGenParams.control_frames_size);
_sampleParameterMarshaller.Free();
_highNoiseSampleParameterMarshaller.Free();
}
}
internal ref struct VideoGenerationParameterMarshallerRef()
{
private VideoGenerationParameterMarshallerIn _inMarshaller = new();
private VideoGenerationParameter? _parameter;
public void FromManaged(VideoGenerationParameter managed) => _inMarshaller.FromManaged(managed);
public Native.Types.sd_vid_gen_params_t ToUnmanaged() => _inMarshaller.ToUnmanaged();
public void FromUnmanaged(Native.Types.sd_vid_gen_params_t unmanaged) => _parameter = ConvertToManaged(unmanaged);
public VideoGenerationParameter ToManaged() => _parameter!;
public void Free() => _inMarshaller.Free();
}
}

View File

@ -26,7 +26,19 @@ internal static partial class Native
#region Methods
private static nint ResolveDllImport(string libraryname, Assembly assembly, DllImportSearchPath? searchpath)
internal static bool LoadNativeLibrary(string libraryPath)
{
if (_loadedLibraryHandle != nint.Zero) return true;
if (NativeLibrary.TryLoad(libraryPath, out nint handle))
{
_loadedLibraryHandle = handle;
return true;
}
return false;
}
private static nint ResolveDllImport(string libraryname, Assembly _, DllImportSearchPath? __)
{
if (libraryname != LIB_NAME) return nint.Zero;
if (_loadedLibraryHandle != nint.Zero) return _loadedLibraryHandle;

View File

@ -1,17 +1,35 @@
#pragma warning disable CS0169 // Field is never used
#pragma warning disable IDE1006
#pragma warning disable IDE1006
// ReSharper disable InconsistentNaming
// ReSharper disable ArrangeTypeMemberModifiers
// ReSharper disable UseSymbolAlias
using HPPH;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace StableDiffusion.NET;
using int32_t = int;
using int64_t = long;
using rng_type_t = RngType;
using sample_method_t = Sampler;
using schedule_t = Schedule;
using sd_type_t = Quantization;
using scheduler_t = Scheduler;
using prediction_t = Prediction;
using sd_ctx_params_t = DiffusionModelParameter;
using sd_ctx_t = Native.Types.sd_ctx_t;
using sd_image_t = Native.Types.sd_image_t;
using sd_sample_params_t = SampleParameter;
using sd_img_gen_params_t = ImageGenerationParameter;
using sd_log_level_t = LogLevel;
using sd_type_t = Quantization;
using sd_vid_gen_params_t = VideoGenerationParameter;
using lora_apply_mode_t = LoraApplyMode;
using preview_t = Preview;
using sd_easycache_params_t = Native.Types.sd_easycache_params_t;
using size_t = nuint;
using uint32_t = uint;
using uint8_t = byte;
using upscaler_ctx_t = Native.Types.upscaler_ctx_t;
internal unsafe partial class Native
{
@ -21,137 +39,179 @@ internal unsafe partial class Native
#endregion
internal static class Types
{
[StructLayout(LayoutKind.Sequential)]
public struct sd_tiling_params_t
{
public sbyte enabled;
public int tile_size_x;
public int tile_size_y;
public float target_overlap;
public float rel_size_x;
public float rel_size_y;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_ctx_params_t
{
public byte* model_path;
public byte* clip_l_path;
public byte* clip_g_path;
public byte* clip_vision_path;
public byte* t5xxl_path;
public byte* llm_path;
public byte* llm_vision_path;
public byte* diffusion_model_path;
public byte* high_noise_diffusion_model_path;
public byte* vae_path;
public byte* taesd_path;
public byte* control_net_path;
public byte* lora_model_dir;
public byte* embedding_dir;
public byte* photo_maker_path;
public byte* tensor_type_rules;
public sbyte vae_decode_only;
public sbyte free_params_immediately;
public int n_threads;
public sd_type_t wtype;
public rng_type_t rng_type;
public rng_type_t sampler_rng_type;
public prediction_t prediction;
public lora_apply_mode_t lora_apply_mode;
public sbyte offload_params_to_cpu;
public sbyte keep_clip_on_cpu;
public sbyte keep_control_net_on_cpu;
public sbyte keep_vae_on_cpu;
public sbyte diffusion_flash_attn;
public sbyte tae_preview_only;
public sbyte diffusion_conv_direct;
public sbyte vae_conv_direct;
public sbyte force_sdxl_vae_conv_scale;
public sbyte chroma_use_dit_mask;
public sbyte chroma_use_t5_mask;
public int chroma_t5_mask_pad;
public float flow_shift;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_image_t
{
public uint32_t width;
public uint32_t height;
public uint32_t channel;
public uint8_t* data;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_slg_params_t
{
public int* layers;
public size_t layer_count;
public float layer_start;
public float layer_end;
public float scale;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_guidance_params_t
{
public float txt_cfg;
public float img_cfg;
public float min_cfg;
public float distilled_guidance;
public sd_slg_params_t slg;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_sample_params_t
{
public sd_guidance_params_t guidance;
public scheduler_t scheduler;
public sample_method_t sample_method;
public int sample_steps;
public float eta;
public int shifted_timestep;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_pm_params_t
{
public sd_image_t* id_images;
public int id_images_count;
public byte* id_embed_path;
public float style_strength;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_easycache_params_t
{
public sbyte enabled;
public float reuse_threshold;
public float start_percent;
public float end_percent;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_img_gen_params_t
{
public byte* prompt;
public byte* negative_prompt;
public int clip_skip;
public sd_image_t init_image;
public sd_image_t* ref_images;
public int ref_images_count;
public sbyte auto_resize_ref_image;
public sbyte increase_ref_index;
public sd_image_t mask_image;
public int width;
public int height;
public sd_sample_params_t sample_params;
public float strength;
public int64_t seed;
public int batch_count;
public sd_image_t control_image;
public float control_strength;
public sd_pm_params_t pm_params;
public sd_tiling_params_t vae_tiling_params;
public sd_easycache_params_t easycache;
}
[StructLayout(LayoutKind.Sequential)]
internal struct sd_vid_gen_params_t
{
public byte* prompt;
public byte* negative_prompt;
public int clip_skip;
public sd_image_t init_image;
public sd_image_t end_image;
public sd_image_t* control_frames;
public int control_frames_size;
public int width;
public int height;
public sd_sample_params_t sample_params;
public sd_sample_params_t high_noise_sample_params;
public float moe_boundary;
public float strength;
public int64_t seed;
public int video_frames;
public float vace_strength;
public sd_easycache_params_t easycache;
}
internal struct sd_ctx_t;
internal struct upscaler_ctx_t;
}
#region Delegates
internal delegate void sd_log_cb_t(sd_log_level_t level, [MarshalAs(UnmanagedType.LPStr)] string text, void* data);
internal delegate void sd_progress_cb_t(int step, int steps, float time, void* data);
internal delegate void sd_preview_cb_t(int step, int frame_count, sd_image_t* frames, bool is_noisy, void* data);
#endregion
#region DLL-Import
internal struct sd_ctx_t;
internal struct upscaler_ctx_t;
[StructLayout(LayoutKind.Sequential)]
internal struct sd_image_t
{
internal uint width;
internal uint height;
internal uint channel;
internal byte* data;
}
[LibraryImport(LIB_NAME, EntryPoint = "get_num_physical_cores")]
internal static partial int get_num_physical_cores();
[LibraryImport(LIB_NAME, EntryPoint = "sd_get_system_info")]
internal static partial void* sd_get_system_info();
[LibraryImport(LIB_NAME, EntryPoint = "new_sd_ctx")]
internal static partial sd_ctx_t* new_sd_ctx([MarshalAs(UnmanagedType.LPStr)] string model_path,
[MarshalAs(UnmanagedType.LPStr)] string vae_path,
[MarshalAs(UnmanagedType.LPStr)] string taesd_path,
[MarshalAs(UnmanagedType.LPStr)] string control_net_path_c_str,
[MarshalAs(UnmanagedType.LPStr)] string lora_model_dir,
[MarshalAs(UnmanagedType.LPStr)] string embed_dir_c_str,
[MarshalAs(UnmanagedType.LPStr)] string stacked_id_embed_dir_c_str,
[MarshalAs(UnmanagedType.I1)] bool vae_decode_only,
[MarshalAs(UnmanagedType.I1)] bool vae_tiling,
[MarshalAs(UnmanagedType.I1)] bool free_params_immediately,
int n_threads,
sd_type_t wtype,
rng_type_t rng_type,
schedule_t s,
[MarshalAs(UnmanagedType.I1)] bool keep_clip_on_cpu,
[MarshalAs(UnmanagedType.I1)] bool keep_control_net_cpu,
[MarshalAs(UnmanagedType.I1)] bool keep_vae_on_cpu);
[LibraryImport(LIB_NAME, EntryPoint = "free_sd_ctx")]
internal static partial void free_sd_ctx(sd_ctx_t* sd_ctx);
[LibraryImport(LIB_NAME, EntryPoint = "txt2img")]
internal static partial sd_image_t* txt2img(sd_ctx_t* sd_ctx,
[MarshalAs(UnmanagedType.LPStr)] string prompt,
[MarshalAs(UnmanagedType.LPStr)] string negative_prompt,
int clip_skip,
float cfg_scale,
int width,
int height,
sample_method_t sample_method,
int sample_steps,
long seed,
int batch_count,
sd_image_t* control_cond,
float control_strength,
float style_strength,
[MarshalAs(UnmanagedType.I1)] bool normalize_input,
[MarshalAs(UnmanagedType.LPStr)] string input_id_images_path);
[LibraryImport(LIB_NAME, EntryPoint = "img2img")]
internal static partial sd_image_t* img2img(sd_ctx_t* sd_ctx,
sd_image_t init_image,
[MarshalAs(UnmanagedType.LPStr)] string prompt,
[MarshalAs(UnmanagedType.LPStr)] string negative_prompt,
int clip_skip,
float cfg_scale,
int width,
int height,
sample_method_t sample_method,
int sample_steps,
float strength,
long seed,
int batch_count,
sd_image_t* control_cond,
float control_strength,
float style_strength,
[MarshalAs(UnmanagedType.I1)] bool normalize_input,
[MarshalAs(UnmanagedType.LPStr)] string input_id_images_path);
[LibraryImport(LIB_NAME, EntryPoint = "img2vid")]
internal static partial 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,
sample_method_t sample_method,
int sample_steps,
float strength,
long seed);
[LibraryImport(LIB_NAME, EntryPoint = "new_upscaler_ctx")]
internal static partial upscaler_ctx_t* new_upscaler_ctx([MarshalAs(UnmanagedType.LPStr)] string esrgan_path,
int n_threads,
sd_type_t wtype);
[LibraryImport(LIB_NAME, EntryPoint = "free_upscaler_ctx")]
internal static partial void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
[LibraryImport(LIB_NAME, EntryPoint = "upscale")]
internal static partial sd_image_t upscale(upscaler_ctx_t* upscaler_ctx,
sd_image_t input_image,
int upscale_factor);
[LibraryImport(LIB_NAME, EntryPoint = "convert")]
internal static partial void convert([MarshalAs(UnmanagedType.LPStr)] string input_path,
[MarshalAs(UnmanagedType.LPStr)] string vae_path,
[MarshalAs(UnmanagedType.LPStr)] string output_path,
sd_type_t output_type);
[LibraryImport(LIB_NAME, EntryPoint = "preprocess_canny")]
internal static partial byte* preprocess_canny(byte* img,
int width,
int height,
float high_threshold,
float low_threshold,
float weak,
float strong,
[MarshalAs(UnmanagedType.I1)] bool inverse);
#region Methods
[LibraryImport(LIB_NAME, EntryPoint = "sd_set_log_callback")]
internal static partial void sd_set_log_callback(sd_log_cb_t sd_log_cb, void* data);
@ -159,5 +219,160 @@ internal unsafe partial class Native
[LibraryImport(LIB_NAME, EntryPoint = "sd_set_progress_callback")]
internal static partial void sd_set_progress_callback(sd_progress_cb_t cb, void* data);
[LibraryImport(LIB_NAME, EntryPoint = "sd_set_preview_callback")]
internal static partial void sd_set_preview_callback(sd_preview_cb_t? cb, preview_t mode, int interval, [MarshalAs(UnmanagedType.I1)] bool denoised, [MarshalAs(UnmanagedType.I1)] bool noisy, void* data);
[LibraryImport(LIB_NAME, EntryPoint = "sd_get_num_physical_cores")]
internal static partial int32_t sd_get_num_physical_cores();
[LibraryImport(LIB_NAME, EntryPoint = "sd_get_system_info")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_get_system_info();
//
[LibraryImport(LIB_NAME, EntryPoint = "sd_type_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_type_name(sd_type_t type);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_sd_type")]
internal static partial sd_type_t str_to_sd_type([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_rng_type_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_rng_type_name(rng_type_t rng_type);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_rng_type")]
internal static partial rng_type_t str_to_rng_type([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_sample_method_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_sample_method_name(sample_method_t sample_method);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_sample_method")]
internal static partial sample_method_t str_to_sample_method([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_scheduler_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_scheduler_name(scheduler_t schedule);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_scheduler")]
internal static partial scheduler_t str_to_scheduler([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_prediction_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_prediction_name(prediction_t prediction);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_prediction")]
internal static partial prediction_t str_to_prediction([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_preview_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_preview_name(preview_t preview);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_preview")]
internal static partial preview_t str_to_preview([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_lora_apply_mode_name")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_lora_apply_mode_name(lora_apply_mode_t mode);
[LibraryImport(LIB_NAME, EntryPoint = "str_to_lora_apply_mode")]
internal static partial lora_apply_mode_t str_to_lora_apply_mode([MarshalAs(UnmanagedType.LPStr)] string str);
[LibraryImport(LIB_NAME, EntryPoint = "sd_easycache_params_init")]
internal static partial void sd_easycache_params_init(ref sd_easycache_params_t easycache_params);
//
[LibraryImport(LIB_NAME, EntryPoint = "sd_ctx_params_init")]
internal static partial void sd_ctx_params_init([MarshalUsing(typeof(DiffusionModelParameterMarshaller))] ref sd_ctx_params_t sd_ctx_params);
[LibraryImport(LIB_NAME, EntryPoint = "sd_ctx_params_to_str")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_ctx_params_to_str([MarshalUsing(typeof(DiffusionModelParameterMarshaller))] in sd_ctx_params_t sd_ctx_params);
//
[LibraryImport(LIB_NAME, EntryPoint = "new_sd_ctx")]
internal static partial sd_ctx_t* new_sd_ctx([MarshalUsing(typeof(DiffusionModelParameterMarshaller))] in sd_ctx_params_t sd_ctx_params);
[LibraryImport(LIB_NAME, EntryPoint = "free_sd_ctx")]
internal static partial void free_sd_ctx(sd_ctx_t* sd_ctx);
//
[LibraryImport(LIB_NAME, EntryPoint = "sd_sample_params_init")]
internal static partial void sd_sample_params_init([MarshalUsing(typeof(SampleParameterMarshaller))] ref sd_sample_params_t sample_params);
[LibraryImport(LIB_NAME, EntryPoint = "sd_sample_params_to_str")]
internal static partial char* sd_sample_params_to_str([MarshalUsing(typeof(SampleParameterMarshaller))] in sd_sample_params_t sample_params);
//
[LibraryImport(LIB_NAME, EntryPoint = "sd_img_gen_params_init")]
internal static partial void sd_img_gen_params_init([MarshalUsing(typeof(ImageGenerationParameterMarshaller))] ref sd_img_gen_params_t sd_img_gen_params);
[LibraryImport(LIB_NAME, EntryPoint = "sd_img_gen_params_to_str")]
[return: MarshalAs(UnmanagedType.LPStr)]
internal static partial string sd_img_gen_params_to_str([MarshalUsing(typeof(ImageGenerationParameterMarshaller))] in sd_img_gen_params_t sd_img_gen_params);
[LibraryImport(LIB_NAME, EntryPoint = "sd_get_default_sample_method")]
internal static partial sample_method_t sd_get_default_sample_method(sd_ctx_t* sd_ctx);
[LibraryImport(LIB_NAME, EntryPoint = "sd_get_default_scheduler")]
internal static partial scheduler_t sd_get_default_scheduler(sd_ctx_t* sd_ctx);
[LibraryImport(LIB_NAME, EntryPoint = "generate_image")]
internal static partial sd_image_t* generate_image(sd_ctx_t* sd_ctx, [MarshalUsing(typeof(ImageGenerationParameterMarshaller))] in sd_img_gen_params_t sd_img_gen_params);
//
[LibraryImport(LIB_NAME, EntryPoint = "sd_vid_gen_params_init")]
internal static partial void sd_vid_gen_params_init([MarshalUsing(typeof(VideoGenerationParameterMarshaller))] ref sd_vid_gen_params_t sd_vid_gen_params);
[LibraryImport(LIB_NAME, EntryPoint = "generate_video")]
internal static partial sd_image_t* generate_video(sd_ctx_t* sd_ctx, [MarshalUsing(typeof(VideoGenerationParameterMarshaller))] in sd_vid_gen_params_t sd_vid_gen_params, out int num_frames_out);
//
[LibraryImport(LIB_NAME, EntryPoint = "new_upscaler_ctx")]
internal static partial upscaler_ctx_t* new_upscaler_ctx([MarshalAs(UnmanagedType.LPStr)] string esrgan_path,
[MarshalAs(UnmanagedType.I1)] bool offload_params_to_cpu,
[MarshalAs(UnmanagedType.I1)] bool direct,
int n_threads);
[LibraryImport(LIB_NAME, EntryPoint = "free_upscaler_ctx")]
internal static partial void free_upscaler_ctx(upscaler_ctx_t* upscaler_ctx);
//
[LibraryImport(LIB_NAME, EntryPoint = "upscale")]
[return: MarshalUsing(typeof(ImageMarshaller))]
internal static partial Image<ColorRGB> upscale(upscaler_ctx_t* upscaler_ctx, [MarshalUsing(typeof(ImageMarshaller))] IImage input_image, uint32_t upscale_factor);
[LibraryImport(LIB_NAME, EntryPoint = "get_upscale_factor")]
internal static partial int get_upscale_factor(upscaler_ctx_t* upscaler_ctx);
//
[LibraryImport(LIB_NAME, EntryPoint = "convert")]
[return: MarshalAs(UnmanagedType.I1)]
internal static partial bool convert([MarshalAs(UnmanagedType.LPStr)] string input_path,
[MarshalAs(UnmanagedType.LPStr)] string vae_path,
[MarshalAs(UnmanagedType.LPStr)] string output_path,
sd_type_t output_type,
[MarshalAs(UnmanagedType.LPStr)] string tensor_type_rules);
//
[LibraryImport(LIB_NAME, EntryPoint = "preprocess_canny")]
[return: MarshalAs(UnmanagedType.I1)]
internal static partial bool preprocess_canny(sd_image_t image,
float high_threshold,
float low_threshold,
float weak,
float strong,
[MarshalAs(UnmanagedType.I1)] bool inverse);
#endregion
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -55,6 +55,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="HPPH" Version="1.0.0" />
<PackageReference Include="JetBrains.Annotations" Version="2025.2.0" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,19 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=attributes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=backends/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=builder/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=enums/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=eventargs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=native/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cbuilder/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cbuilder_005Cextensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cbuilder_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cextensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cparameter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cparameter_005Cextensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cparameter_005Cinterfaces/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=native/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=native_005Cmarshaller/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,121 @@
using HPPH;
using JetBrains.Annotations;
using System;
namespace StableDiffusion.NET;
[PublicAPI]
public static unsafe class StableDiffusionCpp
{
#region Properties & Fields
// ReSharper disable NotAccessedField.Local - They are important, the delegate can be collected if it's not stored!
private static Native.sd_log_cb_t? _logCallback;
private static Native.sd_progress_cb_t? _progressCallback;
private static Native.sd_preview_cb_t? _previewCallback;
// ReSharper restore NotAccessedField.Local
#endregion
#region Events
public static event EventHandler<StableDiffusionLogEventArgs>? Log;
public static event EventHandler<StableDiffusionProgressEventArgs>? Progress;
public static event EventHandler<StableDiffusionPreviewEventArgs>? Preview;
#endregion
#region Methods
public static bool LoadNativeLibrary(string libraryPath) => Native.LoadNativeLibrary(libraryPath);
public static void InitializeEvents()
{
Native.sd_set_log_callback(_logCallback = OnNativeLog, null);
Native.sd_set_progress_callback(_progressCallback = OnNativeProgress, null);
}
public static void EnablePreview(Preview mode, int interval, bool denoised, bool noisy)
{
ArgumentOutOfRangeException.ThrowIfNegative(interval);
if (mode == NET.Preview.None)
_previewCallback = null;
else if (_previewCallback == null)
_previewCallback = OnPreview;
Native.sd_set_preview_callback(_previewCallback, mode, interval, denoised, noisy, null);
}
public static void Convert(string modelPath, string vaePath, Quantization quantization, string outputPath, string tensorTypeRules = "")
{
ArgumentException.ThrowIfNullOrWhiteSpace(nameof(modelPath));
ArgumentException.ThrowIfNullOrWhiteSpace(nameof(outputPath));
ArgumentNullException.ThrowIfNull(vaePath);
if (!Enum.IsDefined(quantization)) throw new ArgumentOutOfRangeException(nameof(quantization));
Native.convert(modelPath, vaePath, outputPath, quantization, tensorTypeRules);
}
public static string GetSystemInfo() => Native.sd_get_system_info();
public static int GetNumPhysicalCores() => Native.sd_get_num_physical_cores();
public static Image<ColorRGB> PreprocessCanny(CannyParameter parameter)
{
parameter.Validate();
IImage<ColorRGB> controlImage = parameter.Image as IImage<ColorRGB> ?? parameter.Image!.ConvertTo<ColorRGB>();
Native.Types.sd_image_t sdImage = controlImage.ToSdImage();
try
{
bool result = Native.preprocess_canny(sdImage,
parameter.HighThreshold,
parameter.LowThreshold,
parameter.Weak,
parameter.Strong,
parameter.Inverse);
return sdImage.ToImage();
}
finally
{
sdImage.Free();
}
}
private static void OnNativeLog(LogLevel level, string text, void* data)
{
try
{
Log?.Invoke(null, new StableDiffusionLogEventArgs(level, text));
}
catch { /**/ }
}
private static void OnNativeProgress(int step, int steps, float time, void* data)
{
try
{
Progress?.Invoke(null, new StableDiffusionProgressEventArgs(step, steps, time));
}
catch { /**/ }
}
private static void OnPreview(int step, int frameCount, Native.Types.sd_image_t* frames, bool isNoisy, void* data)
{
try
{
if (frameCount <= 0 || frames == null) return;
Image<ColorRGB> image = ImageHelper.GetImage(frames, 0);
Preview?.Invoke(null, new StableDiffusionPreviewEventArgs(step, isNoisy, image));
}
catch { /**/ }
}
#endregion
}

View File

@ -1,63 +0,0 @@
using JetBrains.Annotations;
using System;
using System.Runtime.InteropServices;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed unsafe class StableDiffusionImage : IDisposable
{
#region Properties & Fields
private bool _disposed;
internal readonly Native.sd_image_t* Image;
public int Width { get; }
public int Height { get; }
public int Bpp { get; }
public int Stride { get; }
public ReadOnlySpan<byte> Data
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return new ReadOnlySpan<byte>(Image->data, Width * Height * Bpp);
}
}
#endregion
#region Constructors
internal unsafe StableDiffusionImage(Native.sd_image_t* image)
{
this.Image = image;
Width = (int)image->width;
Height = (int)image->height;
Bpp = (int)image->channel;
Stride = Width * Bpp;
}
~StableDiffusionImage() => Dispose();
#endregion
#region Methods
public void Dispose()
{
if (_disposed) return;
Marshal.FreeHGlobal((nint)Image->data);
Marshal.FreeHGlobal((nint)Image);
GC.SuppressFinalize(this);
_disposed = true;
}
#endregion
}

View File

@ -1,393 +0,0 @@
using System;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed unsafe class StableDiffusionModel : IDisposable
{
#region Properties & Fields
// ReSharper disable NotAccessedField.Local - They are important, the delegate can be collected if it's not stored!
private static readonly Native.sd_log_cb_t LOG_CALLBACK;
private static readonly Native.sd_progress_cb_t PROGRESS_CALLBACK;
// ReSharper restore NotAccessedField.Local
private bool _disposed;
private readonly string _modelPath;
private readonly ModelParameter _parameter;
private readonly UpscalerModelParameter? _upscalerParameter;
private Native.sd_ctx_t* _ctx;
private Native.upscaler_ctx_t* _upscalerCtx;
#endregion
#region Events
public static event EventHandler<StableDiffusionLogEventArgs>? Log;
public static event EventHandler<StableDiffusionProgressEventArgs>? Progress;
#endregion
#region Constructors
static StableDiffusionModel()
{
Native.sd_set_log_callback(LOG_CALLBACK = OnNativeLog, null);
Native.sd_set_progress_callback(PROGRESS_CALLBACK = OnNativeProgress, null);
}
public StableDiffusionModel(string modelPath, ModelParameter parameter, UpscalerModelParameter? upscalerParameter = null)
{
this._modelPath = modelPath;
this._parameter = parameter;
this._upscalerParameter = upscalerParameter;
Initialize();
}
~StableDiffusionModel() => Dispose();
#endregion
#region Methods
private void Initialize()
{
_ctx = Native.new_sd_ctx(_modelPath,
_parameter.VaePath,
_parameter.TaesdPath,
_parameter.ControlNetPath,
_parameter.LoraModelDir,
_parameter.EmbeddingsDirectory,
_parameter.StackedIdEmbeddingsDirectory,
_parameter.VaeDecodeOnly,
_parameter.VaeTiling,
false,
_parameter.ThreadCount,
_parameter.Quantization,
_parameter.RngType,
_parameter.Schedule,
_parameter.KeepClipOnCPU,
_parameter.KeepControlNetOnCPU,
_parameter.KeepVaeOnCPU);
if (_ctx == null) throw new NullReferenceException("Failed to initialize Stable Diffusion");
if (_upscalerParameter != null)
{
_upscalerCtx = Native.new_upscaler_ctx(_upscalerParameter.ESRGANPath,
_upscalerParameter.ThreadCount,
_upscalerParameter.Quantization);
if (_upscalerCtx == null) throw new NullReferenceException("Failed to initialize Stable Diffusion");
}
}
public StableDiffusionImage TextToImage(string prompt, StableDiffusionParameter parameter)
{
ObjectDisposedException.ThrowIf(_disposed, this);
Native.sd_image_t* result;
if (parameter.ControlNet.IsEnabled)
{
fixed (byte* imagePtr = parameter.ControlNet.Image)
{
if (parameter.ControlNet.CannyPreprocess)
{
Native.sd_image_t controlNetImage = new()
{
width = (uint)parameter.Width,
height = (uint)parameter.Height,
channel = 3,
data = Native.preprocess_canny(imagePtr,
parameter.Width,
parameter.Height,
parameter.ControlNet.CannyHighThreshold,
parameter.ControlNet.CannyLowThreshold,
parameter.ControlNet.CannyWeak,
parameter.ControlNet.CannyStrong,
parameter.ControlNet.CannyInverse)
};
result = Native.txt2img(_ctx,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Seed,
1,
&controlNetImage,
parameter.ControlNet.Strength,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
Marshal.FreeHGlobal((nint)controlNetImage.data);
}
else
{
Native.sd_image_t controlNetImage = new()
{
width = (uint)parameter.Width,
height = (uint)parameter.Height,
channel = 3,
data = imagePtr
};
result = Native.txt2img(_ctx,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Seed,
1,
&controlNetImage,
parameter.ControlNet.Strength,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
}
}
}
else
{
result = Native.txt2img(_ctx,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Seed,
1,
null,
0,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
}
return new StableDiffusionImage(result);
}
public StableDiffusionImage ImageToImage(string prompt, in ReadOnlySpan<byte> image, StableDiffusionParameter parameter)
{
ObjectDisposedException.ThrowIf(_disposed, this);
fixed (byte* imagePtr = image)
{
Native.sd_image_t img = new()
{
width = (uint)parameter.Width,
height = (uint)parameter.Height,
channel = 3,
data = imagePtr
};
return ImageToImage(prompt, img, parameter);
}
}
public StableDiffusionImage ImageToImage(string prompt, StableDiffusionImage image, StableDiffusionParameter parameter)
=> ImageToImage(prompt, *image.Image, parameter);
private StableDiffusionImage ImageToImage(string prompt, Native.sd_image_t image, StableDiffusionParameter parameter)
{
ObjectDisposedException.ThrowIf(_disposed, this);
Native.sd_image_t* result;
if (parameter.ControlNet.IsEnabled)
{
fixed (byte* imagePtr = parameter.ControlNet.Image)
{
if (parameter.ControlNet.CannyPreprocess)
{
Native.sd_image_t controlNetImage = new()
{
width = (uint)parameter.Width,
height = (uint)parameter.Height,
channel = 3,
data = Native.preprocess_canny(imagePtr,
parameter.Width,
parameter.Height,
parameter.ControlNet.CannyHighThreshold,
parameter.ControlNet.CannyLowThreshold,
parameter.ControlNet.CannyWeak,
parameter.ControlNet.CannyStrong,
parameter.ControlNet.CannyInverse)
};
result = Native.img2img(_ctx,
image,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Strength,
parameter.Seed,
1,
&controlNetImage,
parameter.ControlNet.Strength,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
Marshal.FreeHGlobal((nint)controlNetImage.data);
}
else
{
Native.sd_image_t controlNetImage = new()
{
width = (uint)parameter.Width,
height = (uint)parameter.Height,
channel = 3,
data = imagePtr
};
result = Native.img2img(_ctx,
image,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Strength,
parameter.Seed,
1,
&controlNetImage,
parameter.ControlNet.Strength,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
}
}
}
else
{
result = Native.img2img(_ctx,
image,
prompt,
parameter.NegativePrompt,
parameter.ClipSkip,
parameter.CfgScale,
parameter.Width,
parameter.Height,
parameter.SampleMethod,
parameter.SampleSteps,
parameter.Strength,
parameter.Seed,
1,
null,
0,
parameter.PhotoMaker.StyleRatio,
parameter.PhotoMaker.NormalizeInput,
parameter.PhotoMaker.InputIdImageDirectory);
}
return new StableDiffusionImage(result);
}
public StableDiffusionImage Upscale(StableDiffusionImage image, int upscaleFactor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(upscaleFactor, 0, nameof(upscaleFactor));
if (_upscalerCtx == null) throw new NullReferenceException("The upscaler is not initialized.");
return Upscale(*image.Image, upscaleFactor);
}
public StableDiffusionImage Upscale(in ReadOnlySpan<byte> image, int width, int height, int upscaleFactor)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(upscaleFactor, 0, nameof(upscaleFactor));
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(width, 0, nameof(upscaleFactor));
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(height, 0, nameof(upscaleFactor));
if (_upscalerCtx == null) throw new NullReferenceException("The upscaler is not initialized.");
fixed (byte* imagePtr = image)
{
Native.sd_image_t srcImage = new()
{
width = (uint)width,
height = (uint)height,
channel = 3,
data = imagePtr
};
return Upscale(srcImage, upscaleFactor);
}
}
private StableDiffusionImage Upscale(Native.sd_image_t image, int upscaleFactor)
{
Native.sd_image_t result = Native.upscale(_upscalerCtx, image, upscaleFactor);
return new StableDiffusionImage(&result);
}
public void Dispose()
{
if (_disposed) return;
Native.free_sd_ctx(_ctx);
if (_upscalerCtx != null)
Native.free_upscaler_ctx(_upscalerCtx);
GC.SuppressFinalize(this);
_disposed = true;
}
public static void Convert(string modelPath, string vaePath, Quantization quantization, string outputPath)
=> Native.convert(modelPath, vaePath, outputPath, quantization);
public static string GetSystemInfo()
{
void* s = Native.sd_get_system_info();
return Marshal.PtrToStringUTF8((nint)s) ?? "";
}
public static int GetNumPhysicalCores() => Native.get_num_physical_cores();
private static void OnNativeLog(LogLevel level, string text, void* data)
{
try
{
Log?.Invoke(null, new StableDiffusionLogEventArgs(level, text));
}
catch { /**/ }
}
private static void OnNativeProgress(int step, int steps, float time, void* data)
{
try
{
Progress?.Invoke(null, new StableDiffusionProgressEventArgs(step, steps, time));
}
catch { /**/ }
}
#endregion
}

View File

@ -1,47 +0,0 @@
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public sealed class StableDiffusionParameter
{
#region Properties & Fields
public string NegativePrompt { get; set; } = string.Empty;
public float CfgScale { get; set; } = 7.5f;
public int Width { get; set; } = 512;
public int Height { get; set; } = 512;
public Sampler SampleMethod { get; set; } = Sampler.Euler_A;
public int SampleSteps { get; set; } = 25;
public long Seed { get; set; } = -1;
public float Strength { get; set; } = 0.7f;
public int ClipSkip { get; set; } = -1;
public StableDiffusionControlNetParameter ControlNet { get; } = new();
public PhotoMakerParameter PhotoMaker { get; } = new();
#endregion
}
[PublicAPI]
public sealed class StableDiffusionControlNetParameter
{
public bool IsEnabled => Image?.Length > 0;
public byte[]? Image { get; set; } = null;
public float Strength { get; set; } = 0.9f;
public bool CannyPreprocess { get; set; } = false;
public float CannyHighThreshold { get; set; } = 0.08f;
public float CannyLowThreshold { get; set; } = 0.08f;
public float CannyWeak { get; set; } = 0.8f;
public float CannyStrong { get; set; } = 1.0f;
public bool CannyInverse { get; set; } = false;
}
[PublicAPI]
public sealed class PhotoMakerParameter
{
public string InputIdImageDirectory { get; set; } = string.Empty;
public float StyleRatio { get; set; } = 20f;
public bool NormalizeInput { get; set; } = false;
}

View File

@ -1,17 +0,0 @@
using JetBrains.Annotations;
namespace StableDiffusion.NET;
[PublicAPI]
public class UpscalerModelParameter
{
#region Properties & Fields
public int ThreadCount { get; set; } = 8;
public string ESRGANPath { get; set; } = string.Empty;
//TODO DarthAffe 01.01.2024: K-Quants doesn't seem to work so far
public Quantization Quantization { get; set; } = Quantization.F16;
#endregion
}

View File

@ -1,48 +0,0 @@
if not exist stable-diffusion.cpp (
git clone --recursive https://github.com/leejet/stable-diffusion.cpp
)
cd stable-diffusion.cpp
git fetch
git checkout ce1bcc74a6bf1f2c187d4d8ea14ee247cf562af2
git submodule init
git submodule update
if not exist build (
mkdir build
)
cd build
Rem ----------------------------------------------------------------------------
rem Pick one of the builds below.
rem # cuda12 #
cmake .. -DSD_CUBLAS=ON -DSD_BUILD_SHARED_LIBS=ON -DSD_BUILD_EXAMPLES=OFF
rem # rocm5.5 #
rem cmake .. -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 -DSD_BUILD_EXAMPLES=OFF
rem # avx512 #
rem cmake .. -DGGML_AVX512=ON -DSD_BUILD_SHARED_LIBS=ON -DSD_BUILD_EXAMPLES=OFF
rem # avx2 #
rem cmake .. -DGGML_AVX2=ON -DSD_BUILD_SHARED_LIBS=ON -DSD_BUILD_EXAMPLES=OFF
rem # avx #
rem cmake .. -DGGML_AVX2=OFF -DSD_BUILD_SHARED_LIBS=ON -DSD_BUILD_EXAMPLES=OFF
rem # noavx #
rem cmake .. -DGGML_AVX=OFF -DGGML_AVX2=OFF -DGGML_FMA=OFF -DSD_BUILD_SHARED_LIBS=ON -DSD_BUILD_EXAMPLES=OFF
Rem ----------------------------------------------------------------------------
cmake --build . --config Release
cd ..\..
dotnet publish -c Release -o bin
copy .\stable-diffusion.cpp\build\bin\Release\*.dll .\bin\
pause