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
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@
url = https://github.com/1bit-MONSTER/ROCmFPX.git
shallow = true
branch = 1bit/vulkan-rocmi4
[submodule "third_party/comfyui.cpp"]
path = third_party/comfyui.cpp
url = https://github.com/1bit-MONSTER/comfyui.cpp.git
branch = master
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ if(ONEBIT_ZINC)
endif()
endif()

# ── ComfyUI.cpp (docs/comfyui.md) ──────────────────────────────────────────
# Off by default: it needs LibTorch. Builds third_party/comfyui.cpp (GPL-3.0) with
# scripts/build-comfyui.sh as its own program; `1bit comfy <workflow.json>` runs it as a
# child process. The engine never links it, so the engine itself stays Apache-2.0.
option(ONEBIT_COMFYUI "Build ComfyUI.cpp (third_party/comfyui.cpp) for 1bit comfy" OFF)
if(ONEBIT_COMFYUI)
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/third_party/comfyui.cpp/CMakeLists.txt")
message(FATAL_ERROR "ONEBIT_COMFYUI needs third_party/comfyui.cpp: git submodule update --init third_party/comfyui.cpp")
endif()
set(ONEBIT_COMFYUI_BIN "${CMAKE_BINARY_DIR}/comfyui/comfyui_cpp")
add_custom_target(comfyui
COMMAND ${CMAKE_SOURCE_DIR}/scripts/build-comfyui.sh ${CMAKE_BINARY_DIR}/comfyui
BYPRODUCTS ${ONEBIT_COMFYUI_BIN}
USES_TERMINAL)
add_dependencies(onebit comfyui)
target_compile_definitions(onebit PRIVATE ONEBIT_COMFYUI_BIN="${ONEBIT_COMFYUI_BIN}")
endif()

# ── Hugging Face tokenizers (docs/tokenizers.md) ───────────────────────────
# Off by default: it needs cargo. Any model's tokenizer.json, through our C ABI
# over third_party/tokenizers (the same release as the tokenizers npm/PyPI
Expand Down
10 changes: 10 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ ZINC (third_party/zinc)
Copyright (c) 2025 ZINC Contributors
License: MIT

ComfyUI.cpp (third_party/comfyui.cpp)
https://github.com/1bit-MONSTER/comfyui.cpp
A C++ port of ComfyUI (https://github.com/Comfy-Org/ComfyUI, Copyright (C)
Comfy Org and ComfyUI contributors, GPL-3.0).
License: GPL-3.0. ComfyUI.cpp is built as a separate program
(scripts/build-comfyui.sh) that `1bit comfy` runs as a child process; none of
its code is linked into this product. It links LibTorch (BSD-3-Clause) and
vendors stb (MIT / public domain), nlohmann/json (MIT) and the CLIP BPE
vocabulary (MIT).

