Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/actions/setup/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ runs:
-DSD_BUILD_EXAMPLES=OFF \
-DSD_WEBP=OFF \
-DSD_WEBM=OFF \
-DGGML_AVX2=ON
-DGGML_AVX2=ON \
-DGGML_METAL=OFF
time cmake --build builds/cpu --config Release
working-directory: ./sd
- name: Build stable-diffusion.cpp (Windows)
Expand Down Expand Up @@ -127,7 +128,8 @@ runs:
VENV_BIN="venv/bin"
fi

$VENV_BIN/pip install huggingface_hub requests
$VENV_BIN/python -m pip install --upgrade pip
$VENV_BIN/python -m pip install huggingface_hub requests
$VENV_BIN/python -u .github/scripts/download_ci_models.py
- name: Run tests and print coverages (Unix)
if: ${{ inputs.os == 'unix' }}
Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@ test:
go clean -testcache
go test -v -coverprofile=coverage.out "$(shell go list ./... | grep -v "examples")"
go tool cover -html=coverage.out -o coverage.html

sd_parity:
release=$$(cat stable_diffusion.release); \
cd "$$SD" && git diff $$release HEAD -- include/stable-diffusion.h

run_gen_image_example:
go run examples/image_gen/image_gen.go
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ High-performance diffusion model inference in pure Go.
[![Linux](https://github.com/l8bloom/gosd/actions/workflows/linux.yaml/badge.svg)](https://github.com/l8bloom/gosd/actions/workflows/linux.yaml)
[![Windows](https://github.com/l8bloom/gosd/actions/workflows/windows.yaml/badge.svg)](https://github.com/l8bloom/gosd/actions/workflows/windows.yaml)
[![macOS](https://github.com/l8bloom/gosd/actions/workflows/macos.yaml/badge.svg)](https://github.com/l8bloom/gosd/actions/workflows/macos.yaml)
[![stable-diffusion.cpp](https://img.shields.io/badge/sd.cpp-6614334-yellow)](https://github.com/leejet/stable-diffusion.cpp/releases/tag/master-583-6614334)
[![Coverage](https://img.shields.io/badge/code%20coverage-79%25-purple)](https://github.com/l8bloom/gosd/actions)
[![stable-diffusion.cpp](https://img.shields.io/badge/sd.cpp-6614334-yellow)](https://github.com/leejet/stable-diffusion.cpp/releases/tag/master-593-3d6064b)
[![Coverage](https://img.shields.io/badge/code%20coverage-80%25-purple)](https://github.com/l8bloom/gosd/actions)


## Features
Expand Down
33 changes: 25 additions & 8 deletions examples/image_gen/image_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ package main

import (
"fmt"
"os"

sd "github.com/l8bloom/gosd/pkg/gosd"
)

func main() {
// load the dynamic libraries
sd.Load()
if err := sd.Load(); err != nil {
panic(err.Error())
}

// create and conifgure the inference context
ctxParams := sd.ContextParamsInit()

ctxParams.DiffusionModelPath = "/tmp/stable.diffusion/flux-2-klein-9b-Q8_0.gguf"
ctxParams.VAEPath = "/tmp/stable.diffusion/diffusion_pytorch_model.safetensors"
ctxParams.LLMPath = "/tmp/stable.diffusion/Qwen3-8B-Q8_0.gguf"
ctxParams.DiffusionModelPath = os.Getenv("DIFFUSION_MODEL_PATH")
ctxParams.VAEPath = os.Getenv("VAE_PATH")
ctxParams.LLMPath = os.Getenv("LLM_PATH")

ctxParams.DiffusionFlashAttn = true // potential hardware optimizations
// ctxParams.KeepClipOnCPU = true // in case of lower vram
Expand All @@ -29,23 +32,37 @@ func main() {
imgParams := sd.ImageGenParamsInit()

// prompts
imgParams.Prompt = "An orange cat on palm beach playing with oranges."
imgParams.Prompt = "a beautiful landscape, ultra-detailed, 8K resolution, photorealistic, cinematic lighting"
imgParams.NegativePrompt = "mascots, watermark, signature"
// follow the instruction more closely
imgParams.SampleParams.Guidance.TextCfg = 7

// sampler config
imgParams.SampleParams.SampleSteps = 20
imgParams.SampleParams.SampleSteps = 10

// vram saving configuration in case of lower vram
imgParams.VAETilingParams.Enabled = true
imgParams.VAETilingParams.RelSizeX = 4
imgParams.VAETilingParams.RelSizeY = 4

// image resolution
imgParams.Width = 1536
imgParams.Height = 768
imgParams.Width = 512
imgParams.Height = 512

// optionally refine/upscale the image after 1st generation pass
// Hires = High Resolution
imgParams.HiresParams.Enabled = true
imgParams.HiresParams.Steps = 10
// lower keeps it similar to 1st pass image, higher brings more variance
imgParams.HiresParams.DenoisingStrength = 0.4
imgParams.HiresParams.Scale = 2
// HiresUpscalerLatent is the default mode
// imgParams.HiresParams.Upscaler = sd.HiresUpscalerLatent

fmt.Printf("\nImage params:\n%s", sd.ImageGenParamsToStr(imgParams))

genImage := sd.GenerateImage(ctx, imgParams)
genImage.SavePNG("output.png")
fmt.Println(sd.Version())
fmt.Println(sd.Commit())
}
Binary file added examples/image_gen/output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions pkg/gosd/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ var (

// SD_API char* sd_img_gen_params_to_str(const sd_img_gen_params_t* sd_img_gen_params);
imageGenParamsToStr ffi.Fun

// SD_API void sd_hires_params_init(sd_hires_params_t* hires_params);
hiresParamsInit ffi.Fun
)

func loadImageRoutines(lib ffi.Lib) error {
Expand Down Expand Up @@ -46,6 +49,14 @@ func loadImageRoutines(lib ffi.Lib) error {
return loadError("sd_img_gen_params_to_str", err)
}

if hiresParamsInit, err = lib.Prep(
"sd_hires_params_init",
&ffi.TypeVoid,
&ffi.TypePointer,
); err != nil {
return loadError("sd_hires_params_init", err)
}

return nil
}

Expand Down Expand Up @@ -165,6 +176,82 @@ const (
SampleMethodCount
)

type HiresUpscalerType int32

const (
HiresUpscalerNone HiresUpscalerType = iota
HiresUpscalerLatent
HiresUpscalerLatentNearest
HiresUpscalerLatentNearestExact
HiresUpscalerLatentAntialiased
HiresUpscalerLatentBicubic
HiresUpscalerLatentBicubicAntialiased
HiresUpscalerLanczos
HiresUpscalerNearest
HiresUpscalerModel
HiresUpscalerCount
)

type hiresParams struct {
Enabled uint8 // bool enabled;
Upscaler HiresUpscalerType // enum sd_hires_upscaler_t upscaler;
ModelPath *byte // const char* model_path;
Scale float32 // float scale;
TargetWidth int32 // int target_width;
TargetHeight int32 // int target_height;
Steps int32 // int steps;
DenoisingStrength float32 // float denoising_strength;
UpscaleTileSize int32 // int upscale_tile_size;
}

func (hp *hiresParams) toGo() *HiresParams {
return &HiresParams{
Enabled: byteToBool(hp.Enabled),
Upscaler: hp.Upscaler,
ModelPath: charToString(hp.ModelPath),
Scale: hp.Scale,
TargetWidth: hp.TargetWidth,
TargetHeight: hp.TargetHeight,
Steps: hp.Steps,
DenoisingStrength: hp.DenoisingStrength,
UpscaleTileSize: hp.UpscaleTileSize,
}
}

func newHiresParams() *hiresParams {
hp := &hiresParams{
ModelPath: utilsGetNulString(),
}

return hp
}

type HiresParams struct {
Enabled bool
Upscaler HiresUpscalerType
ModelPath string
Scale float32
TargetWidth int32
TargetHeight int32
Steps int32
DenoisingStrength float32
UpscaleTileSize int32
}

func (hp *HiresParams) toC() *hiresParams {
return &hiresParams{
Enabled: boolToByte(hp.Enabled),
Upscaler: hp.Upscaler,
ModelPath: stringToChar(hp.ModelPath),
Scale: hp.Scale,
TargetWidth: hp.TargetWidth,
TargetHeight: hp.TargetHeight,
Steps: hp.Steps,
DenoisingStrength: hp.DenoisingStrength,
UpscaleTileSize: hp.UpscaleTileSize,
}
}

type guidanceParams struct {
TextCfg float32 // float txt_cfg;
ImageCfg float32 // float img_cfg;
Expand Down Expand Up @@ -356,6 +443,7 @@ type imageParams struct {
PMParams pMParamsType // sd_pm_params_t pm_params;
VAETilingParams vAETilingParams // sd_tiling_params_t vae_tiling_params;
Cache cacheParams // sd_cache_params_t cache;
HiresParams hiresParams // sd_hires_params_t hires;
}

func (i *imageParams) toGo() *ImageParams {
Expand Down Expand Up @@ -400,6 +488,7 @@ func (i *imageParams) toGo() *ImageParams {
PMParams: *i.PMParams.toGo(),
VAETilingParams: *i.VAETilingParams.toGo(),
Cache: *i.Cache.toGo(),
HiresParams: *i.HiresParams.toGo(),
}
}

Expand All @@ -426,6 +515,7 @@ type ImageParams struct {
PMParams PMParamsType
VAETilingParams VAETilingParams
Cache CacheParams
HiresParams HiresParams
}

func (i *ImageParams) toC() *imageParams {
Expand Down Expand Up @@ -461,6 +551,7 @@ func (i *ImageParams) toC() *imageParams {
PMParams: *i.PMParams.toC(),
VAETilingParams: *i.VAETilingParams.toC(),
Cache: *i.Cache.toC(),
HiresParams: *i.HiresParams.toC(),
}
}

Expand Down Expand Up @@ -549,3 +640,10 @@ func (img Image) Pixelize() imgPckg.RGBA {
}
return *rgba
}

func HiresParamsInit() HiresParams {
hp := newHiresParams()

hiresParamsInit.Call(nil, unsafe.Pointer(&hp))
return *hp.toGo()
}
32 changes: 32 additions & 0 deletions pkg/gosd/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,35 @@ func TestGenerateImage(t *testing.T) {

Upscale(uctx, image, 4)
}

func TestHiresParamsInit(t *testing.T) {
hiresParams := HiresParamsInit()

if hiresParams.Enabled {
t.Errorf("expected Enabled=false, got %t", hiresParams.Enabled)
}
if hiresParams.Upscaler != HiresUpscalerLatent {
t.Errorf("expected Upscaler=HiresUpscalerLatent, got %d", hiresParams.Upscaler)
}
if hiresParams.ModelPath != "" {
t.Errorf("expected ModelPath=\"\", got %s", hiresParams.ModelPath)
}
if hiresParams.Scale != 2.0 {
t.Errorf("expected Scale=2.0, got %f", hiresParams.Scale)
}
if hiresParams.TargetHeight != 0 {
t.Errorf("expected TargetHeight=0, got %d", hiresParams.TargetHeight)
}
if hiresParams.TargetWidth != 0 {
t.Errorf("expected TargetWidth=0, got %d", hiresParams.TargetWidth)
}
if hiresParams.Steps != 0 {
t.Errorf("expected Steps=0, got %d", hiresParams.Steps)
}
if hiresParams.DenoisingStrength != 0.7 {
t.Errorf("expected DenoisingStrength=0.7, got %f", hiresParams.DenoisingStrength)
}
if hiresParams.UpscaleTileSize != 128 {
t.Errorf("expected UpscaleTileSize=128, got %d", hiresParams.UpscaleTileSize)
}
}
29 changes: 29 additions & 0 deletions pkg/gosd/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ var (

// SD_API enum lora_apply_mode_t str_to_lora_apply_mode(const char* str);
strToLoraApplyMode ffi.Fun

// SD_API const char* sd_hires_upscaler_name(enum sd_hires_upscaler_t upscaler);
hiresUpscalerName ffi.Fun

// SD_API enum sd_hires_upscaler_t str_to_sd_hires_upscaler(const char* str);
strToHiresUpscaler ffi.Fun
)

func loadSystemRoutines(lib ffi.Lib) error {
Expand Down Expand Up @@ -136,6 +142,14 @@ func loadSystemRoutines(lib ffi.Lib) error {
return loadError("str_to_lora_apply_mode", err)
}

if hiresUpscalerName, err = lib.Prep("sd_hires_upscaler_name", &ffi.TypePointer, &ffi.TypeSint32); err != nil {
return loadError("sd_hires_upscaler_name", err)
}

if strToHiresUpscaler, err = lib.Prep("str_to_sd_hires_upscaler", &ffi.TypeSint32, &ffi.TypePointer); err != nil {
return loadError("str_to_sd_hires_upscaler", err)
}

return nil
}

Expand Down Expand Up @@ -283,3 +297,18 @@ func StrToLoraApplyMode(typeName string) LoraApplyModeType {
strToLoraApplyMode.Call(unsafe.Pointer(&loraMode), unsafe.Pointer(&name))
return loraMode
}

func HiresUpscalerName(hiresMode HiresUpscalerType) string {
res := utilsGetNulString()

hiresUpscalerName.Call(unsafe.Pointer(&res), unsafe.Pointer(&hiresMode))
return charToString(res)
}

func StrToHiresUpscaler(typeName string) HiresUpscalerType {
var hiresMode HiresUpscalerType
name := utilsStrToNulString(typeName)

strToHiresUpscaler.Call(unsafe.Pointer(&hiresMode), unsafe.Pointer(&name))
return hiresMode
}
37 changes: 37 additions & 0 deletions pkg/gosd/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,40 @@ func TestStrToLoraApplyMode(t *testing.T) {
t.Errorf("expected `2` for `LoraApplyAtRuntime`, got %d", loraMode)
}
}

func TestHiresUpscalerName(t *testing.T) {
name := HiresUpscalerName(HiresUpscalerNone)

if name != "None" {
t.Errorf("expected `None` for `HiresUpscalerNone`, got %q", name)
}

name = HiresUpscalerName(HiresUpscalerLatent)

if name != "Latent" {
t.Errorf("expected `Latent` for `HiresUpscalerLatent`, got %q", name)
}

name = HiresUpscalerName(HiresUpscalerModel)

if name != "Model" {
t.Errorf("expected `Model` for `HiresUpscalerModel`, got %q", name)
}

}

func TestStrToHiresUpscaler(t *testing.T) {
name := "Latent (nearest-exact)"
hiresMode := StrToHiresUpscaler(name)

if hiresMode != HiresUpscalerLatentNearestExact {
t.Errorf("expected `%d` for `nearest-exact`, got %d", HiresUpscalerLatentNearestExact, hiresMode)
}

name = "Latent (bicubic antialiased)"
hiresMode = StrToHiresUpscaler(name)

if hiresMode != HiresUpscalerLatentBicubicAntialiased {
t.Errorf("expected `%d` for `bicubic antialiased`, got %d", HiresUpscalerLatentBicubic, hiresMode)
}
}
2 changes: 1 addition & 1 deletion stable_diffusion.release
Original file line number Diff line number Diff line change
@@ -1 +1 @@
master-583-6614334
master-593-3d6064b
Loading
Loading