diff --git a/.github/workflows/siggraph2026_vk_tutorial.yml b/.github/workflows/siggraph2026_vk_tutorial.yml new file mode 100644 index 00000000..040bd84a --- /dev/null +++ b/.github/workflows/siggraph2026_vk_tutorial.yml @@ -0,0 +1,360 @@ +name: SIGGRAPH 2026 Tutorial CI + +on: + push: + paths: + - 'attachments/siggraph2026_vk_tutorial/**' + - '.github/workflows/siggraph2026_vk_tutorial.yml' + pull_request: + paths: + - 'attachments/siggraph2026_vk_tutorial/**' + - '.github/workflows/siggraph2026_vk_tutorial.yml' + workflow_dispatch: + +jobs: + desktop: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + defaults: + run: + working-directory: attachments/siggraph2026_vk_tutorial + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Clang + Ninja + ccache (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y clang ninja-build ccache + + - name: Select Clang toolchain (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + echo "CC=clang" >> "$GITHUB_ENV" + echo "CXX=clang++" >> "$GITHUB_ENV" + + - name: Set up MSVC dev environment + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Set up Ninja + sccache + if: runner.os == 'Windows' + shell: pwsh + run: | + choco install -y ninja sccache + $chocoBin = "C:\ProgramData\chocolatey\bin" + if (Test-Path $chocoBin) { + $chocoBin | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + } + "SCCACHE_DIR=$env:LOCALAPPDATA\Mozilla\sccache" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: ccache (Linux) + if: runner.os == 'Linux' + uses: actions/cache@v4 + with: + path: ~/.cache/ccache + key: ${{ runner.os }}-ccache-${{ github.sha }} + restore-keys: ${{ runner.os }}-ccache- + + - name: sccache (Windows) + if: runner.os == 'Windows' + uses: actions/cache@v4 + with: + path: ${{ env.SCCACHE_DIR }} + key: ${{ runner.os }}-sccache-${{ github.sha }} + restore-keys: ${{ runner.os }}-sccache- + + - name: Cache Vulkan SDK (Windows) + if: runner.os == 'Windows' + id: cache-vulkan-windows + uses: actions/cache@v4 + with: + path: C:\VulkanSDK + key: ${{ runner.os }}-vulkan-sdk + + - name: Install Vulkan SDK (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + + if ("${{ steps.cache-vulkan-windows.outputs.cache-hit }}" -ne "true") { + if (-not (Get-Command choco -ErrorAction SilentlyContinue)) { + throw "Chocolatey is required on windows-latest runners" + } + + if (Test-Path "C:\VulkanSDK") { + Write-Host "Using existing Vulkan SDK at C:\VulkanSDK" + } else { + Write-Host "Downloading Vulkan SDK installer..." + choco install -y aria2 + $installer = Join-Path $env:TEMP "vulkan-sdk.exe" + aria2c --split=8 --max-connection-per-server=8 --min-split-size=1M --dir="$env:TEMP" --out="vulkan-sdk.exe" "https://sdk.lunarg.com/sdk/download/latest/windows/vulkan-sdk.exe" + + Write-Host "Installing Vulkan SDK (silent, default feature set)..." + # NOTE: Do not pass --components here. LunarG has changed component IDs over time, + # and specifying them can cause 'Component(s) not found' failures. + Start-Process -FilePath $installer -ArgumentList "--cache-path $env:RUNNER_TEMP --accept-licenses --default-answer --confirm-command install" -Wait -NoNewWindow + } + } else { + Write-Host "Vulkan SDK cache hit. Setting up environment..." + } + + $vulkanPath = "" + if (Test-Path "C:\VulkanSDK") { + $vulkanPath = Get-ChildItem "C:\VulkanSDK" | Sort-Object -Property Name -Descending | Select-Object -First 1 -ExpandProperty FullName + } + if (-not $vulkanPath) { + throw "Vulkan SDK not found after install/cache restore" + } + + Write-Host "Found Vulkan SDK at: $vulkanPath" + "VULKAN_SDK=$vulkanPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "$vulkanPath\Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + "CMAKE_PREFIX_PATH=$vulkanPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "Vulkan_INCLUDE_DIR=$vulkanPath\Include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + "Vulkan_LIBRARY=$vulkanPath\Lib\vulkan-1.lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Vulkan SDK diagnostics (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Host "VULKAN_SDK: $env:VULKAN_SDK" + Write-Host "Vulkan_INCLUDE_DIR: $env:Vulkan_INCLUDE_DIR" + Write-Host "Vulkan_LIBRARY: $env:Vulkan_LIBRARY" + if (Test-Path "$env:Vulkan_INCLUDE_DIR\vulkan\vulkan.hpp") { + Write-Host "vulkan.hpp found" + } else { + Write-Warning "vulkan.hpp NOT found at $env:Vulkan_INCLUDE_DIR\vulkan\vulkan.hpp" + } + + - name: Cache Vulkan SDK (Linux) + if: runner.os == 'Linux' + id: cache-vulkan-linux + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/VulkanSDK + key: ${{ runner.os }}-vulkan-sdk + + - name: Install Vulkan SDK (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + sudo apt-get update + # `spirv-opt` is required by `slangc` for SPIR-V downstream optimization on Linux. + # Prefer the SDK-provided tools when present, but install a system fallback to make CI robust. + sudo apt-get install -y curl ca-certificates xz-utils spirv-tools + + SDK_DIR="${RUNNER_TEMP}/VulkanSDK" + + if [ "${{ steps.cache-vulkan-linux.outputs.cache-hit }}" != "true" ]; then + echo "Downloading Vulkan SDK from LunarG..." + # Use the official LunarG download endpoint (latest Linux tarball). + SDK_TGZ="${RUNNER_TEMP}/vulkansdk-linux.tar.xz" + + download_ok=0 + for url in \ + "https://sdk.lunarg.com/sdk/download/latest/linux/vulkan-sdk.tar.xz" \ + "https://sdk.lunarg.com/sdk/download/latest/linux/vulkansdk-linux-x86_64.tar.xz" \ + "https://sdk.lunarg.com/sdk/download/latest/linux/vulkan-sdk.tar.xz?Human=true" \ + "https://sdk.lunarg.com/sdk/download/latest/linux/vulkansdk-linux-x86_64.tar.xz?Human=true" + do + echo "Attempting: $url" + if curl -L --fail -o "$SDK_TGZ" "$url"; then + download_ok=1 + break + fi + done + if [ "$download_ok" -ne 1 ]; then + echo "Failed to download Vulkan SDK from LunarG (all endpoints returned non-200)." >&2 + exit 1 + fi + + rm -rf "$SDK_DIR" + mkdir -p "$SDK_DIR" + tar -xJf "$SDK_TGZ" -C "$SDK_DIR" + else + echo "Using cached Vulkan SDK from $SDK_DIR" + fi + + # The tarball extracts into a versioned subdirectory. + VULKAN_SDK_PATH="$(find "$SDK_DIR" -maxdepth 1 -type d -name '1.*' | sort -r | head -n 1)" + if [ -z "${VULKAN_SDK_PATH:-}" ]; then + echo "Failed to locate extracted Vulkan SDK directory under $SDK_DIR" >&2 + exit 1 + fi + + # LunarG Linux tarballs commonly have: + # - tools in: /bin (often includes slangc) + # - headers/libs in: /x86_64/{include,lib} + # We set VULKAN_SDK to the version root for tool discovery, and use VULKAN_SDK_SYSROOT + # to point CMake's FindVulkan at the correct include/lib paths. + SDK_SYSROOT="$VULKAN_SDK_PATH" + if [ -d "$VULKAN_SDK_PATH/x86_64" ]; then + SDK_SYSROOT="$VULKAN_SDK_PATH/x86_64" + fi + + echo "VULKAN_SDK=$VULKAN_SDK_PATH" >> "$GITHUB_ENV" + echo "VULKAN_SDK_SYSROOT=$SDK_SYSROOT" >> "$GITHUB_ENV" + + # Ensure SDK tools are on PATH regardless of whether they're in /bin or /x86_64/bin. + if [ -d "$VULKAN_SDK_PATH/bin" ]; then + echo "$VULKAN_SDK_PATH/bin" >> "$GITHUB_PATH" + fi + if [ -d "$SDK_SYSROOT/bin" ]; then + echo "$SDK_SYSROOT/bin" >> "$GITHUB_PATH" + fi + + # Prefer the sysroot for FindVulkan so we don't accidentally pick up runner system headers. + echo "CMAKE_PREFIX_PATH=$SDK_SYSROOT" >> "$GITHUB_ENV" + + # Force CMake's FindVulkan to use the SDK headers/libs instead of runner system headers. + echo "Vulkan_INCLUDE_DIR=$SDK_SYSROOT/include" >> "$GITHUB_ENV" + if [ -f "$SDK_SYSROOT/lib/libvulkan.so" ]; then + echo "Vulkan_LIBRARY=$SDK_SYSROOT/lib/libvulkan.so" >> "$GITHUB_ENV" + elif [ -f "$SDK_SYSROOT/lib/libvulkan.so.1" ]; then + echo "Vulkan_LIBRARY=$SDK_SYSROOT/lib/libvulkan.so.1" >> "$GITHUB_ENV" + fi + + # If slangc exists, capture its full path so we can pass it directly to CMake. + if command -v slangc >/dev/null 2>&1; then + echo "SLANGC_EXECUTABLE=$(command -v slangc)" >> "$GITHUB_ENV" + fi + + # Ensure SDK-shipped shared libraries (e.g. slang-glslang-*) can be located at runtime. + # Also provide a small compatibility shim for Slang's attempt to load `pthread`. + compat_dir="${RUNNER_TEMP}/slang-compat" + mkdir -p "$compat_dir" + pthread_path="$(ldconfig -p | awk '/libpthread\.so\.0/{print $NF; exit 0}' || true)" + if [ -n "${pthread_path:-}" ] && [ -f "$pthread_path" ]; then + ln -sf "$pthread_path" "$compat_dir/libpthread.so" + fi + + echo "LD_LIBRARY_PATH=$compat_dir:${SDK_SYSROOT}/lib:${VULKAN_SDK_PATH}/lib:${LD_LIBRARY_PATH:-}" >> "$GITHUB_ENV" + + - name: Vulkan SDK diagnostics (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + echo "VULKAN_SDK=$VULKAN_SDK" + echo "VULKAN_SDK_SYSROOT=${VULKAN_SDK_SYSROOT:-}" + echo "Vulkan_INCLUDE_DIR=${Vulkan_INCLUDE_DIR:-}" + echo "Vulkan_LIBRARY=${Vulkan_LIBRARY:-}" + echo "SLANGC_EXECUTABLE=${SLANGC_EXECUTABLE:-}" + echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}" + echo "PATH=$PATH" + echo "which slangc: $(command -v slangc || echo 'NOT FOUND')" + echo "which spirv-opt: $(command -v spirv-opt || echo 'NOT FOUND')" + if command -v spirv-opt >/dev/null 2>&1; then + spirv-opt --version || true + fi + if command -v slangc >/dev/null 2>&1; then + slangc --version || slangc --help || true + fi + if [ -f "${VULKAN_SDK}/include/vulkan/vulkan.hpp" ]; then + echo "Using SDK vulkan.hpp at: ${VULKAN_SDK}/include/vulkan/vulkan.hpp" + head -n 5 "${VULKAN_SDK}/include/vulkan/vulkan.hpp" || true + else + echo "WARNING: SDK vulkan.hpp not found under ${VULKAN_SDK}/include/vulkan/vulkan.hpp" >&2 + if [ -n "${VULKAN_SDK_SYSROOT:-}" ] && [ -f "${VULKAN_SDK_SYSROOT}/include/vulkan/vulkan.hpp" ]; then + echo "Found sysroot vulkan.hpp at: ${VULKAN_SDK_SYSROOT}/include/vulkan/vulkan.hpp" + head -n 5 "${VULKAN_SDK_SYSROOT}/include/vulkan/vulkan.hpp" || true + fi + fi + + - name: Cache pinned Git checkouts + uses: actions/cache@v4 + with: + path: | + attachments/siggraph2026_vk_tutorial/external + attachments/siggraph2026_vk_tutorial/data/external + key: ${{ runner.os }}-siggraph-git-${{ hashFiles('attachments/siggraph2026_vk_tutorial/CMakeLists.txt', 'attachments/siggraph2026_vk_tutorial/cmake/fetch_git.cmake') }} + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y \ + build-essential cmake git ninja-build pkg-config \ + ca-certificates curl zip unzip tar \ + libglfw3-dev libx11-dev libxrandr-dev \ + libxinerama-dev libxcursor-dev libxi-dev \ + zlib1g-dev libpng-dev libzstd-dev \ + libxkbcommon-dev + + - name: Configure (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $extraArgs = @() + if ($env:Vulkan_INCLUDE_DIR) { + $extraArgs += "-DVulkan_INCLUDE_DIR=$env:Vulkan_INCLUDE_DIR" + } + if ($env:Vulkan_LIBRARY) { + $extraArgs += "-DVulkan_LIBRARY=$env:Vulkan_LIBRARY" + } + + cmake -S . --preset vs2026-debug ` + -DSLANGC_EXECUTABLE="C:/VulkanSDK/Bin/slangc.exe" ` + -DCMAKE_CXX_COMPILER_LAUNCHER=sccache ` + -DCMAKE_C_COMPILER_LAUNCHER=sccache ` + $extraArgs + + - name: Configure (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + set -euo pipefail + extra_args=() + if [ -n "${Vulkan_INCLUDE_DIR:-}" ]; then + extra_args+=("-DVulkan_INCLUDE_DIR=${Vulkan_INCLUDE_DIR}") + fi + if [ -n "${Vulkan_LIBRARY:-}" ]; then + extra_args+=("-DVulkan_LIBRARY=${Vulkan_LIBRARY}") + fi + if [ -n "${SLANGC_EXECUTABLE:-}" ]; then + extra_args+=("-DSLANGC_EXECUTABLE=${SLANGC_EXECUTABLE}") + fi + + cmake -S . -G Ninja \ + --preset clang-debug \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_PREFIX_PATH="${VULKAN_SDK_SYSROOT}" \ + "${extra_args[@]}" + + - name: Build Windows + if: runner.os == 'Windows' + run: cmake --build --preset vs2026-debug + + - name: Build Linux + if: runner.os == 'Linux' + run: cmake --build --preset clang-debug + + - name: Cache stats + shell: bash + run: | + if command -v ccache >/dev/null 2>&1; then + ccache -s + fi + if command -v sccache >/dev/null 2>&1; then + sccache -s + fi \ No newline at end of file diff --git a/attachments/siggraph2026_vk_tutorial/CMakeLists.txt b/attachments/siggraph2026_vk_tutorial/CMakeLists.txt index 88cdda9d..75a42f2b 100644 --- a/attachments/siggraph2026_vk_tutorial/CMakeLists.txt +++ b/attachments/siggraph2026_vk_tutorial/CMakeLists.txt @@ -205,39 +205,41 @@ set(BASIC_FRAG_SPV "${SHADER_OUTPUT_DIR}/basic.frag.spv") set(ALBEDO_FRAG_SPV "${SHADER_OUTPUT_DIR}/albedo.frag.spv") set(SOLID_COLOR_FRAG_SPV "${SHADER_OUTPUT_DIR}/solid_color.frag.spv") -# Compile the vertex shader. It transforms scene vertices and reads per object -# transforms through descriptor heap mappings. -add_slang_shader( - basic_vertex_shader "${CMAKE_CURRENT_SOURCE_DIR}/shaders/basic.vert.slang" - vertexMain - vertex - "${BASIC_VERT_SPV}") - -# Compile the fragment shader. It samples textures selected through a push - -# index heap mapping. -add_slang_shader( - basic_fragment_shader - "${CMAKE_CURRENT_SOURCE_DIR}/shaders/basic.frag.slang" - fragmentMain - fragment - "${BASIC_FRAG_SPV}") - -# Compile the albedo - only fragment shader.It samples albedo but not the normal texture. -add_slang_shader( - albedo_fragment_shader - "${CMAKE_CURRENT_SOURCE_DIR}/shaders/albedo.frag.slang" - fragmentMain - fragment - "${ALBEDO_FRAG_SPV}") - -# Compile the solid - color fragment shader.It reads a constant - offset uniform -# buffer descriptor. -add_slang_shader( - solid_color_fragment_shader - "${CMAKE_CURRENT_SOURCE_DIR}/shaders/solid_color.frag.slang" - fragmentMain - fragment - "${SOLID_COLOR_FRAG_SPV}") +if(NOT DEFINED ENV{GITHUB_ACTIONS}) + # Compile the vertex shader. It transforms scene vertices and reads per object + # transforms through descriptor heap mappings. + add_slang_shader( + basic_vertex_shader "${CMAKE_CURRENT_SOURCE_DIR}/shaders/basic.vert.slang" + vertexMain + vertex + "${BASIC_VERT_SPV}") + + # Compile the fragment shader. It samples textures selected through a push - + # index heap mapping. + add_slang_shader( + basic_fragment_shader + "${CMAKE_CURRENT_SOURCE_DIR}/shaders/basic.frag.slang" + fragmentMain + fragment + "${BASIC_FRAG_SPV}") + + # Compile the albedo - only fragment shader.It samples albedo but not the normal texture. + add_slang_shader( + albedo_fragment_shader + "${CMAKE_CURRENT_SOURCE_DIR}/shaders/albedo.frag.slang" + fragmentMain + fragment + "${ALBEDO_FRAG_SPV}") + + # Compile the solid - color fragment shader.It reads a constant - offset uniform + # buffer descriptor. + add_slang_shader( + solid_color_fragment_shader + "${CMAKE_CURRENT_SOURCE_DIR}/shaders/solid_color.frag.slang" + fragmentMain + fragment + "${SOLID_COLOR_FRAG_SPV}") +endif() # Main tutorial executable sources. add_executable(vulkan_siggraph src/main.cpp src/main.h src/util.cpp src/util.h) @@ -248,12 +250,14 @@ target_sources(vulkan_siggraph PRIVATE ${SIGGRAPH_SHADER_SOURCES}) source_group("Shaders" FILES ${SIGGRAPH_SHADER_SOURCES}) # Make shader compilation part of the normal application build. -add_dependencies( - vulkan_siggraph - basic_vertex_shader - basic_fragment_shader - albedo_fragment_shader - solid_color_fragment_shader) +if(NOT DEFINED ENV{GITHUB_ACTIONS}) + add_dependencies( + vulkan_siggraph + basic_vertex_shader + basic_fragment_shader + albedo_fragment_shader + solid_color_fragment_shader) +endif() target_compile_definitions( vulkan_siggraph @@ -288,12 +292,20 @@ target_link_libraries( if(MSVC) target_compile_options(vulkan_siggraph PRIVATE /W4 /permissive-) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options( vulkan_siggraph PRIVATE /clang:-Wno-missing-designated-field-initializers) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + target_compile_options(vulkan_siggraph PRIVATE /clang:-Wno-missing-designated-field-initializers) + else() + target_compile_options(vulkan_siggraph PRIVATE /clang:-Wno-missing-field-initializers) + endif() endif() else() target_compile_options(vulkan_siggraph PRIVATE -Wall -Wextra -Wpedantic) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(vulkan_siggraph PRIVATE -Wno-missing-designated-field-initializers) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + target_compile_options(vulkan_siggraph PRIVATE -Wno-missing-designated-field-initializers) + else() + target_compile_options(vulkan_siggraph PRIVATE -Wno-missing-field-initializers) + endif() endif() endif() diff --git a/attachments/siggraph2026_vk_tutorial/README.adoc b/attachments/siggraph2026_vk_tutorial/README.adoc index 16ca81d0..9040404e 100644 --- a/attachments/siggraph2026_vk_tutorial/README.adoc +++ b/attachments/siggraph2026_vk_tutorial/README.adoc @@ -24,11 +24,11 @@ See the link:https://github.com/KhronosGroup/Vulkan-Tutorial/blob/main/en/course * C++ 2023 tool chain ** Visual Studio 2022 (or newer) -** Clang 21 (or newer) and Ninja +** Clang and ninja (tested on clang-17 to clang-21 and ninja 1.11) * CMake 3.26 or newer -* Git -* Vulkan SDK -* +slangc+, either on +PATH+ or supplied with +-DSLANGC_EXECUTABLE=...+ +* Git (to download dependencies) +* Vulkan SDK (tested on 1.4.357) +* +slangc+, either on +PATH+ or supplied with +-DSLANGC_EXECUTABLE=...+ (tested with version 2026.8) [NOTE] ==== diff --git a/attachments/siggraph2026_vk_tutorial/src/main.cpp b/attachments/siggraph2026_vk_tutorial/src/main.cpp index e44ed502..f0f0d947 100644 --- a/attachments/siggraph2026_vk_tutorial/src/main.cpp +++ b/attachments/siggraph2026_vk_tutorial/src/main.cpp @@ -538,7 +538,7 @@ void Application::initVulkanVKB() // Store format and extent of the swapchain images for later use. { - m_swapchainFormat = vk::Format{m_vkbData.m_swapchain.image_format}; + m_swapchainFormat = static_cast(m_vkbData.m_swapchain.image_format); m_swapchainExtent = vk::Extent2D{ .width = m_vkbData.m_swapchain.extent.width, .height = m_vkbData.m_swapchain.extent.height,