#!/usr/bin/env bash
# Vulkan build for vk-renderer + RHI examples (Linux).
# Usage: build_linux.sh [example] [-r] [-c]
# example Name under src/backend/conformance/ (e.g. triangle). Builds
# src/backend/conformance/<example> with core + backend only.
# Default (no example): full renderer build.
# -r Release build (default is Debug)
# -c Clean build (delete everything first)
# All build artifacts and output live under build/.
# The binary must run from the repo root (shader paths are repo-relative).
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
build_type=debug
clean_build=0
example=""
for arg in "$@"; do
case "$arg" in
-r|release) build_type=release ;;
-c) clean_build=1 ;;
*) example="$arg" ;;
esac
done
if [ -n "$example" ]; then
echo "[$build_type vulkan build: $example]"
else
echo "[$build_type build]"
fi
ROOT="$SCRIPT_DIR"
BUILD="$ROOT/build"
OBJ="$BUILD/obj"
DEPS="$ROOT/build/_deps"
SLANG_DIR="$DEPS/slang-src"
# --- Example mode (positional arg): build src/backend/conformance/<example>
# --- with core + backend only, instead of the full renderer ------------------
EXAMPLES="$ROOT/src/backend/conformance"
if [ -n "$example" ]; then
EXAMPLE_SRC="$EXAMPLES/$example/$example.cpp"
if [ ! -f "$EXAMPLE_SRC" ]; then
echo "[error: no such example: $example (expected $EXAMPLE_SRC)]" >&2
exit 1
fi
OBJ="$ROOT/build/obj-ex-$example"
fi
# --- Vulkan SDK ----------------------------------------------------------
VULKAN_CFLAGS=""
VULKAN_LDFLAGS="-lvulkan"
if command -v pkg-config >/dev/null 2>&1; then
if pkg-config --exists vulkan; then
VULKAN_CFLAGS="$(pkg-config --cflags vulkan)"
VULKAN_LDFLAGS="$(pkg-config --libs vulkan)"
echo "[vulkan] using pkg-config"
fi
fi
if [ -n "$VULKAN_SDK" ]; then
echo "[vulkan] VULKAN_SDK detected, overriding system paths"
VULKAN_CFLAGS="-I$VULKAN_SDK/include"
VULKAN_LDFLAGS="-L$VULKAN_SDK/lib -lvulkan"
fi
# --- Download Slang (binary SDK, shared across targets) ----------------------
if [ ! -f "$SLANG_DIR/include/slang.h" ]; then
echo "[downloading slang]"
mkdir -p "$SLANG_DIR"
SLANG_URL="https://github.com/shader-slang/slang/releases/download/v2026.17.1/slang-2026.17.1-linux-x86_64.tar.gz"
curl -sL "$SLANG_URL" -o "$DEPS/slang.tar.gz"
tar -xzf "$DEPS/slang.tar.gz" -C "$SLANG_DIR" --strip-components=1
rm "$DEPS/slang.tar.gz"
else
echo "[slang already downloaded]"
fi
SLANG_INCLUDE="-I$SLANG_DIR/include"
SLANG_LIB="$SLANG_DIR/lib/libslang.so"
# --- Clean -------------------------------------------------------------------
if [ "$clean_build" = "1" ]; then
if [ -n "$example" ]; then
echo "[cleaning example build: $example]"
rm -rf "$OBJ"
else
echo "[cleaning build]"
rm -rf "$BUILD"
fi
fi
mkdir -p "$OBJ" "$DEPS"
# --- glfw (static, shared across targets) ------------------------------------
if [ ! -f "$DEPS/libglfw3.a" ]; then
echo "[building glfw]"
mkdir -p "$DEPS/glfw"
cmake -G Ninja -S "$ROOT/third-party/glfw" -B "$DEPS/glfw" \
-DGLFW_BUILD_DOCS=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 || exit 1
cmake --build "$DEPS/glfw" >/dev/null 2>&1 || exit 1
cp "$DEPS/glfw/src/libglfw3.a" "$DEPS/libglfw3.a"
else
echo "[glfw already built]"
fi
# --- KTX-Software (examples don't link it: skip) -----------------------------
if [ -z "$example" ]; then
if [ ! -f "$BUILD/libktx.a" ]; then
echo "[building ktx]"
mkdir -p "$DEPS/ktx"
cmake -G Ninja -S "$ROOT/third-party/KTX-Software" -B "$DEPS/ktx" \
-DKTX_FEATURE_TESTS=OFF -DKTX_FEATURE_TOOLS=OFF -DKTX_FEATURE_DOC=OFF \
-DKTX_FEATURE_GL_UPLOAD=OFF -DKTX_FEATURE_VK_UPLOAD=OFF \
-DKTX_FEATURE_LOADTEST_APPS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo >/dev/null 2>&1 || exit 1
cmake --build "$DEPS/ktx" >/dev/null 2>&1 || exit 1
cp "$DEPS/ktx/lib/libktx.a" "$BUILD/libktx.a"
cp "$DEPS/ktx/lib/libktx_read.a" "$BUILD/libktx_read.a"
else
echo "[ktx already built]"
fi
fi
# --- Compile flags -------------------------------------------------------
#CXX="clang++"
CXX="g++"
CXX_COMMON="-std=c++20 -fno-rtti -D_USE_MATH_DEFINES -DVK_NO_PROTOTYPES -DRHI_BACKEND_VULKAN -DENABLE_EDITOR -DFMT_USE_CONSTEXPR=0 -DFMT_ENFORCE_COMPILE_STRING=0"
CXX_INCLUDES="-I$ROOT/src -I$ROOT/third-party -I$ROOT/third-party/glfw/include -I$ROOT/third-party/VMA/include -I$ROOT/third-party/plog/include -I$ROOT/third-party/imgui -I$ROOT/third-party/imgui/backends -I$ROOT/third-party/rtm/includes -I$ROOT/third-party/KTX-Software/include $SLANG_INCLUDE $VULKAN_CFLAGS"
if [ "$build_type" = "debug" ]; then
CXX_FLAGS="-g -O0 -D_DEBUG -DIMGUI_IMPL_VULKAN_USE_VOLK $CXX_COMMON $CXX_INCLUDES"
else
CXX_FLAGS="-g -O2 -DNDEBUG -DIMGUI_IMPL_VULKAN_USE_VOLK $CXX_COMMON $CXX_INCLUDES"
fi
LD_FLAGS="$VULKAN_LDFLAGS -lX11 -lXrandr -lXinerama -lXcursor -lXi -lpthread -ldl"
# --- Source files (matches CMakeLists.txt; VOLK_IMPLEMENTATION lives in
# --- renderer.cpp / the example .cpp, so third-party/volk.c is intentionally
# --- NOT compiled) -----------------------------------------------------------
SRC="$ROOT/src"
TP="$ROOT/third-party"
CORE_SRC="\
$SRC/core/arena.cpp \
$SRC/core/platform.cpp \
$SRC/core/math_rtm.cpp \
$SRC/core/logger.cpp"
BACKEND_SRC="\
$SRC/backend/vulkan/context.cpp \
$SRC/backend/vulkan/swapchain.cpp \
$SRC/backend/vulkan/pipeline.cpp \
$SRC/backend/vulkan/descriptors.cpp \
$SRC/backend/vulkan/debug.cpp \
$SRC/backend/vulkan/query_pool.cpp \
$SRC/backend/vulkan/cmd_pool.cpp \
$SRC/backend/vulkan/cmd_buffer.cpp \
$SRC/backend/vulkan/allocator.cpp \
$SRC/backend/vulkan/queue.cpp \
$SRC/backend/vulkan/buffer.cpp \
$SRC/backend/vulkan/image.cpp \
$SRC/backend/vulkan/sampler.cpp \
$SRC/backend/vulkan/shader.cpp \
$SRC/backend/vulkan/accel_struct.cpp \
$SRC/backend/vulkan/vk_conversion.cpp \
$SRC/backend/vulkan/rhi_helpers.cpp"
RENDERER_SRC="\
$SRC/renderer.cpp \
$SRC/quick_submit.cpp \
$SRC/pipelines.cpp \
$SRC/passes/fg.cpp \
$SRC/asset/asset.cpp \
$SRC/asset/texture.cpp \
$SRC/asset/gltf.cpp \
$TP/mikktspace.c \
$SRC/scene/scene.cpp \
$SRC/scene/ibl.cpp \
$SRC/passes/ssr/ssr.cpp \
$SRC/passes/depth_pre/depth_normal.cpp \
$SRC/passes/dispatch_mdi/build_indirect.cpp \
$SRC/passes/forward/forward.cpp \
$SRC/passes/fwd_cpu/fwd_cpu.cpp \
$SRC/passes/shadow/shadow.cpp \
$SRC/passes/grid/grid.cpp \
$SRC/passes/id_blit/id_blit.cpp \
$SRC/passes/fullscreen/fullscreen.cpp \
$SRC/passes/cluster/cluster_bounds.cpp \
$SRC/passes/cluster/light_cull.cpp \
$SRC/passes/hiz/hiz.cpp \
$SRC/passes/imgui/imgui.cpp \
$SRC/passes/skybox/skybox.cpp \
$SRC/passes/rt_build.cpp \
$SRC/passes/ddgi/ddgi_trace.cpp \
$SRC/passes/ddgi/ddgi_update.cpp \
$SRC/passes/ddgi/ddgi_classify.cpp \
$SRC/passes/ddgi/ddgi_relocate.cpp \
$SRC/passes/ddgi/ddgi_probes_dbg.cpp \
$SRC/passes/debug/debug_lines.cpp \
$SRC/editor/editor.cpp \
$SRC/editor/debug_draw.cpp \
$SRC/editor/frame.cpp \
$SRC/editor/gizmo.cpp \
$SRC/editor/style.cpp \
$SRC/editor/viewport.cpp \
$SRC/editor/log_viewer.cpp \
$SRC/editor/texture_viewer.cpp \
$SRC/editor/targets.cpp \
$SRC/editor/timings.cpp \
$SRC/editor/hierarchy.cpp \
$SRC/editor/inspector.cpp \
$SRC/backend/imgui_backend.cpp \
$TP/imgui/imgui.cpp \
$TP/imgui/imgui_draw.cpp \
$TP/imgui/imgui_tables.cpp \
$TP/imgui/imgui_widgets.cpp \
$TP/imgui/imgui_demo.cpp \
$TP/imgui/backends/imgui_impl_glfw.cpp"
# --- Compile -------------------------------------------------------------
echo "[compiling]"
if [ -n "$example" ]; then
for src in $EXAMPLE_SRC $CORE_SRC $BACKEND_SRC; do
objname=$(basename "$src" | sed 's/\.cpp$/.o/')
$CXX -c $CXX_FLAGS "$src" -o "$OBJ/$objname"
done
else
for src in $CORE_SRC $BACKEND_SRC $RENDERER_SRC; do
objname=$(basename "$src" | sed 's/\.cpp$/.o/;s/\.c$/.o/')
$CXX -c $CXX_FLAGS "$src" -o "$OBJ/$objname"
done
# imgui.o collision: src/passes/imgui/imgui.cpp and third-party/imgui/imgui.cpp
# both emit imgui.o in this flat dir: rebuild with distinct names.
$CXX -c $CXX_FLAGS "$SRC/passes/imgui/imgui.cpp" -o "$OBJ/imgui_pass.o"
$CXX -c $CXX_FLAGS "$TP/imgui/imgui.cpp" -o "$OBJ/imgui_lib.o"
rm -f "$OBJ/imgui.o"
# stale objs from renamed/removed sources (link uses *.o wildcard)
rm -f "$OBJ/descriptor_pool.o" "$OBJ/descriptor_set.o" "$OBJ/gpu_resource.o" \
"$OBJ/cmd_buffers.o" "$OBJ/submission_queue.o" "$OBJ/render_target.o" \
"$OBJ/renderpass_io.o" "$OBJ/imgui_impl_vulkan.o" \
"$OBJ"/ui_*.o "$OBJ"/pass_*.o
fi
# --- Link -------------------------------------------------------------------
echo "[linking]"
if [ -n "$example" ]; then
OUT="$BUILD/vk-$example"
$CXX "$OBJ"/*.o \
"$DEPS/libglfw3.a" \
"$SLANG_LIB" \
$LD_FLAGS \
-o "$OUT"
else
OUT="$BUILD/vk-renderer"
$CXX "$OBJ"/*.o \
"$BUILD/libglfw3.a" "$BUILD/libktx.a" "$BUILD/libktx_read.a" \
"$SLANG_LIB" \
$LD_FLAGS \
-o "$OUT"
fi
echo "[done: $OUT]"
if [ -n "$example" ]; then
echo "[run from the repo root: $OUT]"
fi