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
Binary file added assets/wan/Wan2.2_A14B_vace_r2v.mp4
Binary file not shown.
25 changes: 25 additions & 0 deletions docs/wan.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Wan models require `-M vid_gen`, including single-frame generation. `--video-fra
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/tree/main/split_files/diffusion_models
- gguf: https://huggingface.co/QuantStack/Wan2.2-S2V-14B-GGUF/tree/main
- int8_convrot safetensors: https://huggingface.co/noctrex/Wan2.2-S2V-14B-int8_convrot
- Wan2.2 VACE-Fun A14B
- safetensors: https://huggingface.co/alibaba-pai/Wan2.2-VACE-Fun-A14B
- gguf: https://huggingface.co/QuantStack/Wan2.2-VACE-Fun-A14B-GGUF/tree/main
- Download vae
- wan_2.1_vae (for all the wan model except Wan2.2 TI2V 5B)
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/vae/wan_2.1_vae.safetensors
Expand Down Expand Up @@ -256,3 +259,25 @@ ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\fr
```

<video src=../assets/wan/Wan2.1_14B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>

### Wan2.2 VACE-Fun A14B

VACE-Fun runs as a MoE pair: `--diffusion-model` takes the low-noise expert and
`--high-noise-diffusion-model` the high-noise one. Reference-to-video uses `-i`
for the reference image, same as Wan2.1 VACE.

#### R2V

```
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\models\diffusion_models\Wan2.2-VACE-Fun-A14B-low-noise-Q8_0.gguf --high-noise-diffusion-model ..\models\diffusion_models\Wan2.2-VACE-Fun-A14B-high-noise-Q8_0.gguf --vae ..\models\vae\wan_2.1_vae.safetensors --t5xxl ..\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
```

<video src=../assets/wan/Wan2.2_A14B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>

#### T2V

Same command without `-i` (VACE context is synthesized from an empty control
video, like Wan2.1 VACE t2v).

