diff --git a/containers/llamacpp/build-mesa.sh b/containers/llamacpp/build-mesa.sh new file mode 100644 index 0000000..bd16ddd --- /dev/null +++ b/containers/llamacpp/build-mesa.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +############################################################################### +# build-mesa.sh +# +# Builda Mesa dal repository ufficiale upstream (gitlab.freedesktop.org), +# che su main contiene gia' il fix subgroup per Freedreno (commit +# 185c89084ab7 "freedreno/a6xx: Expose subgroup ops", non ancora presente in +# alcuna release stabile 26.1.x). Produce pacchetti .deb pronti da +# installare, salvati in ./deb/ +# +# Nessun fork necessario: il fix e' gia' su main upstream. +# +# Uso: +# ./build-mesa.sh # clona main upstream in /tmp e builda +# ./build-mesa.sh /path/gia/clonato # usa un checkout gia' esistente +# ./build-mesa.sh # clona un repo/fork diverso, se serve +############################################################################### +set -euo pipefail + +echo "=== Installazione dipendenze di build (primo step) ===" +sudo apt-get update +sudo apt-get build-dep -y mesa || { + echo "build-dep su pacchetto 'mesa' fallito o non disponibile, installo un set minimo esplicito." + sudo apt-get install -y \ + git meson ninja-build pkg-config python3-mako python3-pip \ + libdrm-dev libx11-dev libxext-dev libxfixes-dev libxcb1-dev \ + libxcb-glx0-dev libxxf86vm-dev libxrandr-dev libwayland-dev \ + wayland-protocols llvm-dev clang libclc-dev spirv-tools \ + libspirv-tools-dev rustc cargo libelf-dev libzstd-dev \ + libexpat1-dev bindgen cbindgen dpkg-dev debhelper devscripts +} +echo + +UPSTREAM_URL="https://gitlab.freedesktop.org/mesa/mesa.git" +ARG="${1:-}" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEB_OUT_DIR="${SCRIPT_DIR}/deb" + +# Determina se l'argomento e' un path esistente o un URL alternativo da clonare +if [ -n "${ARG}" ] && [ -d "${ARG}/.git" ]; then + MESA_SRC="${ARG}" + echo "=== Uso checkout gia' esistente: ${MESA_SRC} ===" +elif [ -n "${ARG}" ]; then + # argomento passato ma non e' una dir .git valida: trattalo come URL alternativo + CLONE_URL="${ARG}" +else + CLONE_URL="${UPSTREAM_URL}" +fi + +if [ -z "${MESA_SRC:-}" ]; then + CLONE_DIR="/tmp/mesa-$(date +%Y%m%d-%H%M%S)" + echo "=== Clono ${CLONE_URL} in ${CLONE_DIR} ===" + git clone "${CLONE_URL}" "${CLONE_DIR}" + MESA_SRC="${CLONE_DIR}" +fi + +BUILD_DIR="${MESA_SRC}/build-deb" +LOG_FILE="/tmp/build-mesa-$(date +%Y%m%d-%H%M%S).log" + +echo "=== build-mesa.sh ===" +echo "Sorgente Mesa: ${MESA_SRC}" +echo "Output .deb: ${DEB_OUT_DIR}" +echo "Build dir: ${BUILD_DIR}" +echo "Log: ${LOG_FILE}" +echo + +echo "=== Verifica commit fix subgroup presente ===" +mkdir -p "${DEB_OUT_DIR}" +cd "${MESA_SRC}" +if git log --oneline | grep -q "185c89084ab7\|Expose subgroup ops"; then + echo "OK: commit fix subgroup trovato nella history." +else + echo "ATTENZIONE: non trovo il commit atteso nella history di questo checkout." >&2 + echo "Verifica di essere sul branch/commit giusto prima di continuare." >&2 + read -p "Continuare comunque? [y/N] " -n 1 -r + echo + [[ $REPLY =~ ^[Yy]$ ]] || exit 1 +fi +echo + +echo "=== Build Mesa (meson/ninja) ===" +rm -rf "${BUILD_DIR}" +meson setup "${BUILD_DIR}" "${MESA_SRC}" \ + -Dprefix=/usr \ + -Dgallium-drivers=freedreno \ + -Dvulkan-drivers= \ + -Dgallium-rusticl=true \ + -Dgallium-rusticl-enable-drivers=freedreno \ + -Dopencl-spirv=true \ + -Dbuildtype=release \ + 2>&1 | tee -a "${LOG_FILE}" + +ninja -C "${BUILD_DIR}" 2>&1 | tee -a "${LOG_FILE}" +echo + +echo "=== Packaging .deb ===" +# Se il repo ha tooling Debian (cartella debian/), usalo. +if [ -d "${MESA_SRC}/debian" ]; then + echo "Trovata cartella debian/, uso dpkg-buildpackage." + cd "${MESA_SRC}" + dpkg-buildpackage -us -uc -b 2>&1 | tee -a "${LOG_FILE}" + # dpkg-buildpackage mette i risultati nella dir padre di MESA_SRC + find "$(dirname "${MESA_SRC}")" -maxdepth 1 -name "*.deb" -newer "${LOG_FILE}" \ + -exec cp -v {} "${DEB_OUT_DIR}/" \; +else + echo "Nessuna cartella debian/ trovata: creo un pacchetto .deb minimale" + echo "con i soli file installati dalla build (DESTDIR + fpm/dpkg-deb)." + + DESTDIR="${BUILD_DIR}/install-root" + rm -rf "${DESTDIR}" + DESTDIR="${DESTDIR}" ninja -C "${BUILD_DIR}" install 2>&1 | tee -a "${LOG_FILE}" + + PKG_ROOT="${BUILD_DIR}/deb-pkg" + PKG_NAME="mesa-freedreno-subgroups" + PKG_VERSION="26.2.0-devel+subgroups1" + PKG_ARCH="arm64" + PKG_DIR="${PKG_ROOT}/${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}" + + rm -rf "${PKG_ROOT}" + mkdir -p "${PKG_DIR}/DEBIAN" + cp -a "${DESTDIR}/usr" "${PKG_DIR}/" + + cat > "${PKG_DIR}/DEBIAN/control" << EOF +Package: ${PKG_NAME} +Version: ${PKG_VERSION} +Section: libs +Priority: optional +Architecture: ${PKG_ARCH} +Maintainer: local-build +Description: Mesa build from local fork with Freedreno subgroup support + Build locale da fork Mesa (commit 185c89084ab7 "freedreno/a6xx: Expose + subgroup ops", non ancora presente in alcuna release stabile 26.1.x, + disponibile solo su trunk verso 26.2). + . + Sostituisce i file installati da mesa-libgallium / mesa-opencl-icd + di Debian con la build patchata. Da usare solo per test. +EOF + + cat > "${PKG_DIR}/DEBIAN/README-patch.md" << 'EOF' +# Nota sulla patch inclusa + +Commit: 185c89084ab7 "freedreno/a6xx: Expose subgroup ops" +Autore: upstream Mesa (author date 2026-02-11, committer/merge date 2026-05-18) +Motivo assenza in Debian: il branch stabile 26.1 e' stato diramato da main il +2026-04-14 (tag mesa-26.1.0 il 2026-05-06), quindi questo fix e' arrivato su +main DOPO la diramazione. Essendo una feature (non un bugfix), non verra' +backportato al branch 26.1.x per policy standard di Mesa. Il fix esiste solo +su trunk, verso la futura 26.2. + +Verificato (vedi indagine precedente): +- Nessuna dipendenza da ioctl/kernel driver msm: i campi + shader_subgroup_supported_features / max_subgroups / subgroup_sizes sono + popolati da tabelle statiche compilate (freedreno_devices.py). +- Nessuna condizione che esclude a690 (FD690 e' CHIP.A6XX). +- Nessun commit successivo modifica subgroup/ballot/shuffle in freedreno tra + questo fix e HEAD: e' completo e autosufficiente, non un WIP parziale. + +Questo pacchetto installa i file prodotti da una build meson/ninja pulita +di questo checkout, con gallium-drivers=freedreno e rusticl abilitato per +freedreno. Non tocca Vulkan/Turnip. +EOF + + dpkg-deb --build --root-owner-group "${PKG_DIR}" "${DEB_OUT_DIR}/${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}.deb" +fi +echo + +echo "=== Contenuto pacchetto/i .deb prodotti ===" +for deb in "${DEB_OUT_DIR}"/*.deb; do + [ -e "${deb}" ] || continue + echo "--- ${deb} ---" + dpkg -c "${deb}" + echo +done + +echo "=== Fatto ===" +echo "Pacchetti .deb disponibili in: ${DEB_OUT_DIR}" +echo "Log completo: ${LOG_FILE}" diff --git a/containers/llamacpp/llamacpp-opencl.Containerfile b/containers/llamacpp/llamacpp-opencl.Containerfile new file mode 100644 index 0000000..7dc19ff --- /dev/null +++ b/containers/llamacpp/llamacpp-opencl.Containerfile @@ -0,0 +1,96 @@ +### LLaMACpp Builder Container with OpenCL for Qualcomm Adreno GPUs +### Build from source with GGML_OPENCL=ON since no official Linux OpenCL release exists +### +### BUILD: podman build -t llamacpp:opencl-arm64 -f llamacpp-opencl.Containerfile . +### Export: podman save -o /home/badstorm/llamacpp-opencl-arm64.tar localhost/llamacpp:opencl-arm64 +FROM debian:13-slim +USER root +EXPOSE 8090 + +# Copia il/i pacchetto/i .deb con la Mesa patchata (fix subgroup Freedreno, +# commit 185c89084ab7, non presente in alcuna release Debian/backports). +# Generati da build-mesa.sh nella sottocartella ./deb accanto a questo file. +COPY deb/*.deb /tmp/mesa-deb/ + +RUN apt-get update \ + && apt-get install -y curl tar grep sed git ffmpeg nano python3-pip python3 python3-wheel \ + ocl-icd-libopencl1 \ + # Installa la Mesa patchata locale (fornisce mesa-opencl-icd / mesa-libgallium + # equivalenti, con il fix subgroup). Se il .deb ha dipendenze non risolte, + # il secondo apt-get -f install le completa dai repo standard. + && (dpkg -i /tmp/mesa-deb/*.deb || true) \ + && apt-get install -y -f \ + && rm -rf /tmp/mesa-deb \ + && pip install --break-system-packages --upgrade setuptools \ + && pip install --break-system-packages -U "huggingface_hub[cli]" \ + && if [ -f requirements.txt ]; then pip install --break-system-packages -r requirements.txt; fi \ + && apt autoremove -y \ + && apt clean -y \ + && rm -rf /tmp/* /var/tmp/* \ + && rm -rf /var/lib/apt/lists/* \ + && find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete \ + && find /var/cache -type f -delete + +WORKDIR /build +RUN git clone --depth 1 https://github.com/ggml-org/llama.cpp + +# --- PATCH: riconosci device Rusticl/Freedreno (es. "FD690") come Adreno --- +# Il backend OpenCL controlla dev_ctx->device_name cercando le stringhe +# "Adreno" o "Qualcomm" (vedi ggml_opencl_is_device_supported in +# ggml/src/ggml-opencl/ggml-opencl.cpp). Il driver Mesa/Rusticl riporta pero' +# il device come "FD690" (Freedreno + numero modello), non "Adreno 690", quindi +# il controllo fallisce e il device viene scartato con "unsupported GPU". +# Questa patch resta necessaria SEMPRE, indipendentemente dalla versione Mesa, +# perche' riguarda solo il nome con cui Rusticl si presenta, non le capability +# del device. Aggiungiamo il pattern "FD6" (copre la serie Adreno 6xx via +# Freedreno) alla condizione che marca il device come GPU_FAMILY::ADRENO. +# +# NOTA: la patch precedente che bypassava il controllo "does not support +# subgroups" NON e' piu' necessaria ne' presente qui: con la Mesa patchata +# (vedi deb/ - fix upstream "freedreno/a6xx: Expose subgroup ops") il device +# dichiara realmente cl_khr_subgroups, quindi il controllo originale del +# codice passa senza bisogno di bypass. Se in futuro si torna a una Mesa +# senza quel fix, il controllo tornera' a scartare il device correttamente +# (comportamento voluto: meglio un fallback esplicito che un bypass silente). +RUN cd llama.cpp \ + && grep -n 'strstr(dev_ctx->device_name.c_str(), "Adreno")' ggml/src/ggml-opencl/ggml-opencl.cpp \ + && sed -i \ + 's/strstr(dev_ctx->device_name.c_str(), "Adreno") ||/strstr(dev_ctx->device_name.c_str(), "Adreno") ||\n strstr(dev_ctx->device_name.c_str(), "FD6") ||/' \ + ggml/src/ggml-opencl/ggml-opencl.cpp \ + && grep -n -A5 'strstr(dev_ctx->device_name.c_str(), "Adreno")' ggml/src/ggml-opencl/ggml-opencl.cpp + +RUN cd llama.cpp \ + && mkdir build && cd build \ + && cmake .. -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DGGML_OPENCL=ON \ + -DGGML_OPENCL_EMBED_KERNELS=ON \ + -DGGML_OPENCL_USE_ADRENO_KERNELS=ON \ + -DBUILD_SHARED_LIBS=ON \ + && cmake --build . --config Release -j$(nproc) + +# Copy the built binaries and libraries into /app, matching the layout the runtime expects +RUN mkdir -p /app \ + && find /build/llama.cpp/build/bin -maxdepth 1 -type f -exec cp {} /app/ \; \ + && find /build/llama.cpp/build -maxdepth 3 -iname "*.so*" -exec cp -P {} /app/ \; \ + && chmod +x /app/llama-server \ + && rm -rf /build + +WORKDIR /app +ENV PATH=/app:$PATH +ENV LD_LIBRARY_PATH=/app:$LD_LIBRARY_PATH +ENV HF_HUB_ENABLE_HF_TRANSFER=1 +ENV LLAMA_ARG_HOST=0.0.0.0 +ENV LLAMA_ARG_PORT=8090 +ENV LLAMA_ARG_HF_REPO=unsloth/Qwen3.5-35B-A3B-GGUF:Q2_K_XL +ENV LLAMA_ARG_NO_MMAP=true +ENV LLAMA_ARG_CTX_SIZE=128000 +# Adreno-specific: make sure Rusticl/OpenCL device is picked correctly +ENV GGML_OPENCL_PLATFORM=0 +ENV GGML_OPENCL_DEVICE=0 +# NECESSARIA: senza questa, Rusticl carica la piattaforma OpenCL ma con 0 +# device (il driver Freedreno non viene attivato). Con questa variabile +# clinfo/ggml_opencl vedono correttamente il device Adreno (es. FD690). +ENV RUSTICL_ENABLE=freedreno +ENTRYPOINT ["/app/llama-server"] +CMD ["--no-warmup"]