mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
shaderfix
This commit is contained in:
+50
-1
@@ -220,6 +220,54 @@ output directory; nothing packages it next to a shipped game yet.
|
|||||||
and `ShaderFX.bridgeError()` says why not. Activating an already-converted
|
and `ShaderFX.bridgeError()` says why not. Activating an already-converted
|
||||||
preset never needs any of this.
|
preset never needs any of this.
|
||||||
|
|
||||||
|
**Statically linked bridges.** On iOS there is no loadable library at all:
|
||||||
|
the bridge is linked straight into the app binary, so its symbols live in the
|
||||||
|
main image and are reachable only through `ffi.C`. The candidate list is empty
|
||||||
|
on iOS and, on every platform, a failed candidate walk falls back to probing
|
||||||
|
`ffi.C` for `librashader_translate_preset` after the `ffi.cdef`. If the symbol
|
||||||
|
is there, `ffi.C` becomes the library handle and `ShaderFX.translate` uses it
|
||||||
|
unchanged. If it is not, `ShaderFX.bridgeError()` reports that `ffi.C` was
|
||||||
|
probed as well, followed by the file paths that were tried (omitted on iOS,
|
||||||
|
where none are).
|
||||||
|
|
||||||
|
**Android ships the bridge inside the APK.** `scripts/build_android.sh`
|
||||||
|
stages `liblibrashader_bridge.so` into
|
||||||
|
`mobile/android/app/src/main/jniLibs/<abi>/` for the two ABIs the APK ships,
|
||||||
|
arm64-v8a and armeabi-v7a, so `ffi.load("liblibrashader_bridge.so")` finds it
|
||||||
|
by bare name in the app's native library directory. The build cross-compiles
|
||||||
|
the crate with `cargo ndk` against the same NDK the gradle project uses
|
||||||
|
(25.2.9519653), which needs `cargo install cargo-ndk` and
|
||||||
|
`rustup target add aarch64-linux-android armv7-linux-androideabi`; the script
|
||||||
|
names the exact command for any target that is missing. Set
|
||||||
|
`SHADERFX_BRIDGE_ANDROID_DIR` to a directory holding
|
||||||
|
`<abi>/liblibrashader_bridge.so` to bundle prebuilt libraries instead. As on
|
||||||
|
desktop, the bridge is only needed to CONVERT a preset: a build without it
|
||||||
|
still runs presets converted elsewhere, and the packager warns and continues
|
||||||
|
rather than failing.
|
||||||
|
|
||||||
|
**iOS links the bridge instead of loading it.** An iOS app cannot `dlopen` a
|
||||||
|
dylib shipped beside its binary, so `tools/shaderfx-bridge` also builds as a
|
||||||
|
`staticlib` and `scripts/build_ios.sh` links it into the app executable
|
||||||
|
(`bundle_shader_bridge_ios`, mirroring `bundle_shader_bridge` in
|
||||||
|
`scripts/build.sh`). `SHADERFX_BRIDGE_IOS` points at a prebuilt archive;
|
||||||
|
otherwise cargo builds `aarch64-apple-ios` for device builds and every
|
||||||
|
installed simulator target (`aarch64-apple-ios-sim`, `x86_64-apple-ios`) for
|
||||||
|
the Simulator, with `IPHONEOS_DEPLOYMENT_TARGET=15.0`, and `ARCHS` is pinned to
|
||||||
|
what got built. The archive is never linked directly: LÖVE 12 carries its own
|
||||||
|
glslang for shader validation and the crate drags in a second, incompatible
|
||||||
|
one, and linking both crashed inside `Shader::validateInternal` at startup.
|
||||||
|
Each slice is therefore prelinked with `ld -r -all_load
|
||||||
|
-exported_symbols_list` so only `_librashader_translate_preset` and
|
||||||
|
`_librashader_free_string` stay external and every other symbol (glslang,
|
||||||
|
spirv-cross, Rust std) becomes private to the object, then `rust-objcopy`
|
||||||
|
drops the `__LLVM` bitcode sections rustup's prebuilt std carries, since
|
||||||
|
Apple's `nm` cannot read them. `OTHER_LDFLAGS` adds `-Wl,-u` on both entry
|
||||||
|
points to keep them past dead-stripping and `-lc++` for the C++ the crate
|
||||||
|
needs; no Objective-C framework is involved. A build that linked the object
|
||||||
|
fails if `nm` cannot find `_librashader_translate_preset` in the finished
|
||||||
|
binary; a build that could not produce one only warns and lands where the
|
||||||
|
desktop packages without cargo land: converted presets run, CONVERT does not.
|
||||||
|
|
||||||
### Fixup
|
### Fixup
|
||||||
|
|
||||||
`ShaderFixup.lua` mechanically rewrites the emitted GLSL into something LOVE
|
`ShaderFixup.lua` mechanically rewrites the emitted GLSL into something LOVE
|
||||||
@@ -656,7 +704,8 @@ None of these are theoretical.
|
|||||||
`bundle_shader_bridge`, building it with cargo when a prebuilt one is not
|
`bundle_shader_bridge`, building it with cargo when a prebuilt one is not
|
||||||
supplied through `SHADERFX_BRIDGE`. A build host without cargo produces a
|
supplied through `SHADERFX_BRIDGE`. A build host without cargo produces a
|
||||||
package that can run converted presets but cannot CONVERT new ones, and says
|
package that can run converted presets but cannot CONVERT new ones, and says
|
||||||
so rather than failing. Android ships the `.so` via `jniLibs`.
|
so rather than failing. `scripts/build_android.sh` and `scripts/build_ios.sh`
|
||||||
|
follow the same rule with `cargo ndk` and the iOS static archive.
|
||||||
- **The buildbot shortlist is a temporary trim.** `KEPT_PRESETS` reflects one
|
- **The buildbot shortlist is a temporary trim.** `KEPT_PRESETS` reflects one
|
||||||
manual pass over `handheld/` and is expected to change, most likely to shrink.
|
manual pass over `handheld/` and is expected to change, most likely to shrink.
|
||||||
- **Tilt direction is unverified.** Which way forward and back rocking moves the
|
- **Tilt direction is unverified.** Which way forward and back rocking moves the
|
||||||
|
|||||||
@@ -100,6 +100,11 @@ love-android 11.5a expects:
|
|||||||
Set `ANDROID_SDK_ROOT` (or `ANDROID_HOME`), or let the script write
|
Set `ANDROID_SDK_ROOT` (or `ANDROID_HOME`), or let the script write
|
||||||
`local.properties` when it finds `~/Library/Android/sdk`.
|
`local.properties` when it finds `~/Library/Android/sdk`.
|
||||||
|
|
||||||
|
**ShaderFX bridge**: `scripts/build_android.sh` bundles
|
||||||
|
`liblibrashader_bridge.so` for arm64-v8a and armeabi-v7a via `cargo ndk` (or
|
||||||
|
from `SHADERFX_BRIDGE_ANDROID_DIR`), and warns and continues when neither is
|
||||||
|
available; see `docs/shaderfx.md`.
|
||||||
|
|
||||||
Gradle flavor used: **`embedNoRecord`** (game fused into the APK, no microphone).
|
Gradle flavor used: **`embedNoRecord`** (game fused into the APK, no microphone).
|
||||||
Build task: `assembleEmbedNoRecordDebug`.
|
Build task: `assembleEmbedNoRecordDebug`.
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ The script verifies the final app before packaging it:
|
|||||||
|
|
||||||
- the public Documents plist settings are present
|
- the public Documents plist settings are present
|
||||||
- the native picker bridge is present
|
- the native picker bridge is present
|
||||||
|
- the SHADER FX librashader bridge is linked into the app binary, when this build produced one
|
||||||
- `game.love` exists and is non-empty
|
- `game.love` exists and is non-empty
|
||||||
|
|
||||||
If the payload is missing, the build fails instead of producing a blank app.
|
If the payload is missing, the build fails instead of producing a blank app.
|
||||||
|
|||||||
@@ -461,6 +461,115 @@ pack_game_love() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --------------------------------------------------------------- ShaderFX bridge
|
||||||
|
SHADER_BRIDGE_LIB="liblibrashader_bridge.so"
|
||||||
|
SHADER_BRIDGE_ABIS="arm64-v8a armeabi-v7a"
|
||||||
|
|
||||||
|
shader_bridge_rust_target() {
|
||||||
|
case "$1" in
|
||||||
|
arm64-v8a) printf 'aarch64-linux-android' ;;
|
||||||
|
armeabi-v7a) printf 'armv7-linux-androideabi' ;;
|
||||||
|
x86_64) printf 'x86_64-linux-android' ;;
|
||||||
|
x86) printf 'i686-linux-android' ;;
|
||||||
|
*) printf '' ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
shader_bridge_staged_count() {
|
||||||
|
local jni="$1" abi count=0
|
||||||
|
for abi in $SHADER_BRIDGE_ABIS; do
|
||||||
|
[ -f "$jni/$abi/$SHADER_BRIDGE_LIB" ] && count=$((count + 1))
|
||||||
|
done
|
||||||
|
printf '%s' "$count"
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_shader_bridge_android() {
|
||||||
|
local jni="$ANDROID_DIR/app/src/main/jniLibs"
|
||||||
|
local crate="$ROOT/tools/shaderfx-bridge"
|
||||||
|
local abi target
|
||||||
|
|
||||||
|
for abi in $SHADER_BRIDGE_ABIS; do
|
||||||
|
rm -f "$jni/$abi/$SHADER_BRIDGE_LIB"
|
||||||
|
done
|
||||||
|
|
||||||
|
local prebuilt="${SHADERFX_BRIDGE_ANDROID_DIR:-}"
|
||||||
|
if [ -n "$prebuilt" ]; then
|
||||||
|
for abi in $SHADER_BRIDGE_ABIS; do
|
||||||
|
if [ -f "$prebuilt/$abi/$SHADER_BRIDGE_LIB" ]; then
|
||||||
|
mkdir -p "$jni/$abi"
|
||||||
|
cp "$prebuilt/$abi/$SHADER_BRIDGE_LIB" "$jni/$abi/$SHADER_BRIDGE_LIB"
|
||||||
|
else
|
||||||
|
warn "SHADERFX_BRIDGE_ANDROID_DIR has no $abi/$SHADER_BRIDGE_LIB"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$(shader_bridge_staged_count "$jni")" -gt 0 ]; then
|
||||||
|
say "bundled $SHADER_BRIDGE_LIB for SHADER FX preset conversion (prebuilt)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$crate/Cargo.toml" ]; then
|
||||||
|
warn "$SHADER_BRIDGE_LIB not found: this build can run converted presets but not CONVERT new ones (tools/shaderfx-bridge is missing)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v cargo >/dev/null 2>&1 || ! cargo ndk --version >/dev/null 2>&1; then
|
||||||
|
warn "$SHADER_BRIDGE_LIB not found: this build can run converted presets but not CONVERT new ones (set SHADERFX_BRIDGE_ANDROID_DIR or run 'cargo install cargo-ndk')"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ndk="${ANDROID_NDK_HOME:-}"
|
||||||
|
if [ -z "$ndk" ] || [ ! -d "$ndk" ]; then
|
||||||
|
ndk="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-$HOME/Library/Android/sdk}}/ndk/$NDK_VERSION"
|
||||||
|
fi
|
||||||
|
if [ ! -d "$ndk" ]; then
|
||||||
|
warn "$SHADER_BRIDGE_LIB not built: NDK $NDK_VERSION not found (set ANDROID_NDK_HOME)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local installed missing="" buildable=""
|
||||||
|
installed="$(rustup target list --installed 2>/dev/null || true)"
|
||||||
|
for abi in $SHADER_BRIDGE_ABIS; do
|
||||||
|
target="$(shader_bridge_rust_target "$abi")"
|
||||||
|
if grep -qx "$target" <<< "$installed"; then
|
||||||
|
buildable="$buildable $abi"
|
||||||
|
else
|
||||||
|
missing="$missing $target"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -n "$missing" ]; then
|
||||||
|
warn "$SHADER_BRIDGE_LIB: skipping$missing. Run: rustup target add$missing"
|
||||||
|
fi
|
||||||
|
if [ -z "$buildable" ]; then
|
||||||
|
warn "$SHADER_BRIDGE_LIB not built: this build can run converted presets but not CONVERT new ones (no Android Rust targets installed)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local args=()
|
||||||
|
for abi in $buildable; do
|
||||||
|
args+=(-t "$abi")
|
||||||
|
done
|
||||||
|
|
||||||
|
say "building the ShaderFX bridge with cargo-ndk (${buildable# })"
|
||||||
|
mkdir -p "$jni"
|
||||||
|
if ! (
|
||||||
|
cd "$crate"
|
||||||
|
export ANDROID_NDK_HOME="$ndk"
|
||||||
|
export ANDROID_NDK_ROOT="$ndk"
|
||||||
|
export CARGO_PROFILE_RELEASE_STRIP="symbols"
|
||||||
|
cargo ndk "${args[@]}" -o "$jni" build --release
|
||||||
|
); then
|
||||||
|
warn "$SHADER_BRIDGE_LIB failed to cross-compile: this build can run converted presets but not CONVERT new ones"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(shader_bridge_staged_count "$jni")" -gt 0 ]; then
|
||||||
|
say "bundled $SHADER_BRIDGE_LIB for SHADER FX preset conversion"
|
||||||
|
else
|
||||||
|
warn "$SHADER_BRIDGE_LIB not found after cargo-ndk: this build can run converted presets but not CONVERT new ones"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# --------------------------------------------------------------- SDK check
|
# --------------------------------------------------------------- SDK check
|
||||||
require_android_sdk() {
|
require_android_sdk() {
|
||||||
local sdk="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}"
|
local sdk="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}"
|
||||||
@@ -575,5 +684,6 @@ if $PACKAGE_ONLY; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
require_android_sdk
|
require_android_sdk
|
||||||
|
bundle_shader_bridge_android
|
||||||
run_gradle
|
run_gradle
|
||||||
say "done"
|
say "done"
|
||||||
|
|||||||
+149
-3
@@ -615,6 +615,138 @@ verify_game_payload() {
|
|||||||
say "game.love present ($(du -h "$app/game.love" | cut -f1))"
|
say "game.love present ($(du -h "$app/game.love" | cut -f1))"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SHADER_BRIDGE_LIB=""
|
||||||
|
SHADER_BRIDGE_NAME="liblibrashader_bridge.a"
|
||||||
|
SHADER_BRIDGE_OBJ="librashader_bridge.o"
|
||||||
|
|
||||||
|
SHADER_BRIDGE_ARCHS=""
|
||||||
|
|
||||||
|
rust_targets_for_sdk() {
|
||||||
|
if [ "$1" = "iphoneos" ]; then
|
||||||
|
printf 'aarch64-apple-ios'
|
||||||
|
else
|
||||||
|
printf 'aarch64-apple-ios-sim x86_64-apple-ios'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_shader_bridge_slice() {
|
||||||
|
local rust_target="$1"
|
||||||
|
local crate="$ROOT/tools/shaderfx-bridge"
|
||||||
|
local built="$crate/target/$rust_target/release/$SHADER_BRIDGE_NAME"
|
||||||
|
if [ -f "$built" ]; then
|
||||||
|
printf '%s' "$built"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v cargo >/dev/null 2>&1 \
|
||||||
|
&& rustup target list --installed 2>/dev/null \
|
||||||
|
| grep -x "$rust_target" >/dev/null; then
|
||||||
|
say "building the ShaderFX bridge with cargo ($rust_target)" >&2
|
||||||
|
if (cd "$crate" && IPHONEOS_DEPLOYMENT_TARGET=15.0 \
|
||||||
|
cargo build --release --target "$rust_target" >/dev/null 2>&1); then
|
||||||
|
printf '%s' "$built"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
prelink_shader_bridge_slice() {
|
||||||
|
local archive="$1" sdk="$2" out="$3"
|
||||||
|
local arch platform sdk_version syms objcopy tmp
|
||||||
|
arch="$(lipo -archs "$archive" 2>/dev/null | awk '{print $1}')"
|
||||||
|
[ -n "$arch" ] || return 1
|
||||||
|
if [ "$sdk" = "iphoneos" ]; then platform="ios"; else platform="ios-simulator"; fi
|
||||||
|
sdk_version="$(xcrun --sdk "$sdk" --show-sdk-version 2>/dev/null)"
|
||||||
|
[ -n "$sdk_version" ] || return 1
|
||||||
|
syms="$LIBS_DIR/librashader_bridge.exports"
|
||||||
|
printf '_librashader_translate_preset\n_librashader_free_string\n' > "$syms"
|
||||||
|
tmp="$out.tmp"
|
||||||
|
xcrun ld -r -arch "$arch" -platform_version "$platform" 15.0 "$sdk_version" \
|
||||||
|
-all_load -exported_symbols_list "$syms" -o "$tmp" "$archive" || return 1
|
||||||
|
objcopy="$(ls "$(rustc --print sysroot 2>/dev/null)"/lib/rustlib/*/bin/rust-objcopy 2>/dev/null | head -1)"
|
||||||
|
if [ -n "$objcopy" ] && "$objcopy" --remove-section __LLVM,__bitcode \
|
||||||
|
--remove-section __LLVM,__cmdline "$tmp" "$out" 2>/dev/null; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
else
|
||||||
|
mv "$tmp" "$out"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_shader_bridge_ios() {
|
||||||
|
local rust_targets="$1" sdk="$2"
|
||||||
|
local src="${SHADERFX_BRIDGE_IOS:-}"
|
||||||
|
local slices=() objs=() target slice obj i
|
||||||
|
SHADER_BRIDGE_LIB=""
|
||||||
|
SHADER_BRIDGE_ARCHS=""
|
||||||
|
rm -f "$LIBS_DIR/$SHADER_BRIDGE_NAME" "$LIBS_DIR/$SHADER_BRIDGE_OBJ" "$LIBS_DIR"/librashader_bridge.*.o
|
||||||
|
if [ -n "$src" ]; then
|
||||||
|
if [ -f "$src" ]; then
|
||||||
|
slices+=("$src")
|
||||||
|
else
|
||||||
|
warn "SHADERFX_BRIDGE_IOS=$src does not exist"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
for target in $rust_targets; do
|
||||||
|
if slice="$(build_shader_bridge_slice "$target")"; then
|
||||||
|
slices+=("$slice")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ "${#slices[@]}" -gt 0 ]; then
|
||||||
|
mkdir -p "$LIBS_DIR"
|
||||||
|
i=0
|
||||||
|
for slice in "${slices[@]}"; do
|
||||||
|
i=$((i + 1))
|
||||||
|
obj="$LIBS_DIR/librashader_bridge.$i.o"
|
||||||
|
if prelink_shader_bridge_slice "$slice" "$sdk" "$obj"; then
|
||||||
|
objs+=("$obj")
|
||||||
|
else
|
||||||
|
warn "could not prelink $(basename "$slice") for $sdk"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ "${#objs[@]}" -eq 1 ]; then
|
||||||
|
mv "${objs[0]}" "$LIBS_DIR/$SHADER_BRIDGE_OBJ"
|
||||||
|
elif [ "${#objs[@]}" -gt 1 ]; then
|
||||||
|
lipo -create "${objs[@]}" -output "$LIBS_DIR/$SHADER_BRIDGE_OBJ"
|
||||||
|
rm -f "${objs[@]}"
|
||||||
|
fi
|
||||||
|
if [ -f "$LIBS_DIR/$SHADER_BRIDGE_OBJ" ]; then
|
||||||
|
SHADER_BRIDGE_LIB="$LIBS_DIR/$SHADER_BRIDGE_OBJ"
|
||||||
|
SHADER_BRIDGE_ARCHS="$(lipo -archs "$SHADER_BRIDGE_LIB" 2>/dev/null || true)"
|
||||||
|
say "linking $SHADER_BRIDGE_OBJ for SHADER FX preset conversion (${SHADER_BRIDGE_ARCHS:-unknown arch})"
|
||||||
|
else
|
||||||
|
warn "$SHADER_BRIDGE_NAME not found: this build can run converted presets but not CONVERT new ones (set SHADERFX_BRIDGE_IOS, or install cargo plus one of: rustup target add $rust_targets)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_shader_bridge() {
|
||||||
|
local app="$1"
|
||||||
|
local exe bin
|
||||||
|
if [ -z "$SHADER_BRIDGE_LIB" ]; then
|
||||||
|
warn "no SHADER FX bridge in this build: CONVERT stays unavailable, converted presets still run"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
exe="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' \
|
||||||
|
"$app/Info.plist" 2>/dev/null || true)"
|
||||||
|
bin="$app/${exe:-love}"
|
||||||
|
[ -f "$bin" ] || bin="$app/love"
|
||||||
|
if [ ! -f "$bin" ]; then
|
||||||
|
warn "no executable inside $(basename "$app"); skipping SHADER FX bridge check"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if nm "$bin" 2>/dev/null | grep -E ' _librashader_translate_preset$' >/dev/null \
|
||||||
|
|| xcrun dyld_info -exports "$bin" 2>/dev/null \
|
||||||
|
| grep -E ' _librashader_translate_preset$' >/dev/null; then
|
||||||
|
say "SHADER FX bridge present (librashader_translate_preset)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fail "built app does not carry the SHADER FX bridge symbols.
|
||||||
|
$SHADER_BRIDGE_OBJ was linked but librashader_translate_preset is absent,
|
||||||
|
so SHADER FX CONVERT would fail at runtime.
|
||||||
|
Rebuild after: rm -rf tools/shaderfx-bridge/target"
|
||||||
|
}
|
||||||
|
|
||||||
run_xcodebuild() {
|
run_xcodebuild() {
|
||||||
local config sdk destination
|
local config sdk destination
|
||||||
if $RELEASE; then
|
if $RELEASE; then
|
||||||
@@ -633,6 +765,8 @@ run_xcodebuild() {
|
|||||||
|
|
||||||
mkdir -p "$BUILD_DIR"
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
|
bundle_shader_bridge_ios "$(rust_targets_for_sdk "$sdk")" "$sdk"
|
||||||
|
|
||||||
# Prefer -target + SYMROOT over -derivedDataPath: modern Xcode requires
|
# Prefer -target + SYMROOT over -derivedDataPath: modern Xcode requires
|
||||||
# -scheme whenever -derivedDataPath is set, and love-ios ships no shared schemes.
|
# -scheme whenever -derivedDataPath is set, and love-ios ships no shared schemes.
|
||||||
# Always stamp both: the overlay plist expands $(MARKETING_VERSION) /
|
# Always stamp both: the overlay plist expands $(MARKETING_VERSION) /
|
||||||
@@ -661,6 +795,13 @@ run_xcodebuild() {
|
|||||||
ONLY_ACTIVE_ARCH=NO
|
ONLY_ACTIVE_ARCH=NO
|
||||||
DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING=YES
|
DISABLE_MANUAL_TARGET_ORDER_BUILD_WARNING=YES
|
||||||
)
|
)
|
||||||
|
if [ -n "$SHADER_BRIDGE_LIB" ]; then
|
||||||
|
args+=(OTHER_LDFLAGS="-Wl,-u,_librashader_translate_preset -Wl,-u,_librashader_free_string \"$SHADER_BRIDGE_LIB\" -lc++")
|
||||||
|
if [ -n "$SHADER_BRIDGE_ARCHS" ]; then
|
||||||
|
args+=(ARCHS="$SHADER_BRIDGE_ARCHS")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if ! $DEVICE; then
|
if ! $DEVICE; then
|
||||||
# Simulator: ad-hoc signing (no certificate needed). A plain unsigned
|
# Simulator: ad-hoc signing (no certificate needed). A plain unsigned
|
||||||
# build would drop the entitlements file, and HealthKit refuses to run
|
# build would drop the entitlements file, and HealthKit refuses to run
|
||||||
@@ -723,11 +864,14 @@ run_xcodebuild() {
|
|||||||
|
|
||||||
local products="$BUILD_DIR/Build/Products/${config}-${sdk}"
|
local products="$BUILD_DIR/Build/Products/${config}-${sdk}"
|
||||||
local app=""
|
local app=""
|
||||||
local candidate
|
local candidate newest=0 mtime
|
||||||
for candidate in "$products/$PRODUCT_NAME.app" "$products/$APP_NAME.app" "$products/love.app"; do
|
for candidate in "$products/$PRODUCT_NAME.app" "$products/$APP_NAME.app" "$products/love.app"; do
|
||||||
if [ -d "$candidate" ]; then
|
if [ -d "$candidate" ]; then
|
||||||
app="$candidate"
|
mtime="$(stat -f %m "$candidate" 2>/dev/null || echo 0)"
|
||||||
break
|
if [ "$mtime" -gt "$newest" ]; then
|
||||||
|
newest="$mtime"
|
||||||
|
app="$candidate"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ -z "$app" ]; then
|
if [ -z "$app" ]; then
|
||||||
@@ -736,6 +880,7 @@ run_xcodebuild() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [ "$app" != "$products/$APP_NAME.app" ]; then
|
if [ "$app" != "$products/$APP_NAME.app" ]; then
|
||||||
|
rm -rf "$products/$APP_NAME.app"
|
||||||
mv "$app" "$products/$APP_NAME.app"
|
mv "$app" "$products/$APP_NAME.app"
|
||||||
app="$products/$APP_NAME.app"
|
app="$products/$APP_NAME.app"
|
||||||
fi
|
fi
|
||||||
@@ -754,6 +899,7 @@ run_xcodebuild() {
|
|||||||
|
|
||||||
verify_game_payload "$app"
|
verify_game_payload "$app"
|
||||||
verify_native_bridge "$app"
|
verify_native_bridge "$app"
|
||||||
|
verify_shader_bridge "$app"
|
||||||
|
|
||||||
local dist_dir="$DIST/${config}-${sdk}"
|
local dist_dir="$DIST/${config}-${sdk}"
|
||||||
rm -rf "$dist_dir"
|
rm -rf "$dist_dir"
|
||||||
|
|||||||
@@ -76,7 +76,14 @@ local function sdlFfi()
|
|||||||
]])
|
]])
|
||||||
if sdlCdefOk then
|
if sdlCdefOk then
|
||||||
local okLoad, lib = pcall(ffi.load, "SDL2")
|
local okLoad, lib = pcall(ffi.load, "SDL2")
|
||||||
sdlLib = okLoad and lib or ffi.C
|
lib = okLoad and lib or ffi.C
|
||||||
|
local okSyms = pcall(function()
|
||||||
|
return lib.SDL_InitSubSystem, lib.SDL_NumSensors, lib.SDL_SensorGetDeviceType,
|
||||||
|
lib.SDL_SensorOpen, lib.SDL_SensorUpdate, lib.SDL_SensorGetData,
|
||||||
|
lib.SDL_GL_GetCurrentWindow, lib.SDL_GetWindowDisplayIndex,
|
||||||
|
lib.SDL_GetDisplayOrientation
|
||||||
|
end)
|
||||||
|
sdlLib = okSyms and lib or false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not sdlCdefOk or not sdlLib then return nil end
|
if not sdlCdefOk or not sdlLib then return nil end
|
||||||
|
|||||||
@@ -984,6 +984,17 @@ local function modScopeChipsWidth(options, gap, m)
|
|||||||
return need
|
return need
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function profileState(imp)
|
||||||
|
if imp._profileCache == nil then
|
||||||
|
local LauncherMods = require("src.mods.LauncherMods")
|
||||||
|
local SaveData = require("src.core.SaveData")
|
||||||
|
local options = SaveData.loadOptions()
|
||||||
|
local list, cur = LauncherMods.getProfiles(options)
|
||||||
|
imp._profileCache = { options = options, list = list, active = cur }
|
||||||
|
end
|
||||||
|
return imp._profileCache
|
||||||
|
end
|
||||||
|
|
||||||
local function buildModScopeRow(imp, x, y, w, m)
|
local function buildModScopeRow(imp, x, y, w, m)
|
||||||
local LauncherMods = require("src.mods.LauncherMods")
|
local LauncherMods = require("src.mods.LauncherMods")
|
||||||
local h = math.max(Kit.tapMin(), math.floor(26 * m.s))
|
local h = math.max(Kit.tapMin(), math.floor(26 * m.s))
|
||||||
@@ -994,7 +1005,7 @@ local function buildModScopeRow(imp, x, y, w, m)
|
|||||||
local options = modScopeOptions(imp)
|
local options = modScopeOptions(imp)
|
||||||
|
|
||||||
-- Dedicated Profile control section (cycle button + gear icon button) on right side of Scope Bar
|
-- Dedicated Profile control section (cycle button + gear icon button) on right side of Scope Bar
|
||||||
local _, activeProf = LauncherMods.getProfiles()
|
local activeProf = profileState(imp).active
|
||||||
local isCompact = (w < math.floor(500 * m.s))
|
local isCompact = (w < math.floor(500 * m.s))
|
||||||
local nameText = tostring(activeProf or "Default")
|
local nameText = tostring(activeProf or "Default")
|
||||||
local profLabel = isCompact and nameText or Strings("Profile: %s", nameText)
|
local profLabel = isCompact and nameText or Strings("Profile: %s", nameText)
|
||||||
@@ -3698,9 +3709,8 @@ local function buildSingleProfileActionsModal(imp, m)
|
|||||||
if not pName then imp._singleProfileActions = nil return end
|
if not pName then imp._singleProfileActions = nil return end
|
||||||
|
|
||||||
local LauncherMods = require("src.mods.LauncherMods")
|
local LauncherMods = require("src.mods.LauncherMods")
|
||||||
local SaveData = require("src.core.SaveData")
|
local prof = profileState(imp)
|
||||||
local options = SaveData.loadOptions()
|
local options, profiles = prof.options, prof.list
|
||||||
local profiles, active = LauncherMods.getProfiles(options)
|
|
||||||
|
|
||||||
local pad = math.floor(18 * m.s)
|
local pad = math.floor(18 * m.s)
|
||||||
local w = math.min(math.floor(380 * m.s), m.w - 2 * m.pad)
|
local w = math.min(math.floor(380 * m.s), m.w - 2 * m.pad)
|
||||||
@@ -3715,6 +3725,7 @@ local function buildSingleProfileActionsModal(imp, m)
|
|||||||
action = function()
|
action = function()
|
||||||
LauncherMods.duplicateProfile(pName, options)
|
LauncherMods.duplicateProfile(pName, options)
|
||||||
imp._singleProfileActions = nil
|
imp._singleProfileActions = nil
|
||||||
|
if imp._refreshMods then imp:_refreshMods() end
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3736,6 +3747,7 @@ local function buildSingleProfileActionsModal(imp, m)
|
|||||||
imp:pressDelete("profile", pName, nil, function()
|
imp:pressDelete("profile", pName, nil, function()
|
||||||
LauncherMods.deleteProfile(pName, options)
|
LauncherMods.deleteProfile(pName, options)
|
||||||
imp._singleProfileActions = nil
|
imp._singleProfileActions = nil
|
||||||
|
if imp._refreshMods then imp:_refreshMods() end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
@@ -3771,9 +3783,8 @@ end
|
|||||||
-- Modal for Mod Profiles (#593) - interactive profile manager (switch, edit, duplicate, delete)
|
-- Modal for Mod Profiles (#593) - interactive profile manager (switch, edit, duplicate, delete)
|
||||||
local function buildProfilesModal(imp, m)
|
local function buildProfilesModal(imp, m)
|
||||||
local LauncherMods = require("src.mods.LauncherMods")
|
local LauncherMods = require("src.mods.LauncherMods")
|
||||||
local SaveData = require("src.core.SaveData")
|
local prof = profileState(imp)
|
||||||
local options = SaveData.loadOptions()
|
local options, profiles, active = prof.options, prof.list, prof.active
|
||||||
local profiles, active = LauncherMods.getProfiles(options)
|
|
||||||
|
|
||||||
local pad = math.floor(18 * m.s)
|
local pad = math.floor(18 * m.s)
|
||||||
local w = math.min(math.floor(460 * m.s), m.w - 2 * m.pad)
|
local w = math.min(math.floor(460 * m.s), m.w - 2 * m.pad)
|
||||||
|
|||||||
@@ -4803,6 +4803,7 @@ function RomImporter:_refreshMods()
|
|||||||
local LauncherMods = require("src.mods.LauncherMods")
|
local LauncherMods = require("src.mods.LauncherMods")
|
||||||
local SaveData = require("src.core.SaveData")
|
local SaveData = require("src.core.SaveData")
|
||||||
self._cartPlan = nil
|
self._cartPlan = nil
|
||||||
|
self._profileCache = nil
|
||||||
self.findInstalled = nil
|
self.findInstalled = nil
|
||||||
self.safeMode = SaveData.isSafeMode(SaveData.loadOptions())
|
self.safeMode = SaveData.isSafeMode(SaveData.loadOptions())
|
||||||
-- Once per session, ahead of the first listing: pull in any mod the player
|
-- Once per session, ahead of the first listing: pull in any mod the player
|
||||||
|
|||||||
+17
-3
@@ -264,7 +264,9 @@ ShaderFX.BRIDGE_DIR = "tools/shaderfx-bridge"
|
|||||||
local function libNames()
|
local function libNames()
|
||||||
local osName = (love and love.system and love.system.getOS
|
local osName = (love and love.system and love.system.getOS
|
||||||
and love.system.getOS()) or ""
|
and love.system.getOS()) or ""
|
||||||
if osName == "Windows" then
|
if osName == "iOS" then
|
||||||
|
return {}
|
||||||
|
elseif osName == "Windows" then
|
||||||
return { "librashader_bridge.dll" }
|
return { "librashader_bridge.dll" }
|
||||||
elseif osName == "OS X" then
|
elseif osName == "OS X" then
|
||||||
return { "liblibrashader_bridge.dylib", "librashader_bridge.dylib" }
|
return { "liblibrashader_bridge.dylib", "librashader_bridge.dylib" }
|
||||||
@@ -313,11 +315,13 @@ end
|
|||||||
|
|
||||||
-- Every place the bridge may sit, most specific first.
|
-- Every place the bridge may sit, most specific first.
|
||||||
local function libCandidates()
|
local function libCandidates()
|
||||||
|
local names = libNames()
|
||||||
|
if #names == 0 then return {} end
|
||||||
local out = {}
|
local out = {}
|
||||||
local override = os.getenv("LIBRASHADER_BRIDGE_DLL")
|
local override = os.getenv("LIBRASHADER_BRIDGE_DLL")
|
||||||
if override and override ~= "" then out[#out + 1] = override end
|
if override and override ~= "" then out[#out + 1] = override end
|
||||||
local dirs, save = sourceDirs(), saveDir()
|
local dirs, save = sourceDirs(), saveDir()
|
||||||
for _, name in ipairs(libNames()) do
|
for _, name in ipairs(names) do
|
||||||
for _, dir in ipairs(dirs) do
|
for _, dir in ipairs(dirs) do
|
||||||
out[#out + 1] = dir .. "/" .. name
|
out[#out + 1] = dir .. "/" .. name
|
||||||
out[#out + 1] = dir .. "/" .. ShaderFX.BRIDGE_DIR .. "/target/release/" .. name
|
out[#out + 1] = dir .. "/" .. ShaderFX.BRIDGE_DIR .. "/target/release/" .. name
|
||||||
@@ -353,7 +357,17 @@ local function ensureLib()
|
|||||||
end
|
end
|
||||||
tried[#tried + 1] = path
|
tried[#tried + 1] = path
|
||||||
end
|
end
|
||||||
libError = "librashader bridge not found; looked in " .. table.concat(tried, ", ")
|
local okSym, sym = pcall(function()
|
||||||
|
return ffi.C and ffi.C.librashader_translate_preset
|
||||||
|
end)
|
||||||
|
if okSym and sym ~= nil then
|
||||||
|
lib = ffi.C
|
||||||
|
return lib
|
||||||
|
end
|
||||||
|
libError = "librashader bridge not found; ffi.C has no librashader_translate_preset"
|
||||||
|
if #tried > 0 then
|
||||||
|
libError = libError .. "; looked in " .. table.concat(tried, ", ")
|
||||||
|
end
|
||||||
return nil, libError
|
return nil, libError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||||
|
|
||||||
|
local T = require("tests.harness")
|
||||||
|
|
||||||
|
love = love or require("tests.love_stub")
|
||||||
|
|
||||||
|
local NAME = "src.render.ShaderFX"
|
||||||
|
|
||||||
|
local function withFfi(fakeFfi, os, fn)
|
||||||
|
local oldModule = package.loaded[NAME]
|
||||||
|
local oldFfi = package.loaded.ffi
|
||||||
|
local oldPreload = package.preload.ffi
|
||||||
|
local oldGetOS = love.system.getOS
|
||||||
|
if os then love.system.getOS = function() return os end end
|
||||||
|
package.loaded[NAME] = nil
|
||||||
|
package.loaded.ffi = nil
|
||||||
|
package.preload.ffi = function() return fakeFfi end
|
||||||
|
local ShaderFX = require(NAME)
|
||||||
|
local can, err = ShaderFX.canConvert(), ShaderFX.bridgeError()
|
||||||
|
if fn then fn(ShaderFX) end
|
||||||
|
package.loaded[NAME] = oldModule
|
||||||
|
package.loaded.ffi = oldFfi
|
||||||
|
package.preload.ffi = oldPreload
|
||||||
|
love.system.getOS = oldGetOS
|
||||||
|
return can, err, ShaderFX
|
||||||
|
end
|
||||||
|
|
||||||
|
local function baseFfi()
|
||||||
|
return {
|
||||||
|
cdef = function() end,
|
||||||
|
load = function() error("no such library") end,
|
||||||
|
string = function(value) return value end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local staticFfi = baseFfi()
|
||||||
|
staticFfi.C = {
|
||||||
|
librashader_translate_preset = function() return "{}" end,
|
||||||
|
librashader_free_string = function() end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local can, err = withFfi(staticFfi)
|
||||||
|
T.eq(can, true, "a statically linked bridge is found through ffi.C")
|
||||||
|
T.eq(err, nil, "the ffi.C fallback leaves no bridge error behind")
|
||||||
|
|
||||||
|
local emptyFfi = baseFfi()
|
||||||
|
emptyFfi.C = setmetatable({}, {
|
||||||
|
__index = function() error("undefined symbol") end,
|
||||||
|
})
|
||||||
|
|
||||||
|
local missing, missingErr = withFfi(emptyFfi)
|
||||||
|
T.eq(missing, false, "no library and no ffi.C symbol means no conversion")
|
||||||
|
T.check(missingErr and missingErr:find("ffi.C has no librashader_translate_preset", 1, true) ~= nil,
|
||||||
|
"libError says ffi.C was probed too (got " .. tostring(missingErr) .. ")")
|
||||||
|
T.check(missingErr and missingErr:find("looked in", 1, true) ~= nil,
|
||||||
|
"libError still lists the paths that were tried")
|
||||||
|
|
||||||
|
local iosMissing, iosErr = withFfi(baseFfi(), "iOS")
|
||||||
|
T.eq(iosMissing, false, "iOS with no static symbol cannot convert")
|
||||||
|
T.check(iosErr and iosErr:find("ffi.C has no librashader_translate_preset", 1, true) ~= nil,
|
||||||
|
"iOS libError names the ffi.C probe")
|
||||||
|
T.check(iosErr and iosErr:find("looked in", 1, true) == nil,
|
||||||
|
"iOS lists no file candidates (got " .. tostring(iosErr) .. ")")
|
||||||
|
|
||||||
|
local iosCan = withFfi(staticFfi, "iOS")
|
||||||
|
T.eq(iosCan, true, "iOS resolves the bridge through ffi.C alone")
|
||||||
|
|
||||||
|
local freed = false
|
||||||
|
local translateFfi = baseFfi()
|
||||||
|
translateFfi.C = {
|
||||||
|
librashader_translate_preset = function(path, es)
|
||||||
|
return ('{"pass_count":1,"passes":[{"name":"%s","es":%d}]}'):format(path, es)
|
||||||
|
end,
|
||||||
|
librashader_free_string = function() freed = true end,
|
||||||
|
}
|
||||||
|
withFfi(translateFfi, "iOS", function(ShaderFX)
|
||||||
|
local preset, terr = ShaderFX.translate("/presets/a.slangp", true)
|
||||||
|
T.check(preset ~= nil, "translate works with lib == ffi.C (" .. tostring(terr) .. ")")
|
||||||
|
T.eq(preset and preset.passes[1].name, "/presets/a.slangp",
|
||||||
|
"the ffi.C symbol receives the preset path")
|
||||||
|
T.eq(preset and preset.passes[1].es, 1, "the es flag reaches the ffi.C symbol")
|
||||||
|
T.eq(freed, true, "the returned string is freed through ffi.C")
|
||||||
|
end)
|
||||||
|
|
||||||
|
T.finish("shaderfx ffi.C fallback")
|
||||||
@@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "librashader_bridge"
|
name = "librashader_bridge"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "staticlib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
librashader-preprocess = "0.12.0"
|
librashader-preprocess = "0.12.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user