diff --git a/CMakeLists.txt b/CMakeLists.txt index a79ce9a..c6234ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,6 +207,15 @@ if(TARGET ggml-vulkan) target_link_libraries(trellis_core PUBLIC ggml-vulkan) message(STATUS "trellis.cpp: Vulkan backend enabled") endif() +if(TARGET ggml-metal) + # macOS/Apple Silicon: ggml's Metal backend (GGML_METAL defaults ON for + # Apple builds). The generic device-enumeration path in make_backend() picks + # the Metal GPU automatically; the two custom kernels (deform_conv, + # decimate_qem) run their CPU fallbacks on this backend. + set(TRELLIS_BUILD_BACKEND "metal") + target_link_libraries(trellis_core PUBLIC ggml-metal) + message(STATUS "trellis.cpp: Metal backend enabled") +endif() if(TARGET ggml-hip) set(TRELLIS_BUILD_BACKEND "hip") target_link_libraries(trellis_core PUBLIC ggml-hip) diff --git a/README.md b/README.md index 6336599..cbb670c 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,12 @@ On Strix Halo, Vulkan is the fastest backend: ROCm requires `GGML_CUDA_DISABLE_GRAPHS=1` (ggml's HIP graph capture stalls on these graphs) and still trails Vulkan by 10–40 %. +**Apple Silicon (Metal):** verified end-to-end on an Apple M5 (24 GB unified): +res-512 image → textured GLB in **9:21** with a **5.6 GB** peak RSS, all +neural stages on Metal (2.4M decoded voxels, 4.8M-face raw mesh). bfloat16 and +f16 tensor APIs are available from M2 on; on M1 use `TRELLIS_FA_FAST=1` +(f16 K/V) since the default FlashAttention path casts K/V to bf16. + ## Tools | tool | purpose | @@ -255,9 +261,15 @@ GGML is vendored in `thirdparty/ggml`. Pick a backend: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DGGML_VULKAN=ON # Vulkan cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON # CUDA cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DGGML_HIP=ON # ROCm +cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release # macOS: Metal (auto) cmake --build build -j ``` +On **macOS/Apple Silicon** no backend flag is needed — ggml's Metal backend +defaults ON for Apple builds and the generic device selection picks the GPU. +The two custom kernels (BiRefNet deformable conv, QEM decimation) run their +CPU fallbacks there. + See `.github/workflows/release.yml` for the exact flags the release binaries use (GPU target lists, `-DGGML_OPENMP=OFF` on Windows). Releases also include a `cuda12` variant built with CUDA 12.9 for Pascal/Volta GPUs (compute capability diff --git a/src/smoke_test.cpp b/src/smoke_test.cpp index 4cae97e..599a5f0 100644 --- a/src/smoke_test.cpp +++ b/src/smoke_test.cpp @@ -15,7 +15,7 @@ int main(int argc, char** argv) { const std::string path = argv[1]; const int gpu = argc > 2 ? atoi(argv[2]) : 0; - printf("loading %s on %s\n", path.c_str(), gpu >= 0 ? "CUDA" : "CPU"); + printf("loading %s on %s\n", path.c_str(), gpu >= 0 ? "GPU" : "CPU"); trellis::Model m = trellis::Model::load(path, gpu); printf("arch : %s\n", m.arch.c_str()); diff --git a/src/ss_decoder.cpp b/src/ss_decoder.cpp index 29d0c0b..e1cec51 100644 --- a/src/ss_decoder.cpp +++ b/src/ss_decoder.cpp @@ -27,8 +27,19 @@ static T* conv3d(ggml_context* c, const Model& m, const std::string& p, T* x, in T* w = m.get(p + ".weight"); const int k = (int)w->ne[0]; const int pad = (k - 1) / 2; - T* y = ggml_conv_3d(c, w, x, IC, 1, 1, 1, pad, pad, pad, 1, 1, 1); // [s0,s1,s2,OC] const int64_t OC = w->ne[3] / IC; +#ifdef __APPLE__ + // ggml_conv_3d lowers to IM2COL_3D + GEMM; the Metal backend implements + // the direct CONV_3D op but not IM2COL_3D, and trellis.cpp runs whole + // graphs on one backend (no sched fallback) — so the im2col lowering + // aborts on Apple GPUs. Same math, same [k,k,k,IC*OC] / [W,H,D,C] + // layouts via the direct op; non-Apple backends keep the validated + // im2col path. + T* y = ggml_conv_3d_direct(c, w, x, 1, 1, 1, pad, pad, pad, 1, 1, 1, + IC, /*n_batch=*/1, (int)OC); // [s0,s1,s2,OC] +#else + T* y = ggml_conv_3d(c, w, x, IC, 1, 1, 1, pad, pad, pad, 1, 1, 1); // [s0,s1,s2,OC] +#endif T* b = ggml_reshape_4d(c, m.get(p + ".bias"), 1, 1, 1, OC); return ggml_add(c, y, b); }