OpenFlowLM-Next open kernels (third_party/OpenFlowLM-Next; only open_kernels/ is built)
https://github.com/bong-water-water-bong/OpenFlowLM-Next
(a fork of https://github.com/Atomic-Germ/OpenFlowLM-Next)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ serves each model behind an OpenAI-compatible API (`1bit serve`), whatever devic
- ZINC, which also reaches NVIDIA GPUs (CUDA) and Apple GPUs (Metal)
- MLX on Apple Silicon, through lemon-mlx-engine
- Laya, which decides where each request runs
- ComfyUI.cpp: ComfyUI workflows (Stable Diffusion 1.5 text-to-image and image-to-image) in C++, matching ComfyUI's output to 50 dB ([docs/comfyui.md](docs/comfyui.md))
- every Hugging Face model architecture, kept current by a daily census

> **Status:** the engine runs inside Lemonade through `1bit serve` ([docs/lemonade.md](docs/lemonade.md),
Expand Down
28 changes: 28 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
#include <string>
#include <vector>

#include <cerrno>
#include <unistd.h>

namespace {

constexpr const char* kVersion = "0.0.1";
Expand All @@ -61,6 +64,7 @@ void usage(FILE* out) {
" npu-run [options] generate on the NPU fast lane (npu-run --help)\n"
" npu-lax [options] Qwen3.6-35B-A3B on the NPU lax kernels (npu-lax --help)\n"
#endif
" comfy <workflow.json> run a ComfyUI workflow with ComfyUI.cpp (docs/comfyui.md)\n"
" version print the version\n"
" help show this help\n");
}
Expand Down Expand Up @@ -119,6 +123,29 @@ int run_npu(int argc, char** argv) {
}
#endif

// ComfyUI.cpp is GPL-3.0 and a separate program: exec it, never link it (docs/comfyui.md).
// The binary: $ONEBIT_COMFYUI, then this build's (-DONEBIT_COMFYUI=ON), then comfyui_cpp on PATH.
int run_comfy(int argc, char** argv) {
if (argc < 1 || !std::strcmp(argv[0], "-h") || !std::strcmp(argv[0], "--help")) {
std::printf("usage: 1bit comfy <workflow.json>\n"
" runs a ComfyUI API-format workflow (SD1.5 txt2img/img2img, see docs/comfyui.md)\n");
return argc < 1 ? 2 : 0;
}
std::string bin;
if (const char* e = std::getenv("ONEBIT_COMFYUI"); e && *e) bin = e;
#ifdef ONEBIT_COMFYUI_BIN
if (bin.empty() && std::filesystem::exists(ONEBIT_COMFYUI_BIN)) bin = ONEBIT_COMFYUI_BIN;
#endif
if (bin.empty()) bin = "comfyui_cpp";
std::vector<char*> args{bin.data()};
for (int i = 0; i < argc; ++i) args.push_back(argv[i]);
args.push_back(nullptr);
::execvp(args[0], args.data());
std::fprintf(stderr, "1bit comfy: cannot run %s: %s (build with -DONEBIT_COMFYUI=ON or set ONEBIT_COMFYUI)\n",
bin.c_str(), std::strerror(errno));
return 127;
}

} // namespace

int main(int argc, char** argv) {
Expand Down Expand Up @@ -161,6 +188,7 @@ int main(int argc, char** argv) {
}
}
#endif
if (cmd == "comfy") return run_comfy(argc - 2, argv + 2);
if (cmd == "version" || cmd == "--version") {
std::printf("1bit %s\n", kVersion);
return 0;
Expand Down
72 changes: 72 additions & 0 deletions docs/comfyui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!--
Copyright 2026 bong-water-water-bong
SPDX-License-Identifier: Apache-2.0

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# ComfyUI.cpp

ComfyUI.cpp runs ComfyUI workflows in C++. It is a port of ComfyUI's workflow engine and of
the Stable Diffusion 1.5 nodes on LibTorch, pinned as the submodule `third_party/comfyui.cpp`
([1bit-MONSTER/comfyui.cpp](https://github.com/1bit-MONSTER/comfyui.cpp)).

```
git submodule update --init third_party/comfyui.cpp
cmake -S . -B build -DONEBIT_COMFYUI=ON
cmake --build build
build/1bit comfy workflow.json
```

`1bit comfy` takes a workflow in ComfyUI's API format (the JSON that ComfyUI's "Save (API)"
writes) and runs it. SaveImage writes the PNG to the node's `path` input.

## License

ComfyUI is GPL-3.0, and so is ComfyUI.cpp. The engine is Apache-2.0, so it never links
ComfyUI.cpp. `scripts/build-comfyui.sh` builds it as its own program,
`build/comfyui/comfyui_cpp`, and `1bit comfy` runs that program in place of itself.
`1bit comfy` looks for the binary in `$ONEBIT_COMFYUI` first, then this build's, then
`comfyui_cpp` on `PATH`.

## What runs

Everything a Stable Diffusion 1.5 txt2img or img2img workflow needs:
- CheckpointLoaderSimple, CLIPTextEncode, EmptyLatentImage
- KSampler with `euler` / `normal`, including denoise below 1
- VAEEncode, VAEDecode, LoadImage and SaveImage
- the conditioning, latent and image nodes

It runs on the CPU for now, through LibTorch; the build takes LibTorch from the Python `torch`
package.

## Parity with ComfyUI

ComfyUI.cpp pins ComfyUI itself (`third_party/ComfyUI`, Comfy-Org/ComfyUI `1568e6c`).
`tools/parity_sd15.py` in that repository runs the same workflows through both on CPU fp32 and
compares the images. Measured on Strix Halo with `v1-5-pruned-emaonly.safetensors`, 512x512,
20 steps, cfg 7:

| workflow | PSNR vs ComfyUI | max / mean abs diff (0-255) | pixels off by > 2 |
|---|---|---|---|
| txt2img, "a cat", seed 0 | 50.6 dB | 15 / 0.515 | 0.286% |
| img2img, denoise 0.6, seed 7 | 51.2 dB | 1 / 0.498 | 0.000% |

The remaining difference is fp32 rounding: ComfyUI batches the cond and uncond passes and uses
PyTorch's attention kernel. Through `1bit comfy`, the txt2img workflow takes 48 s and gives the
same 50.6 dB. ComfyUI takes about 35 s on the same CPU.

## Next

- The GPU: LibTorch on ROCm for gfx1151.
- More model families and samplers beyond SD1.5 with Euler.
33 changes: 33 additions & 0 deletions scripts/build-comfyui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Copyright 2026 bong-water-water-bong
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# build-comfyui.sh <prefix>
#
# Builds ComfyUI.cpp pinned in third_party/comfyui.cpp (1bit-MONSTER/comfyui.cpp, GPL-3.0)
# into <prefix>/comfyui_cpp. It is a separate program: the engine runs it as a child
# process (`1bit comfy`) and never links it (docs/comfyui.md). Needs LibTorch; the
# build takes torch's CMake config from `python3 -c "import torch"`, or from Torch_DIR.
set -euo pipefail
prefix=${1:?usage: build-comfyui.sh <prefix>}
root=$(cd "$(dirname "$0")/.." && pwd)
src=$root/third_party/comfyui.cpp
[ -f "$src/CMakeLists.txt" ] || { echo "third_party/comfyui.cpp is empty: git submodule update --init third_party/comfyui.cpp"; exit 1; }
mkdir -p "$prefix"
prefix=$(cd "$prefix" && pwd)
cmake -S "$src" -B "$prefix/build" -DCMAKE_BUILD_TYPE=Release
cmake --build "$prefix/build" -j"$(nproc)" --target comfyui_cpp
cp "$prefix/build/comfyui_cpp" "$prefix/comfyui_cpp"
echo "built $prefix/comfyui_cpp (comfyui.cpp $(git -C "$src" rev-parse --short=12 HEAD 2>/dev/null || echo unknown))"
1 change: 1 addition & 0 deletions third_party/comfyui.cpp
Submodule comfyui.cpp added at 4a0823
2 changes: 1 addition & 1 deletion tools/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
("Start", [("serve", "1bit serve"), ("lemonade", "Lemonade")]),
("Devices", [("npu", "NPU"), ("hrx", "HRX + Vulkan"), ("vulkan", "Vulkan (upstream)"), ("lean", "Lean (ROCmFP4, ROCmI4)"),
("zinc", "ZINC"), ("apple", "Apple Silicon")]),
("Components", [("laya", "Laya router"), ("tokenizers", "Tokenizers"), ("kernel", "Linux kernel")]),
("Components", [("laya", "Laya router"), ("comfyui", "ComfyUI.cpp"), ("tokenizers", "Tokenizers"), ("kernel", "Linux kernel")]),
("Project", [("PORTING", "Porting map")]),
]

Expand Down
Loading