mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-09-21 13:38:01 -05:00
feat: support building with upstream ggml (#1999)
This commit is contained in:
+3
-12
@@ -95,6 +95,8 @@ option(SD_MUSA "sd: musa backend" OFF)
|
||||
option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF)
|
||||
option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF)
|
||||
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
||||
option(SD_USE_UPSTREAM_GGML "sd: build with upstream GGML instead of the patched GGML extensions" OFF)
|
||||
set(SD_GGML_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ggml" CACHE PATH "sd: ggml source directory (also supplies private headers for system ggml)")
|
||||
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
@@ -325,18 +327,7 @@ if (NOT SD_USE_SYSTEM_GGML)
|
||||
endif()
|
||||
|
||||
# deps
|
||||
# Only add ggml if it hasn't been added yet
|
||||
if (NOT TARGET ggml)
|
||||
if (SD_USE_SYSTEM_GGML)
|
||||
find_package(ggml REQUIRED)
|
||||
if (NOT ggml_FOUND)
|
||||
message(FATAL_ERROR "System-installed GGML library not found.")
|
||||
endif()
|
||||
add_library(ggml ALIAS ggml::ggml)
|
||||
else()
|
||||
add_subdirectory(ggml)
|
||||
endif()
|
||||
endif()
|
||||
include(cmake/ggml.cmake)
|
||||
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
if(NOT TARGET ggml AND NOT TARGET ggml::ggml)
|
||||
if(SD_USE_SYSTEM_GGML)
|
||||
find_package(ggml REQUIRED)
|
||||
else()
|
||||
add_subdirectory("${SD_GGML_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ggml")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT TARGET ggml)
|
||||
add_library(ggml ALIAS ggml::ggml)
|
||||
endif()
|
||||
|
||||
get_target_property(sd_ggml_imported ggml IMPORTED)
|
||||
if(sd_ggml_imported)
|
||||
set(sd_ggml_private_include "${SD_GGML_SOURCE_DIR}/src")
|
||||
else()
|
||||
get_target_property(sd_ggml_private_include ggml SOURCE_DIR)
|
||||
endif()
|
||||
if(NOT EXISTS "${sd_ggml_private_include}/ggml-impl.h")
|
||||
message(FATAL_ERROR "Set SD_GGML_SOURCE_DIR to the source tree matching the selected ggml library (ggml-impl.h is required).")
|
||||
endif()
|
||||
target_include_directories(${SD_LIB} PRIVATE "${sd_ggml_private_include}")
|
||||
set_property(TARGET ${SD_LIB} PROPERTY SD_GGML_PRIVATE_INCLUDE_DIR "${sd_ggml_private_include}")
|
||||
|
||||
if(SD_USE_UPSTREAM_GGML)
|
||||
target_compile_definitions(${SD_LIB} PUBLIC SD_USE_UPSTREAM_GGML)
|
||||
message(WARNING "Using upstream GGML: FP8 and INT8 tensorwise/convrot are disabled. Some operators may be unsupported and performance may be lower than with patched GGML.")
|
||||
endif()
|
||||
@@ -16,6 +16,38 @@ git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
## Selecting a GGML source tree
|
||||
|
||||
By default, sd.cpp builds the patched GGML submodule in `ggml/`. To build with
|
||||
an upstream GGML checkout instead, enable `SD_USE_UPSTREAM_GGML` and set
|
||||
`SD_GGML_SOURCE_DIR`:
|
||||
|
||||
```shell
|
||||
cmake -S . -B build-upstream -DSD_USE_UPSTREAM_GGML=ON -DSD_GGML_SOURCE_DIR=../ggml-upstream
|
||||
cmake --build build-upstream --config Release
|
||||
```
|
||||
|
||||
The selected source tree supplies both the library and its private headers.
|
||||
Backend options such as `-DSD_CUDA=ON` apply to the selected tree as usual.
|
||||
|
||||
`SD_USE_UPSTREAM_GGML` defaults to `OFF`, which enables the patched GGML
|
||||
extensions. Set it to `ON` when using upstream GGML; it selects the compatibility
|
||||
mode and does not download or replace the GGML source tree. Upstream mode
|
||||
disables FP8 and INT8 tensorwise/convrot and rejects their model files with an
|
||||
explicit error. FP8 weight type requests, tensor type rules and conversion
|
||||
outputs are also rejected; no automatic conversion is performed.
|
||||
|
||||
Upstream GGML may lack some operators and performance optimizations provided by
|
||||
the patched version. A warning is emitted during CMake configuration and when
|
||||
creating an inference context. Ordinary floating-point and shared GGML
|
||||
quantization types remain available, subject to backend operator support.
|
||||
|
||||
`SD_USE_SYSTEM_GGML=ON` instead links an installed GGML CMake package, located
|
||||
with `ggml_DIR` or `CMAKE_PREFIX_PATH`. In that mode, `SD_GGML_SOURCE_DIR` must
|
||||
point to the matching source tree for private headers. The installed library
|
||||
must use the same ABI settings as sd.cpp, including `GGML_MAX_NAME`.
|
||||
Set `SD_USE_UPSTREAM_GGML=ON` as well if the installed package is upstream GGML.
|
||||
|
||||
## WebP and WebM Support in Examples
|
||||
|
||||
The example applications (`examples/cli` and `examples/server`) use `libwebp` to support WebP image I/O, and `examples/cli` can also use `libwebm` for `.webm` video output. Both are enabled by default. WebM output currently reuses `libwebp` to encode each frame as VP8 before muxing with `libwebm`.
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
sd.cpp can load and execute ComfyUI `int8_tensorwise` safetensors with `convrot` metadata directly. The stored INT8 weights are not converted to another weight type at load time.
|
||||
|
||||
This requires the INT8 tensorwise/convrot extensions in the patched GGML.
|
||||
Builds with `SD_USE_UPSTREAM_GGML=ON` reject these files during loading.
|
||||
|
||||
## Checkpoint format
|
||||
|
||||
Each quantized linear module contains the following tensors:
|
||||
|
||||
@@ -362,6 +362,9 @@ bool convert_with_components(const char* model_path,
|
||||
const char* tensor_type_rules,
|
||||
bool convert_name,
|
||||
int n_threads) {
|
||||
if (!validate_tensor_types(output_type, tensor_type_rules)) {
|
||||
return false;
|
||||
}
|
||||
ModelLoader model_loader;
|
||||
bool loaded_any = false;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "core/ggml_graph_cut.h"
|
||||
#include "core/util.h"
|
||||
#include "ggml-cpu.h"
|
||||
#include "ggml/src/ggml-impl.h"
|
||||
#include "ggml-impl.h"
|
||||
|
||||
namespace sd {
|
||||
ComputeWorkspace::~ComputeWorkspace() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "core/ggml_extend.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "core/ggml_extend_backend.h"
|
||||
@@ -247,6 +248,7 @@ ggml_tensor* ggml_ext_linear_i8_tensorwise(ggml_context* ctx,
|
||||
ggml_tensor* b,
|
||||
int convrot_group_size,
|
||||
float scale) {
|
||||
#ifndef SD_USE_UPSTREAM_GGML
|
||||
GGML_ASSERT(x->type == GGML_TYPE_F32 || (x->type == GGML_TYPE_I8 && scale == 1.f));
|
||||
if (scale != 1.f) {
|
||||
x = ggml_ext_scale(ctx, x, scale);
|
||||
@@ -270,6 +272,16 @@ ggml_tensor* ggml_ext_linear_i8_tensorwise(ggml_context* ctx,
|
||||
}
|
||||
}
|
||||
return x;
|
||||
#else
|
||||
GGML_UNUSED(ctx);
|
||||
GGML_UNUSED(x);
|
||||
GGML_UNUSED(w);
|
||||
GGML_UNUSED(weight_scale);
|
||||
GGML_UNUSED(b);
|
||||
GGML_UNUSED(convrot_group_size);
|
||||
GGML_UNUSED(scale);
|
||||
throw std::runtime_error("INT8 tensorwise/convrot is not supported by this ggml build");
|
||||
#endif
|
||||
}
|
||||
|
||||
ggml_tensor* ggml_ext_pad_ext(ggml_context* ctx,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#endif
|
||||
|
||||
#include "core/util.h"
|
||||
#include "ggml/src/ggml-impl.h"
|
||||
#include "ggml-impl.h"
|
||||
#include "stable-diffusion.h"
|
||||
|
||||
static std::string trim_copy(const std::string& value) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "ggml-alloc.h"
|
||||
#include "ggml-backend.h"
|
||||
|
||||
#include "ggml/src/ggml-impl.h"
|
||||
#include "ggml-impl.h"
|
||||
|
||||
namespace sd::ggml_graph_cut {
|
||||
|
||||
|
||||
+30
-1
@@ -414,14 +414,43 @@ std::vector<std::string> split_string(const std::string& str, char delimiter) {
|
||||
}
|
||||
|
||||
ggml_type sd_type_to_ggml_type(sd_type_t sdtype) {
|
||||
if (sdtype == SD_TYPE_F8_E4M3 || sdtype == SD_TYPE_F8_E5M2) {
|
||||
#ifndef SD_USE_UPSTREAM_GGML
|
||||
return sdtype == SD_TYPE_F8_E4M3 ? GGML_TYPE_F8_E4M3 : GGML_TYPE_F8_E5M2;
|
||||
#else
|
||||
return GGML_TYPE_COUNT;
|
||||
#endif
|
||||
}
|
||||
const int type_value = static_cast<int>(sdtype);
|
||||
if (type_value < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
||||
if (type_value >= 0 && type_value < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
||||
return static_cast<ggml_type>(type_value);
|
||||
} else {
|
||||
return GGML_TYPE_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_tensor_types(sd_type_t type, const char* tensor_type_rules) {
|
||||
if (type != SD_TYPE_COUNT && sd_type_to_ggml_type(type) == GGML_TYPE_COUNT) {
|
||||
LOG_ERROR("weight type %s is not supported by this ggml build", sd_type_name(type));
|
||||
return false;
|
||||
}
|
||||
#ifdef SD_USE_UPSTREAM_GGML
|
||||
for (const auto& rule : split_string(SAFE_STR(tensor_type_rules), ',')) {
|
||||
const auto pos = rule.find('=');
|
||||
if (pos != std::string::npos) {
|
||||
const auto name = rule.substr(pos + 1);
|
||||
if (name == "f8_e4m3" || name == "f8_e5m2") {
|
||||
LOG_ERROR("FP8 is not supported by this ggml build (tensor type rule '%s')", rule.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
GGML_UNUSED(tensor_type_rules);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyValueArgs parse_key_value_args(const char* args, const char* context) {
|
||||
KeyValueArgs pairs;
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ void log_printf(sd_log_level_t level, const char* file, int line, const char* fo
|
||||
void sd_ggml_log_callback(ggml_log_level level, const char* text, void*);
|
||||
|
||||
ggml_type sd_type_to_ggml_type(sd_type_t sdtype);
|
||||
bool validate_tensor_types(sd_type_t type, const char* tensor_type_rules);
|
||||
|
||||
std::string trim(const std::string& s);
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ public:
|
||||
ggml_tensor* w = params["weight"];
|
||||
const float scale = ctx->linear_scale > 0.f ? ctx->linear_scale : this->scale;
|
||||
ggml_tensor* weight_scale = has_weight_scale ? params["weight_scale"] : nullptr;
|
||||
#ifndef SD_USE_UPSTREAM_GGML
|
||||
if (w->type == GGML_TYPE_F8_E4M3 || w->type == GGML_TYPE_F8_E5M2) {
|
||||
bool supports_fp8_matmul = false;
|
||||
if (ctx->backend != nullptr) {
|
||||
@@ -221,6 +222,7 @@ public:
|
||||
w = ggml_cast(ctx->ggml_ctx, w, GGML_TYPE_BF16);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ggml_tensor* b = nullptr;
|
||||
if (bias) {
|
||||
b = params["bias"];
|
||||
@@ -238,6 +240,7 @@ public:
|
||||
if (ctx->weight_adapter && b != nullptr) {
|
||||
b = ctx->weight_adapter->patch_weight(ctx->ggml_ctx, ctx->backend, b, prefix + "bias");
|
||||
}
|
||||
#ifndef SD_USE_UPSTREAM_GGML
|
||||
if (int8_convrot && scale == 1.f) {
|
||||
const auto cache_key = std::make_pair(x, int8_convrot_group_size);
|
||||
auto cached = ctx->int8_convrot_cache.find(cache_key);
|
||||
@@ -248,6 +251,7 @@ public:
|
||||
x = cached->second;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
out = ggml_ext_linear_i8_tensorwise(ctx->ggml_ctx,
|
||||
x,
|
||||
w,
|
||||
|
||||
@@ -57,6 +57,18 @@ bool read_gguf_file(const std::string& file_path,
|
||||
|
||||
size_t data_offset = gguf_reader.data_offset();
|
||||
for (const auto& gguf_tensor_info : gguf_reader.tensors()) {
|
||||
#ifdef SD_USE_UPSTREAM_GGML
|
||||
if (static_cast<int>(gguf_tensor_info.type) == SD_TYPE_F8_E4M3 ||
|
||||
static_cast<int>(gguf_tensor_info.type) == SD_TYPE_F8_E5M2) {
|
||||
set_error(error, "FP8 is not supported by this ggml build (tensor '" + gguf_tensor_info.name + "')");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (static_cast<unsigned>(gguf_tensor_info.type) >= GGML_TYPE_COUNT ||
|
||||
ggml_get_type_traits(gguf_tensor_info.type)->type_size == 0) {
|
||||
set_error(error, "unsupported GGUF tensor type (tensor '" + gguf_tensor_info.name + "')");
|
||||
return false;
|
||||
}
|
||||
TensorStorage tensor_storage(
|
||||
gguf_tensor_info.name,
|
||||
gguf_tensor_info.type,
|
||||
|
||||
@@ -86,10 +86,12 @@ static ggml_type safetensors_dtype_to_ggml_type(const std::string& dtype) {
|
||||
ttype = GGML_TYPE_F32;
|
||||
} else if (dtype == "F64") {
|
||||
ttype = GGML_TYPE_F32;
|
||||
#ifndef SD_USE_UPSTREAM_GGML
|
||||
} else if (dtype == "F8_E4M3") {
|
||||
ttype = GGML_TYPE_F8_E4M3;
|
||||
} else if (dtype == "F8_E5M2") {
|
||||
ttype = GGML_TYPE_F8_E5M2;
|
||||
#endif
|
||||
} else if (dtype == "I32") {
|
||||
ttype = GGML_TYPE_I32;
|
||||
} else if (dtype == "I64") {
|
||||
@@ -230,6 +232,12 @@ bool read_safetensors_file(const std::string& file_path,
|
||||
if (!read_comfy_quant_config(file, file_path, name, data_start + begin, end - begin, config, error)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef SD_USE_UPSTREAM_GGML
|
||||
if (config.format == "int8_tensorwise") {
|
||||
set_error(error, "INT8 tensorwise/convrot is not supported by this ggml build (tensor '" + name + "')");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
const std::string module_name = name.substr(0, name.size() - std::string(".comfy_quant").size());
|
||||
comfy_quant_configs.emplace(module_name, std::move(config));
|
||||
}
|
||||
@@ -279,6 +287,12 @@ bool read_safetensors_file(const std::string& file_path,
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef SD_USE_UPSTREAM_GGML
|
||||
if (dtype == "F8_E4M3" || dtype == "F8_E5M2") {
|
||||
set_error(error, "FP8 is not supported by this ggml build (tensor '" + name + "')");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
ggml_type type = safetensors_dtype_to_ggml_type(dtype);
|
||||
if (type == GGML_TYPE_COUNT) {
|
||||
set_error(error, "unsupported dtype '" + dtype + "' (tensor '" + name + "')");
|
||||
|
||||
@@ -857,6 +857,14 @@ bool StableDiffusionGGML::init_model_loader(ModelLoader& model_loader, ModelConf
|
||||
}
|
||||
|
||||
bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
|
||||
#ifdef SD_USE_UPSTREAM_GGML
|
||||
LOG_WARN(
|
||||
"Using upstream GGML: FP8 and INT8 tensorwise/convrot are disabled. "
|
||||
"Some operators may be unsupported and performance may be lower than with patched GGML.");
|
||||
#endif
|
||||
if (!validate_tensor_types(sd_ctx_params->wtype, sd_ctx_params->tensor_type_rules)) {
|
||||
return false;
|
||||
}
|
||||
for (float scale : {sd_ctx_params->linear_scale, sd_ctx_params->attn_scale}) {
|
||||
if (!std::isfinite(scale) || scale < 0.f || (scale > 0.f && !std::isfinite(1.f / scale))) {
|
||||
LOG_ERROR("scale overrides must be finite positive values, or 0 to keep model defaults");
|
||||
|
||||
@@ -26,13 +26,26 @@ static float get_cache_reuse_threshold(const sd_cache_params_t& params) {
|
||||
}
|
||||
|
||||
const char* sd_type_name(enum sd_type_t type) {
|
||||
if ((int)type < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
||||
return ggml_type_name((ggml_type)type);
|
||||
if (type == SD_TYPE_F8_E4M3) {
|
||||
return "f8_e4m3";
|
||||
}
|
||||
if (type == SD_TYPE_F8_E5M2) {
|
||||
return "f8_e5m2";
|
||||
}
|
||||
const auto ggml_type = sd_type_to_ggml_type(type);
|
||||
if (ggml_type != GGML_TYPE_COUNT) {
|
||||
return ggml_type_name(ggml_type);
|
||||
}
|
||||
return NONE_STR;
|
||||
}
|
||||
|
||||
enum sd_type_t str_to_sd_type(const char* str) {
|
||||
if (!strcmp(str, "f8_e4m3")) {
|
||||
return SD_TYPE_F8_E4M3;
|
||||
}
|
||||
if (!strcmp(str, "f8_e5m2")) {
|
||||
return SD_TYPE_F8_E5M2;
|
||||
}
|
||||
for (int i = 0; i < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT); i++) {
|
||||
auto trait = ggml_get_type_traits((ggml_type)i);
|
||||
if (!strcmp(str, trait->type_name)) {
|
||||
|
||||
Reference in New Issue
Block a user