Skip to content

Building

Requires: .NET 10 SDK, a Rust toolchain (via rustup), clang/libclang-dev (for PipeWire's bindgen-based bindings, needed by native/pwshim only — native/x11shim is pure Rust and needs no system dev headers at all), and libpipewire-0.3-dev. On Ubuntu:

sudo apt install dotnet-sdk-10.0 libpipewire-0.3-dev pkg-config clang libclang-dev cmake

Then:

cmake -S . -B build
cmake --build build
# -> build/dist/GlavaSharp (+ libglfw*.so + shaders/ alongside it --
#    see Packaging for turning this into one actual file)

CMakeLists.txt orchestrates two cargo build --release invocations (native/pwshim, native/x11shim) followed by dotnet publish -p:PublishAot=true, statically linking both Rust staticlibs into the final executable via <NativeLibrary>/<DirectPInvoke> in the .csproj.

AVX2+FMA CPU FFT

For a faster CPU FFT on machines you know have AVX2+FMA (most x86_64 CPUs since ~2013), reconfigure with -DGLAVASHARP_AVX2_CPU_FFT=ON before building — 1.2x-1.6x faster (see Benchmarks), at the cost of the resulting binary refusing to run at all on CPUs without AVX2+FMA:

cmake -S . -B build -DGLAVASHARP_AVX2_CPU_FFT=ON
cmake --build build && cmake --build build --target appimage

-DGLAVASHARP_AVX2_CPU_FFT=ON adds -p:IlcInstructionSet=avx2 to the publish command, enabling CpuFft's AVX2+FMA path in the AOT build (see Benchmarks for why this is off by default and what it actually costs). Two things worth knowing if you're touching this option itself: the value has to be avx2 alone, not avx2,fmafma isn't a standalone --instruction-set token this ILC version recognizes (ilc --help lists the valid x64 set; AVX2 already implies FMA in its grouping, confirmed by checking CpuFft.UsingAvx2 on the resulting binary), and a literal comma-containing value hits a separate MSBuild command-line parsing quirk (MSB1006: Property is not valid. Switch: fma) if you do add one without quoting the whole property. And packaging/build-appimage.sh --avx2-cpu-fft's Text file busy if mksquashfs can't write the output file isn't a bug in the script — it means the AppImage it's trying to overwrite is currently FUSE-mounted/running; close that first.

The appimage target names its output GlavaSharp-x86_64-avx2.AppImage instead of the plain name when this is on, so the two artifacts can't be mixed up — see Packaging.

Cleaning

To clean everything, including dotnet's obj/bin and both crates' target/ directories (not just CMake's own build/ directory):

cmake --build build --target clean-all

Building each piece independently

# Rust shims only
./native/pwshim/build.sh
./native/x11shim/build.sh

# .NET project only (plain build, no AOT, doesn't need either Rust lib at all)
dotnet build GlavaSharp.slnx

Note: a plain dotnet build/dotnet run (no PublishAot) can't actually run the app end-to-end — PipeWireNative/X11Native's LibraryImport calls need the statically-linked AOT build to resolve; without it they'll throw DllNotFoundException trying to dlopen a .so that doesn't exist. Use it for editing/compiling C# quickly; use the full cmake --build build flow to actually run GlavaSharp.