Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions .github/workflows/ios-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: ios-build

on:
workflow_dispatch:
inputs:
configuration:
description: Xcode configuration to archive
required: true
default: Release
type: choice
options:
- Release
- RelWithDebInfo
- Debug
ref:
description: Optional git ref to build
required: false
default: ""
type: string

concurrency:
group: ios-build-${{ github.workflow }}-${{ inputs.ref || github.ref }}
cancel-in-progress: true

jobs:
generate-spirv:
name: Generate SPIR-V Headers
runs-on: windows-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
submodules: recursive

- name: Generate SPIR-V Headers
shell: pwsh
run: ./scripts/ci/generate-spirv-headers.ps1

- name: Upload SPIR-V Headers
uses: actions/upload-artifact@v4
with:
name: spirv-headers
path: ./.artifacts/spirv-headers
if-no-files-found: error

build-ios:
name: Build iOS Archive
runs-on: macos-26
needs: generate-spirv

steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref || github.ref }}
submodules: recursive

- name: Install Build Dependencies
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
HOMEBREW_NO_INSTALL_CLEANUP: "1"
run: brew install cmake ninja pkg-config

- name: Validate Private File Secrets
env:
PRIVATE_DEFAULT_XEX_B64: ${{ secrets.PRIVATE_DEFAULT_XEX_B64 }}
PRIVATE_DEFAULT_XEXP_B64: ${{ secrets.PRIVATE_DEFAULT_XEXP_B64 }}
PRIVATE_SHADER_AR_B64: ${{ secrets.PRIVATE_SHADER_AR_B64 }}
run: |
set -euo pipefail
if [[ -f ./UnleashedRecompLib/private/default.xex && -f ./UnleashedRecompLib/private/default.xexp && -f ./UnleashedRecompLib/private/shader.ar ]]; then
echo "Using private files already present in the repository checkout."
exit 0
fi
for name in PRIVATE_DEFAULT_XEX_B64 PRIVATE_DEFAULT_XEXP_B64 PRIVATE_SHADER_AR_B64; do
if [[ -z "${!name}" ]]; then
echo "Missing required secret ${name}, and private files are not present in the repository checkout." >&2
exit 1
fi
done
- name: Stage Private Assets
env:
PRIVATE_DEFAULT_XEX_B64: ${{ secrets.PRIVATE_DEFAULT_XEX_B64 }}
PRIVATE_DEFAULT_XEXP_B64: ${{ secrets.PRIVATE_DEFAULT_XEXP_B64 }}
PRIVATE_SHADER_AR_B64: ${{ secrets.PRIVATE_SHADER_AR_B64 }}
run: |
set -euo pipefail
mkdir -p ./UnleashedRecompLib/private
if [[ -f ./UnleashedRecompLib/private/default.xex && -f ./UnleashedRecompLib/private/default.xexp && -f ./UnleashedRecompLib/private/shader.ar ]]; then
echo "Private files already staged in repository checkout."
exit 0
fi
printf '%s' "${PRIVATE_DEFAULT_XEX_B64}" | base64 --decode > ./UnleashedRecompLib/private/default.xex
printf '%s' "${PRIVATE_DEFAULT_XEXP_B64}" | base64 --decode > ./UnleashedRecompLib/private/default.xexp
printf '%s' "${PRIVATE_SHADER_AR_B64}" | base64 --decode > ./UnleashedRecompLib/private/shader.ar
- name: Prepare Host-Generated Sources
run: |
set -euo pipefail
cmake . --preset macos-release -DSDL2MIXER_VORBIS=VORBISFILE
cmake --build ./out/build/macos-release --target XenonRecomp x_decompress XenosRecomp file_to_c --parallel 3
pushd ./out/build/macos-release/UnleashedRecompLib
../tools/XenonRecomp/XenonRecomp/XenonRecomp 2>&1 | tee "${RUNNER_TEMP}/xenonrecomp.log"
../tools/x_decompress/x_decompress \
"${GITHUB_WORKSPACE}/UnleashedRecompLib/private/shader.ar" \
"${GITHUB_WORKSPACE}/UnleashedRecompLib/private/shader_decompressed.ar"
../tools/XenosRecomp/XenosRecomp/XenosRecomp 2>&1 | tee "${RUNNER_TEMP}/xenosrecomp.log"
popd
cmake --build ./out/build/macos-release --target UnleashedRecomp --parallel 3 --verbose
- name: Download SPIR-V Headers
uses: actions/download-artifact@v4
with:
name: spirv-headers
path: ./.artifacts/spirv-headers