> On GPUs with ~12 GB VRAM, VACE also needs `--vae-tiling` — the control-video
> encode can exceed the budget otherwise.
10 changes: 7 additions & 3 deletions src/model_io/gguf_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ bool read_gguf_file(const std::string& file_path,
gguf_context* ctx_gguf_ = nullptr;
ggml_context* ctx_meta_ = nullptr;

ctx_gguf_ = gguf_init_from_file(file_path.c_str(), {true, &ctx_meta_});
GGUFReader gguf_reader;
bool probe_ok = gguf_reader.load(file_path);

if (!probe_ok || !gguf_reader.has_tensors_beyond_ggml_limits()) {
ctx_gguf_ = gguf_init_from_file(file_path.c_str(), {true, &ctx_meta_});
}
if (!ctx_gguf_) {
GGUFReader gguf_reader;
if (!gguf_reader.load(file_path)) {
if (!probe_ok && !gguf_reader.load(file_path)) {
set_error(error, "failed to open '" + file_path + "' with GGUFReader");
return false;
}
Expand Down
122 changes: 71 additions & 51 deletions src/model_io/gguf_reader_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,72 @@ enum class GGUFMetadataType : uint32_t {
class GGUFReader {
private:
std::vector<GGUFTensorInfo> tensors_;
bool has_wide_tensors_ = false;
uint64_t remaining_bytes_ = 0;
size_t data_offset_;
size_t alignment_ = 32; // default alignment is 32

template <typename T>
bool safe_read(std::ifstream& fin, T& value) {
fin.read(reinterpret_cast<char*>(&value), sizeof(T));
return fin.good();
return safe_read(fin, reinterpret_cast<char*>(&value), sizeof(T));
}

bool safe_read(std::ifstream& fin, char* buffer, size_t size) {
if (size > remaining_bytes_)
return false;
fin.read(buffer, size);
return fin.good();
if (!fin.good())
return false;
remaining_bytes_ -= size;
return true;
}

bool safe_skip(std::ifstream& fin, uint64_t count, uint64_t element_size = 1) {
if (count > remaining_bytes_ / element_size)
return false;
uint64_t size = count * element_size;
fin.seekg(static_cast<std::streamoff>(size), std::ios::cur);
if (!fin.good())
return false;
remaining_bytes_ -= size;
return true;
}

bool safe_seek(std::ifstream& fin, std::streamoff offset, std::ios::seekdir dir) {
fin.seekg(offset, dir);
return fin.good();
bool skip_metadata_values(std::ifstream& fin, GGUFMetadataType type, uint64_t count) {
switch (type) {
case GGUFMetadataType::UINT8:
case GGUFMetadataType::INT8:
case GGUFMetadataType::BOOL:
return safe_skip(fin, count);

case GGUFMetadataType::UINT16:
case GGUFMetadataType::INT16:
return safe_skip(fin, count, 2);

case GGUFMetadataType::UINT32:
case GGUFMetadataType::INT32:
case GGUFMetadataType::FLOAT32:
return safe_skip(fin, count, 4);

case GGUFMetadataType::UINT64:
case GGUFMetadataType::INT64:
case GGUFMetadataType::FLOAT64:
return safe_skip(fin, count, 8);

case GGUFMetadataType::STRING:
if (count > remaining_bytes_ / sizeof(uint64_t))
return false;
for (uint64_t i = 0; i < count; i++) {
uint64_t len = 0;
if (!safe_read(fin, len) || !safe_skip(fin, len))
return false;
}
return true;

default:
LOG_ERROR("Unknown metadata type=%u", static_cast<uint32_t>(type));
return false;
}
}

bool read_metadata(std::ifstream& fin) {
Expand Down Expand Up @@ -84,52 +133,12 @@ class GGUFReader {
return true;
}

switch (static_cast<GGUFMetadataType>(type)) {
case GGUFMetadataType::UINT8:
case GGUFMetadataType::INT8:
case GGUFMetadataType::BOOL:
return safe_seek(fin, 1, std::ios::cur);

case GGUFMetadataType::UINT16:
case GGUFMetadataType::INT16:
return safe_seek(fin, 2, std::ios::cur);

case GGUFMetadataType::UINT32:
case GGUFMetadataType::INT32:
case GGUFMetadataType::FLOAT32:
return safe_seek(fin, 4, std::ios::cur);

case GGUFMetadataType::UINT64:
case GGUFMetadataType::INT64:
case GGUFMetadataType::FLOAT64:
return safe_seek(fin, 8, std::ios::cur);

case GGUFMetadataType::STRING: {
uint64_t len = 0;
if (!safe_read(fin, len))
return false;
return safe_seek(fin, len, std::ios::cur);
}

case GGUFMetadataType::ARRAY: {
uint32_t elem_type = 0;
uint64_t len = 0;
if (!safe_read(fin, elem_type))
return false;
if (!safe_read(fin, len))
return false;

for (uint64_t i = 0; i < len; i++) {
if (!read_metadata(fin))
return false;
}
return true;
}

default:
LOG_ERROR("Unknown metadata type=%u", type);
uint64_t count = 1;
if (type == static_cast<uint32_t>(GGUFMetadataType::ARRAY)) {
if (!safe_read(fin, type) || !safe_read(fin, count))
return false;
}
return skip_metadata_values(fin, static_cast<GGUFMetadataType>(type), count);
}

GGUFTensorInfo read_tensor_info(std::ifstream& fin) {
Expand All @@ -154,6 +163,7 @@ class GGUFReader {
}

if (n_dims > GGML_MAX_DIMS) {
has_wide_tensors_ = true;
for (uint32_t i = GGML_MAX_DIMS; i < n_dims; i++) {
info.shape[GGML_MAX_DIMS - 1] *= info.shape[i]; // stack to last dim;
}
Expand All @@ -174,12 +184,20 @@ class GGUFReader {

public:
bool load(const std::string& file_path) {
std::ifstream fin(file_path, std::ios::binary);
std::ifstream fin(file_path, std::ios::binary | std::ios::ate);
if (!fin) {
LOG_ERROR("failed to open '%s'", file_path.c_str());
return false;
}

std::streamoff file_size = fin.tellg();
if (file_size < 0)
return false;
remaining_bytes_ = static_cast<uint64_t>(file_size);
fin.seekg(0, std::ios::beg);
if (!fin.good())
return false;

// --- Header ---
char magic[4];
if (!safe_read(fin, magic, 4) || strncmp(magic, "GGUF", 4) != 0) {
Expand Down Expand Up @@ -228,6 +246,8 @@ class GGUFReader {
}

const std::vector<GGUFTensorInfo>& tensors() const { return tensors_; }

bool has_tensors_beyond_ggml_limits() const { return has_wide_tensors_; }
size_t data_offset() const { return data_offset_; }
};

Expand Down
Loading