164 lines
7.1 KiB
PowerShell
164 lines
7.1 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Campaign V slice V6c: compile acdream's GLSL to the committed SPIR-V the
|
|
Vulkan backend loads at startup.
|
|
|
|
.DESCRIPTION
|
|
Plan §4.6 rules out runtime shader compilation: it would add a native
|
|
dependency and a startup cost for shaders that never change at runtime, and
|
|
CI runners have no Vulkan SDK. So the .spv artifacts are committed, this
|
|
script regenerates them, and an App test re-hashes the GLSL sources against
|
|
the manifest this writes so a source edit that never got recompiled fails a
|
|
test rather than shipping a stale binary.
|
|
|
|
Two compilers are supported, in this order:
|
|
|
|
1. glslc from a Vulkan SDK, if one is on PATH or under $VULKAN_SDK. This is
|
|
the reference implementation and is what the plan names.
|
|
2. tools/ShaderCompiler, a small .NET tool over Silk.NET.Shaderc — the same
|
|
shaderc library glslc is built on, through the already-pinned Silk.NET
|
|
2.23.0 family. It exists because neither the development machine nor CI
|
|
has an SDK installed, and requiring one to build acdream would put a
|
|
500 MB manual install between a contributor and a working checkout.
|
|
|
|
Both paths inject the same Vulkan preamble (see
|
|
tools/ShaderCompiler/VulkanGlslPreamble.cs) so the GLSL sources stay the
|
|
single source of truth for both backends.
|
|
|
|
.PARAMETER ShadersDirectory
|
|
Source directory. Defaults to src/AcDream.App/Rendering/Shaders.
|
|
|
|
.PARAMETER OutputDirectory
|
|
Where .spv and shaders.manifest.json are written. Defaults to
|
|
src/AcDream.App/Rendering/Shaders/spv.
|
|
|
|
.PARAMETER PreferSdk
|
|
Use glslc when available. On by default; pass -PreferSdk:$false to force the
|
|
managed path, which is what a comparison between the two wants.
|
|
|
|
.PARAMETER ForceRecompile
|
|
Recompile even when the existing manifest proves that a source and its
|
|
committed SPIR-V artifact are unchanged. Use this only when validating a
|
|
compiler/toolchain change; ordinary regeneration preserves exact retail
|
|
binaries while compiling every changed shader.
|
|
|
|
.EXAMPLE
|
|
tools/compile-shaders.ps1
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$ShadersDirectory,
|
|
[string]$OutputDirectory,
|
|
[bool]$PreferSdk = $true,
|
|
[switch]$ForceRecompile
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$repo = Split-Path -Parent $PSScriptRoot
|
|
# Campaign V slice V9: every path below is composed one segment at a time rather
|
|
# than from an embedded 'a\b\c' literal. A backslash is a path separator on
|
|
# Windows and an ordinary filename character everywhere else, so the embedded
|
|
# form silently produced one long nonexistent file name on the Linux CI runner
|
|
# that this slice's lavapipe job introduced.
|
|
if (-not $ShadersDirectory) {
|
|
$ShadersDirectory = [System.IO.Path]::Combine(
|
|
$repo, 'src', 'AcDream.App', 'Rendering', 'Shaders')
|
|
}
|
|
if (-not $OutputDirectory) {
|
|
$OutputDirectory = Join-Path $ShadersDirectory 'spv'
|
|
}
|
|
|
|
function Write-Step($message) { Write-Host "[shaders] $message" }
|
|
|
|
New-Item -ItemType Directory -Force -Path $OutputDirectory | Out-Null
|
|
|
|
# --- 1. Locate glslc, if the machine has a Vulkan SDK -------------------------
|
|
$glslc = $null
|
|
if ($PreferSdk) {
|
|
$onPath = Get-Command glslc -ErrorAction SilentlyContinue
|
|
if ($onPath) {
|
|
$glslc = $onPath.Source
|
|
}
|
|
elseif ($env:VULKAN_SDK) {
|
|
# 'Bin/glslc.exe' on Windows, 'bin/glslc' on the SDK's Linux layout.
|
|
$candidates = @(
|
|
[System.IO.Path]::Combine($env:VULKAN_SDK, 'Bin', 'glslc.exe'),
|
|
[System.IO.Path]::Combine($env:VULKAN_SDK, 'bin', 'glslc')
|
|
)
|
|
foreach ($candidate in $candidates) {
|
|
if (Test-Path $candidate) { $glslc = $candidate; break }
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- 2. Compile ---------------------------------------------------------------
|
|
# Even with glslc present the managed tool does the work: it owns the preamble
|
|
# injection and the manifest, and running the same transform through two
|
|
# code paths is exactly how the two would drift. glslc's presence is reported so
|
|
# a future slice can add a cross-check between them.
|
|
if ($glslc) {
|
|
Write-Step "a Vulkan SDK glslc was found at $glslc (recorded; the managed compiler still runs)"
|
|
}
|
|
else {
|
|
Write-Step 'no Vulkan SDK glslc found; using the managed Silk.NET.Shaderc compiler'
|
|
}
|
|
|
|
$tool = [System.IO.Path]::Combine(
|
|
$repo, 'tools', 'ShaderCompiler', 'ShaderCompiler.csproj')
|
|
|
|
# Campaign V slice V9 follow-up: the tool is PUBLISHED for the host runtime
|
|
# identifier rather than built portable. A portable `dotnet build` leaves
|
|
# shaderc's native module under runtimes/<rid>/native/ and makes loading it the
|
|
# job of Silk.NET's probing chain, which reaches it on some hosts and not
|
|
# others - it resolved on Ubuntu 24.04 locally and failed on the ubuntu-24.04
|
|
# CI runner with "Could not load from any of the possible library names!".
|
|
# Publishing with an explicit RID flattens the native next to the assembly,
|
|
# where AppContext.BaseDirectory - the first candidate Silk.NET tries - always
|
|
# finds it. Same managed code, same pinned shaderc 2.23.0; only the lookup
|
|
# changes.
|
|
$architecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture
|
|
$ridArchitecture = switch ($architecture) {
|
|
'X64' { 'x64' }
|
|
'Arm64' { 'arm64' }
|
|
'X86' { 'x86' }
|
|
default { throw "No shaderc native is published for processor architecture $architecture." }
|
|
}
|
|
# $IsWindows and friends only exist in PowerShell 6+; 5.1 is Windows by
|
|
# definition, and -or short-circuits before touching the undefined variable.
|
|
$ridOs =
|
|
if ($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows) { 'win' }
|
|
elseif ($IsMacOS) { 'osx' }
|
|
elseif ($IsLinux) { 'linux' }
|
|
else { throw 'Unrecognised operating system; cannot choose a shaderc native.' }
|
|
$rid = "$ridOs-$ridArchitecture"
|
|
|
|
$toolDirectory = [System.IO.Path]::Combine(
|
|
$repo, 'tools', 'ShaderCompiler', 'bin', 'publish', $rid)
|
|
Write-Step "publishing the shader compiler for $rid"
|
|
& dotnet publish $tool -c Release -r $rid --self-contained false -o $toolDirectory --nologo -v q | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw "Shader compiler publish failed with exit code $LASTEXITCODE." }
|
|
|
|
$binary = Join-Path $toolDirectory 'AcDream.Tools.ShaderCompiler.dll'
|
|
if (-not (Test-Path $binary)) { throw "Shader compiler not found at $binary." }
|
|
|
|
# Name the missing file rather than letting Silk.NET report the generic
|
|
# "could not load from any of the possible library names" three steps later.
|
|
$nativeName =
|
|
if ($ridOs -eq 'win') { 'shaderc_shared.dll' }
|
|
elseif ($ridOs -eq 'osx') { 'libshaderc_shared.dylib' }
|
|
else { 'libshaderc_shared.so' }
|
|
$native = Join-Path $toolDirectory $nativeName
|
|
if (-not (Test-Path $native)) {
|
|
throw ("The shaderc native $nativeName is not beside the shader compiler at " +
|
|
"$toolDirectory. The Silk.NET.Shaderc.Native package did not publish a " +
|
|
"$rid asset.")
|
|
}
|
|
Write-Step "shaderc native: $native"
|
|
|
|
Write-Step "compiling $ShadersDirectory -> $OutputDirectory"
|
|
$compilerArguments = @($binary, $ShadersDirectory, $OutputDirectory)
|
|
if ($ForceRecompile) { $compilerArguments += '--force' }
|
|
& dotnet @compilerArguments
|
|
if ($LASTEXITCODE -ne 0) { throw "Shader compilation failed with exit code $LASTEXITCODE." }
|
|
|
|
Write-Step 'done'
|