- name: Stage SPIR-V Headers
run: rsync -a ./.artifacts/spirv-headers/ ./

- name: Build iOS Archive
env:
IOS_CONFIGURATION: ${{ inputs.configuration }}
run: bash ./scripts/ci/build-ios.sh

- name: Upload iOS Artifacts
uses: actions/upload-artifact@v4
with:
name: ios-${{ inputs.configuration }}
path: ./out/artifacts/ios
if-no-files-found: error
Binary file added UnleashedRecompLib/private/default.xex
Binary file not shown.
Binary file added UnleashedRecompLib/private/default.xexp
Binary file not shown.
Binary file added UnleashedRecompLib/private/shader.ar
Binary file not shown.
71 changes: 71 additions & 0 deletions scripts/ci/build-ios.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/../.." && pwd)"
cd "${repo_root}"

configuration="${IOS_CONFIGURATION:-Release}"
deployment_target="${IOS_DEPLOYMENT_TARGET:-15.0}"
bundle_id="${IOS_BUNDLE_ID:-io.github.aw514844.unleashedrecomp}"
artifact_root="${repo_root}/out/artifacts/ios"

case "${configuration}" in
Debug) preset_suffix="debug" ;;
Release) preset_suffix="release" ;;
RelWithDebInfo) preset_suffix="relwithdebinfo" ;;
*)
echo "Unsupported IOS_CONFIGURATION: ${configuration}" >&2
exit 1
;;
esac

preset="ios-xcode-${preset_suffix}"
build_dir="${repo_root}/out/build/${preset}"
archive_path="${artifact_root}/UnleashedRecomp.xcarchive"
app_zip="${artifact_root}/UnleashedRecomp-${configuration}.app.zip"
log_path="${artifact_root}/xcodebuild-${configuration}.log"

mkdir -p "${artifact_root}"

required_tools=(cmake xcodebuild xcrun)
for tool in "${required_tools[@]}"; do
if ! command -v "${tool}" >/dev/null 2>&1; then
echo "Missing required tool: ${tool}" >&2
exit 1
fi
done

spirv_count="$(find "${repo_root}/UnleashedRecomp/gpu/shader/hlsl" -name '*.spirv.h' | wc -l | tr -d ' ')"
if [[ "${spirv_count}" -lt 23 ]]; then
echo "Expected at least 23 generated .spirv.h files, found ${spirv_count}" >&2
exit 1
fi

rm -rf "${archive_path}"

cmake . --preset "${preset}" \
-DUNLEASHED_RECOMP_IOS_ENABLE_CODE_SIGNING=OFF \
-DUNLEASHED_RECOMP_IOS_BUNDLE_ID="${bundle_id}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${deployment_target}"

xcodebuild \
-project "${build_dir}/UnleashedRecomp.xcodeproj" \
-scheme "UnleashedRecomp" \
-configuration "${configuration}" \
-sdk iphoneos \
-destination "generic/platform=iOS" \
-archivePath "${archive_path}" \
archive | tee "${log_path}"

app_path="$(find "${archive_path}/Products/Applications" -maxdepth 1 -name '*.app' -print -quit)"
if [[ -z "${app_path}" ]]; then
echo "Archive completed but no .app bundle was found in ${archive_path}" >&2
exit 1
fi

ditto -c -k --sequesterRsrc --keepParent "${app_path}" "${app_zip}"

echo "Archive: ${archive_path}"
echo "App zip: ${app_zip}"
echo "Log: ${log_path}"
112 changes: 112 additions & 0 deletions scripts/ci/generate-spirv-headers.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = Resolve-Path (Join-Path $scriptDir "../..")
$hlslRoot = Join-Path $repoRoot "UnleashedRecomp/gpu/shader/hlsl"
$artifactRoot = Join-Path $repoRoot ".artifacts/spirv-headers/UnleashedRecomp/gpu/shader/hlsl"
$toolRoot = Join-Path $repoRoot ".tools/dxc"

