diff --git a/.github/actions/build-container/action.yaml b/.github/actions/build-container/action.yaml index eed2231d..bea0e41f 100644 --- a/.github/actions/build-container/action.yaml +++ b/.github/actions/build-container/action.yaml @@ -21,6 +21,14 @@ inputs: skyeye-version: description: 'Version to use in build args' required: true + tag-suffix: + description: 'Suffix appended to generated image tags (e.g. "-vulkan")' + required: false + default: '' + backend: + description: 'Which whisper.cpp backend to build (cpu or vulkan)' + required: false + default: 'cpu' runs: using: composite steps: @@ -35,12 +43,15 @@ runs: uses: docker/metadata-action@v5 with: images: ${{ inputs.registry }}/${{ inputs.image-name }} + flavor: | + suffix=${{ inputs.tag-suffix }},onlatest=true - name: Build and push Docker image id: push uses: docker/build-push-action@v6 with: build-args: | SKYEYE_VERSION: ${{ inputs.skyeye-version }} + WHISPER_CPP_BACKEND: ${{ inputs.backend }} target: ${{ inputs.target }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/actions/build-whisper/action.yaml b/.github/actions/build-whisper/action.yaml index ef1b93e0..adcc2010 100644 --- a/.github/actions/build-whisper/action.yaml +++ b/.github/actions/build-whisper/action.yaml @@ -21,9 +21,13 @@ inputs: description: 'The shell to use' required: true default: 'bash' + backend: + description: 'Which whisper.cpp backend to build (cpu or vulkan)' + required: true + default: 'cpu' runs: using: composite steps: - name: Build whisper.cpp shell: ${{ inputs.shell }} - run: make whisper + run: make whisper WHISPER_CPP_BACKEND=${{ inputs.backend }} diff --git a/.github/workflows/skyeye.yaml b/.github/workflows/skyeye.yaml index 1fdf9e4f..d4243821 100644 --- a/.github/workflows/skyeye.yaml +++ b/.github/workflows/skyeye.yaml @@ -9,39 +9,51 @@ on: permissions: {} jobs: lint: - name: Lint + name: Lint (${{ matrix.backend }}) runs-on: ubuntu-latest permissions: contents: read + strategy: + fail-fast: false + matrix: + backend: [cpu, vulkan] steps: - name: Checkout uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup + - name: Install Vulkan dependencies + if: matrix.backend == 'vulkan' + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev glslc - name: Build whisper.cpp uses: ./.github/actions/build-whisper + with: + backend: ${{ matrix.backend }} - name: Lint - run: | - make lint - make vet - make fix - make format - go mod tidy - git diff --exit-code + run: WHISPER_CPP_BACKEND=${{ matrix.backend }} make ci-lint test: - name: Test + name: Test (${{ matrix.backend }}) runs-on: ubuntu-latest permissions: contents: read + strategy: + fail-fast: false + matrix: + backend: [cpu, vulkan] steps: - name: Checkout uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup + - name: Install Vulkan dependencies + if: matrix.backend == 'vulkan' + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev glslc - name: Build whisper.cpp uses: ./.github/actions/build-whisper + with: + backend: ${{ matrix.backend }} - name: Test - run: make test + run: WHISPER_CPP_BACKEND=${{ matrix.backend }} make test build-linux-amd64: name: Build on Linux AMD64 runs-on: ubuntu-latest @@ -54,23 +66,8 @@ jobs: uses: ./.github/actions/setup - name: Build whisper.cpp uses: ./.github/actions/build-whisper - - name: Build SkyEye - run: make skyeye - - name: Build SkyEye Scaler - run: make skyeye-scaler - name: Create dist - shell: bash - run: | - mkdir -p dist/skyeye-linux-amd64/docs/ - cp skyeye dist/skyeye-linux-amd64/skyeye - cp skyeye-scaler dist/skyeye-linux-amd64/skyeye-scaler - chmod +x dist/skyeye-linux-amd64/skyeye - chmod +x dist/skyeye-linux-amd64/skyeye-scaler - cp README.md dist/skyeye-linux-amd64/README.md - cp LICENSE dist/skyeye-linux-amd64/LICENSE - cp config.yaml dist/skyeye-linux-amd64/config.yaml - cp docs/*.md dist/skyeye-linux-amd64/docs/ - tar -czf dist/skyeye-linux-amd64.tar.gz -C dist skyeye-linux-amd64 + run: make dist-linux - name: Upload artifact if: startsWith(github.ref, 'refs/tags/') uses: actions/upload-artifact@v4 @@ -79,6 +76,32 @@ jobs: path: dist/skyeye-linux-amd64.tar.gz if-no-files-found: error overwrite: true + build-linux-amd64-vulkan: + name: Build on Linux AMD64 (Vulkan) + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup + uses: ./.github/actions/setup + - name: Install Vulkan dependencies + run: sudo apt-get update && sudo apt-get install -y libvulkan-dev glslc + - name: Build whisper.cpp + uses: ./.github/actions/build-whisper + with: + backend: vulkan + - name: Create dist + run: make dist-linux-vulkan + - name: Upload artifact + if: startsWith(github.ref, 'refs/tags/') + uses: actions/upload-artifact@v4 + with: + name: skyeye-linux-amd64-vulkan.tar.gz + path: dist/skyeye-linux-amd64-vulkan.tar.gz + if-no-files-found: error + overwrite: true build-macos-arm64: name: Build on macOS runs-on: macos-latest @@ -88,30 +111,14 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies - shell: bash run: make install-macos-dependencies - name: Build whisper.cpp uses: ./.github/actions/build-whisper with: os: macos arch: arm64 - - name: Build SkyEye - run: make skyeye - - name: Build SkyEye Scaler - run: make skyeye-scaler - name: Create dist - shell: bash - run: | - mkdir -p dist/skyeye-macos-arm64/docs/ - cp skyeye dist/skyeye-macos-arm64/skyeye - cp skyeye-scaler dist/skyeye-macos-arm64/skyeye-scaler - chmod +x dist/skyeye-macos-arm64/skyeye - chmod +x dist/skyeye-macos-arm64/skyeye-scaler - cp README.md dist/skyeye-macos-arm64/README.md - cp LICENSE dist/skyeye-macos-arm64/LICENSE - cp config.yaml dist/skyeye-macos-arm64/config.yaml - cp docs/*.md dist/skyeye-macos-arm64/docs/ - tar -czf dist/skyeye-macos-arm64.tar.gz -C dist skyeye-macos-arm64 + run: make dist-macos - name: Upload artifact if: startsWith(github.ref, 'refs/tags/') uses: actions/upload-artifact@v4 @@ -151,29 +158,9 @@ jobs: with: os: windows shell: msys2 {0} - - name: Build Skyeye - shell: msys2 {0} - run: make skyeye.exe - - name: Build Skyeye Scaler - shell: msys2 {0} - run: make skyeye-scaler.exe - name: Create dist shell: msys2 {0} - run: | - mkdir -p dist/skyeye-windows-amd64/docs/ - cp skyeye.exe dist/skyeye-windows-amd64/skyeye.exe - cp skyeye-scaler.exe dist/skyeye-windows-amd64/skyeye-scaler.exe - curl -fsL https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe -o winsw.exe - cp README.md dist/skyeye-windows-amd64/README.md - cp LICENSE dist/skyeye-windows-amd64/LICENSE - cp config.yaml dist/skyeye-windows-amd64/config.yaml - cp docs/*.md dist/skyeye-windows-amd64/docs/ - cp winsw.exe dist/skyeye-windows-amd64/skyeye-service.exe - cp init/winsw/skyeye-service.yml dist/skyeye-windows-amd64/skyeye-service.yml - cp winsw.exe dist/skyeye-windows-amd64/skyeye-scaler-service.exe - cp init/winsw/skyeye-scaler-service.yml dist/skyeye-windows-amd64/skyeye-scaler-service.yml - cd dist - zip -r skyeye-windows-amd64.zip skyeye-windows-amd64 + run: make dist-windows - name: Upload artifact if: startsWith(github.ref, 'refs/tags/') uses: actions/upload-artifact@v4 @@ -182,6 +169,53 @@ jobs: path: dist/skyeye-windows-amd64.zip if-no-files-found: error overwrite: true + build-windows-amd64-vulkan: + name: Build on Windows AMD64 (Vulkan) + runs-on: windows-latest + permissions: + contents: read + env: + GOROOT: /ucrt64/lib/go + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + install: | + base-devel + git + mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-toolchain + mingw-w64-ucrt-x86_64-opus + mingw-w64-ucrt-x86_64-libsoxr + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-go + mingw-w64-ucrt-x86_64-curl + mingw-w64-ucrt-x86_64-vulkan-headers + mingw-w64-ucrt-x86_64-vulkan-loader + mingw-w64-ucrt-x86_64-shaderc + mingw-w64-ucrt-x86_64-spirv-tools + zip + - name: Build whisper.cpp + uses: ./.github/actions/build-whisper + with: + os: windows + shell: msys2 {0} + backend: vulkan + - name: Create dist + shell: msys2 {0} + run: make dist-windows-vulkan + - name: Upload artifact + if: startsWith(github.ref, 'refs/tags/') + uses: actions/upload-artifact@v4 + with: + name: skyeye-windows-amd64-vulkan.zip + path: dist/skyeye-windows-amd64-vulkan.zip + if-no-files-found: error + overwrite: true build-image: name: Build container image if: "!startsWith(github.ref, 'refs/tags/')" @@ -205,7 +239,9 @@ jobs: - lint - test - build-linux-amd64 + - build-linux-amd64-vulkan - build-windows-amd64 + - build-windows-amd64-vulkan - build-macos-arm64 runs-on: ubuntu-latest steps: @@ -222,8 +258,10 @@ jobs: with: files: | dist/skyeye-linux-amd64.tar.gz + dist/skyeye-linux-amd64-vulkan.tar.gz dist/skyeye-macos-arm64.tar.gz dist/skyeye-windows-amd64.zip + dist/skyeye-windows-amd64-vulkan.zip token: ${{ secrets.RELEASE_TOKEN }} push-images: name: Build and push container images @@ -256,3 +294,13 @@ jobs: image-name: ${{ github.repository }}-scaler target: skyeye-scaler skyeye-version: ${{ env.GITHUB_REF_NAME }} + - name: Build and push Vulkan image + uses: ./.github/actions/build-container + with: + registry-username: ${{ github.actor }} + registry-password: ${{ secrets.GITHUB_TOKEN }} + image-name: ${{ github.repository }} + target: skyeye + tag-suffix: '-vulkan' + skyeye-version: ${{ env.GITHUB_REF_NAME }} + backend: vulkan diff --git a/Dockerfile b/Dockerfile index 4ec21f9d..33ce48bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ ARG SKYEYE_VERSION +ARG WHISPER_CPP_BACKEND=cpu + FROM golang:1.26 AS builder +ARG WHISPER_CPP_BACKEND RUN apt-get update && apt-get install -y \ git \ make \ @@ -8,25 +11,31 @@ RUN apt-get update && apt-get install -y \ gcc \ libopus-dev \ libsoxr-dev \ + && if [ "$WHISPER_CPP_BACKEND" = "vulkan" ]; then \ + apt-get install -y libvulkan-dev glslc; \ + fi \ && rm -rf /var/lib/apt/lists/* WORKDIR /skyeye COPY third_party third_party COPY Makefile Makefile -RUN make whisper -COPY go.mod go.mod -COPY go.sum go.sum +RUN make whisper WHISPER_CPP_BACKEND=$WHISPER_CPP_BACKEND +COPY go.mod go.sum ./ RUN go mod download -x COPY cmd cmd COPY internal internal COPY pkg pkg -RUN make skyeye +RUN make skyeye WHISPER_CPP_BACKEND=$WHISPER_CPP_BACKEND RUN make skyeye-scaler FROM debian:bookworm-slim AS base +ARG WHISPER_CPP_BACKEND RUN apt-get update && apt-get install -y \ ca-certificates \ libopus0 \ libsoxr0 \ + && if [ "$WHISPER_CPP_BACKEND" = "vulkan" ]; then \ + apt-get install -y libvulkan1 mesa-vulkan-drivers; \ + fi \ && rm -rf /var/lib/apt/lists/* FROM base AS skyeye diff --git a/Makefile b/Makefile index 94351e29..0438d933 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,12 @@ SKYEYE_BIN = skyeye SKYEYE_SCALER_BIN = skyeye-scaler WHISPER_CPP_PATH = third_party/whisper.cpp -WHISPER_CPP_BUILD_DIR = $(WHISPER_CPP_PATH)/build_go +WHISPER_CPP_BACKEND ?= cpu +WHISPER_CPP_BUILD_DIR_SUFFIX = +ifneq ($(WHISPER_CPP_BACKEND),cpu) + WHISPER_CPP_BUILD_DIR_SUFFIX = _$(WHISPER_CPP_BACKEND) +endif +WHISPER_CPP_BUILD_DIR = $(WHISPER_CPP_PATH)/build_go$(WHISPER_CPP_BUILD_DIR_SUFFIX) LIBWHISPER_PATH = $(WHISPER_CPP_BUILD_DIR)/src/libwhisper.a WHISPER_H_PATH = $(WHISPER_CPP_PATH)/include/whisper.h WHISPER_CPP_REPO = https://github.com/ggml-org/whisper.cpp.git @@ -36,6 +41,7 @@ GOBUILDVARS = GOARCH=$(GOARCH) ABS_WHISPER_CPP_PATH = $(abspath $(WHISPER_CPP_PATH)) ABS_WHISPER_CPP_BUILD_DIR = $(abspath $(WHISPER_CPP_BUILD_DIR)) LIBRARY_PATHS = $(ABS_WHISPER_CPP_BUILD_DIR)/src:$(ABS_WHISPER_CPP_BUILD_DIR)/ggml/src +CGO_LDFLAGS = BUILD_VARS = CGO_ENABLED=1 \ C_INCLUDE_PATH="$(ABS_WHISPER_CPP_PATH)/ggml/include:$(ABS_WHISPER_CPP_PATH)/include" \ LIBRARY_PATH="$(LIBRARY_PATHS)" @@ -55,7 +61,7 @@ CXX=$(shell brew --prefix llvm)/bin/clang++ BUILD_VARS += CC=$(CC) CXX=$(CXX) LIBRARY_PATHS := $(LIBRARY_PATHS):$(ABS_WHISPER_CPP_BUILD_DIR)/ggml/src/ggml-metal:$(ABS_WHISPER_CPP_BUILD_DIR)/ggml/src/ggml-blas # Link OpenMP runtime for ggml-cpu on macOS (Go bindings only specify -fopenmp on Linux) -BUILD_VARS += CGO_LDFLAGS=-fopenmp +CGO_LDFLAGS += -fopenmp WHISPER_CPP_CMAKE_ARGS = -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) \ -DCMAKE_C_FLAGS=-Wno-elaborated-enum-base -DCMAKE_CXX_FLAGS=-Wno-elaborated-enum-base endif @@ -78,13 +84,49 @@ EXTLDFLAGS = $(shell pkg-config $(LIBRARIES) --libs --static) LDFLAGS += -linkmode external -extldflags "$(EXTLDFLAGS) -lgomp -static" endif +# Vulkan backend settings +ifeq ($(WHISPER_CPP_BACKEND),vulkan) + WHISPER_CPP_CMAKE_ARGS += -DGGML_VULKAN=ON + LIBRARY_PATHS := $(LIBRARY_PATHS):$(ABS_WHISPER_CPP_BUILD_DIR)/ggml/src/ggml-vulkan + BUILD_VARS := $(filter-out LIBRARY_PATH=%,$(BUILD_VARS)) LIBRARY_PATH="$(LIBRARY_PATHS)" + ifeq ($(OS_DISTRIBUTION),Windows) + CGO_LDFLAGS += -lggml-vulkan -lvulkan-1 + else + CGO_LDFLAGS += -lggml-vulkan -lvulkan + endif +endif + +ifneq ($(strip $(CGO_LDFLAGS)),) +BUILD_VARS += CGO_LDFLAGS='$(CGO_LDFLAGS)' +endif BUILD_VARS += LDFLAGS='$(LDFLAGS)' BUILD_FLAGS += -ldflags '$(LDFLAGS)' GO := $(GOBUILDVARS) $(GO) +# CI distribution variables +DIST_BACKEND_SUFFIX = +ifneq ($(WHISPER_CPP_BACKEND),cpu) + DIST_BACKEND_SUFFIX = -$(WHISPER_CPP_BACKEND) +endif +ifeq ($(OS_DISTRIBUTION),macOS) + DIST_OS = macos +else ifeq ($(OS_DISTRIBUTION),Windows) + DIST_OS = windows +else + DIST_OS = linux +endif +DIST_NAME = skyeye-$(DIST_OS)-$(GOARCH)$(DIST_BACKEND_SUFFIX) +DIST_DIR = dist/$(DIST_NAME) + .PHONY: default default: $(SKYEYE_BIN) +.PHONY: whisper-vulkan skyeye-vulkan +whisper-vulkan: + $(MAKE) WHISPER_CPP_BACKEND=vulkan whisper +skyeye-vulkan: + $(MAKE) WHISPER_CPP_BACKEND=vulkan $(SKYEYE_BIN) + .PHONY: install-msys2-dependencies install-msys2-dependencies: pacman -Syu --needed \ @@ -94,7 +136,11 @@ install-msys2-dependencies: $(MINGW_PACKAGE_PREFIX)-toolchain \ $(MINGW_PACKAGE_PREFIX)-go \ $(MINGW_PACKAGE_PREFIX)-opus \ - $(MINGW_PACKAGE_PREFIX)-libsoxr + $(MINGW_PACKAGE_PREFIX)-libsoxr \ + $(MINGW_PACKAGE_PREFIX)-vulkan-headers \ + $(MINGW_PACKAGE_PREFIX)-vulkan-loader \ + $(MINGW_PACKAGE_PREFIX)-shaderc \ + $(MINGW_PACKAGE_PREFIX)-spirv-tools .PHONY: install-arch-linux-dependencies install-arch-linux-dependencies: @@ -104,7 +150,10 @@ install-arch-linux-dependencies: cmake \ go \ opus \ - libsoxr + libsoxr \ + vulkan-headers \ + vulkan-icd-loader \ + shaderc .PHONY: install-debian-dependencies install-debian-dependencies: @@ -117,7 +166,9 @@ install-debian-dependencies: libopus-dev \ libopus0 \ libsoxr-dev \ - libsoxr0 + libsoxr0 \ + libvulkan-dev \ + glslc .PHONY: install-fedora-dependencies install-fedora-dependencies: @@ -130,7 +181,10 @@ install-fedora-dependencies: opus-devel \ opus \ soxr-devel \ - sox + sox \ + vulkan-headers \ + vulkan-loader-devel \ + glslc .PHONY: install-macos-dependencies install-macos-dependencies: @@ -207,11 +261,62 @@ fix: generate format: find . -name '*.go' -exec gofmt -s -w {} ';' +.PHONY: dist-linux dist-macos +dist-linux dist-macos: $(SKYEYE_BIN) $(SKYEYE_SCALER_BIN) + mkdir -p $(DIST_DIR)/docs/ + cp $(SKYEYE_BIN) $(DIST_DIR)/$(SKYEYE_BIN) + cp $(SKYEYE_SCALER_BIN) $(DIST_DIR)/$(SKYEYE_SCALER_BIN) + chmod +x $(DIST_DIR)/$(SKYEYE_BIN) + chmod +x $(DIST_DIR)/$(SKYEYE_SCALER_BIN) + cp README.md $(DIST_DIR)/README.md + cp LICENSE $(DIST_DIR)/LICENSE + cp config.yaml $(DIST_DIR)/config.yaml + cp docs/*.md $(DIST_DIR)/docs/ + tar -czf dist/$(DIST_NAME).tar.gz -C dist $(DIST_NAME) + +.PHONY: dist-linux-vulkan +dist-linux-vulkan: + $(MAKE) WHISPER_CPP_BACKEND=vulkan dist-linux + +WINSW_VERSION = 2.12.0 +WINSW_SHA256 = 05b82d46ad331cc16bdc00de5c6332c1ef818df8ceefcd49c726553209b3a0da + +winsw.exe: + curl -fsL https://github.com/winsw/winsw/releases/download/v$(WINSW_VERSION)/WinSW-x64.exe -o winsw.exe + echo "$(WINSW_SHA256) winsw.exe" | sha256sum -c - || \ + (echo "ERROR: winsw.exe hash verification failed - expected SHA256 $(WINSW_SHA256)" && rm -f winsw.exe && exit 1) + +.PHONY: dist-windows +dist-windows: $(SKYEYE_BIN) $(SKYEYE_SCALER_BIN) winsw.exe + mkdir -p $(DIST_DIR)/docs/ + cp $(SKYEYE_BIN) $(DIST_DIR)/$(SKYEYE_BIN) + cp $(SKYEYE_SCALER_BIN) $(DIST_DIR)/$(SKYEYE_SCALER_BIN) + cp README.md $(DIST_DIR)/README.md + cp LICENSE $(DIST_DIR)/LICENSE + cp config.yaml $(DIST_DIR)/config.yaml + cp docs/*.md $(DIST_DIR)/docs/ + cp winsw.exe $(DIST_DIR)/skyeye-service.exe + cp init/winsw/skyeye-service.yml $(DIST_DIR)/skyeye-service.yml + cp winsw.exe $(DIST_DIR)/skyeye-scaler-service.exe + cp init/winsw/skyeye-scaler-service.yml $(DIST_DIR)/skyeye-scaler-service.yml + cd dist && zip -r $(DIST_NAME).zip $(DIST_NAME) + +.PHONY: dist-windows-vulkan +dist-windows-vulkan: + $(MAKE) WHISPER_CPP_BACKEND=vulkan dist-windows + +.PHONY: ci-lint +ci-lint: lint vet fix format + $(GO) mod tidy + git diff --exit-code + .PHONY: mostlyclean mostlyclean: rm -f "$(SKYEYE_BIN)" "$(SKYEYE_SCALER_BIN)" + rm -rf dist/ find . -type f -name 'mock_*.go' -delete .PHONY: clean clean: mostlyclean rm -rf "$(WHISPER_CPP_PATH)" + rm -f winsw.exe