function Get-DxcPath {
$existing = Get-Command dxc.exe -ErrorAction SilentlyContinue
if ($existing) {
return $existing.Source
}

New-Item -ItemType Directory -Force -Path $toolRoot | Out-Null

$release = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/DirectXShaderCompiler/releases/latest"
$asset = $release.assets |
Where-Object { $_.name -match '^dxc_.*\.zip$' } |
Select-Object -First 1

if (-not $asset) {
$assetNames = ($release.assets | ForEach-Object { $_.name }) -join ", "
throw "Could not find a Windows DXC zip in the latest DirectXShaderCompiler release. Assets: $assetNames"
}

$zipPath = Join-Path $toolRoot $asset.name
$extractPath = Join-Path $toolRoot "extract"

Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $zipPath
if (Test-Path $extractPath) {
Remove-Item -Recurse -Force $extractPath
}

Expand-Archive -Path $zipPath -DestinationPath $extractPath

$downloaded = Get-ChildItem -Path $extractPath -Filter dxc.exe -Recurse |
Sort-Object FullName |
Where-Object { $_.FullName -match '[\\/]x64[\\/]' } |
Select-Object -First 1

if (-not $downloaded) {
$downloaded = Get-ChildItem -Path $extractPath -Filter dxc.exe -Recurse |
Sort-Object FullName |
Select-Object -First 1
}

if (-not $downloaded) {
throw "Downloaded DXC archive did not contain dxc.exe"
}

return $downloaded.FullName
}

$shaderSpecs = @(
@{ Name = "blend_color_alpha_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "copy_vs"; Profile = "vs_6_0"; ExtraArgs = @("-fvk-invert-y", "-DUNLEASHED_RECOMP") },
@{ Name = "copy_color_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "copy_depth_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "csd_filter_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "csd_no_tex_vs"; Profile = "vs_6_0"; ExtraArgs = @("-fvk-invert-y", "-DUNLEASHED_RECOMP") },
@{ Name = "csd_vs"; Profile = "vs_6_0"; ExtraArgs = @("-fvk-invert-y", "-DUNLEASHED_RECOMP") },
@{ Name = "enhanced_motion_blur_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "gaussian_blur_3x3"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "gaussian_blur_5x5"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "gaussian_blur_7x7"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "gaussian_blur_9x9"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "gamma_correction_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "imgui_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "imgui_vs"; Profile = "vs_6_0"; ExtraArgs = @("-fvk-invert-y", "-DUNLEASHED_RECOMP") },
@{ Name = "movie_ps"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "movie_vs"; Profile = "vs_6_0"; ExtraArgs = @("-fvk-invert-y", "-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_color_2x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_color_4x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_color_8x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_depth_2x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_depth_4x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") },
@{ Name = "resolve_msaa_depth_8x"; Profile = "ps_6_0"; ExtraArgs = @("-DUNLEASHED_RECOMP") }
)

$dxcPath = Get-DxcPath
New-Item -ItemType Directory -Force -Path $artifactRoot | Out-Null

foreach ($shader in $shaderSpecs) {
$inputPath = Join-Path $hlslRoot ($shader.Name + ".hlsl")
$outputPath = $inputPath + ".spirv.h"
$artifactPath = Join-Path $artifactRoot ($shader.Name + ".hlsl.spirv.h")

$arguments = @(
"-T", $shader.Profile,
"-HV", "2021",
"-all-resources-bound",
"-spirv",
"-fvk-use-dx-layout"
) + $shader.ExtraArgs + @(
"-E", "shaderMain",
"-Fh", $outputPath,
$inputPath,
"-Vn", ("g_" + $shader.Name + "_spirv")
)

Write-Host "Generating $outputPath"
& $dxcPath @arguments
if ($LASTEXITCODE -ne 0) {
throw "DXC failed while generating $outputPath"
}

Copy-Item -Force -Path $outputPath -Destination $artifactPath
}

Write-Host "Generated $($shaderSpecs.Count) SPIR-V headers"