mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-23 09:01:44 +02:00
The old radius setting exported FOVE_INNER/FOVE_OUTER, which Lepton doesn't pass into the container and Valve's foveation layer doesn't read, so every size looked the same. Game settings now offers Default (large) / Large / Medium / Small, exported as FOVE_LEVEL=lo/med/hi (the layer's own preset names); saved radius choices map to the nearest preset. The agent's launcher update accepts FOVE_LEVEL.
204 lines
9.9 KiB
Python
204 lines
9.9 KiB
Python
"""Shared injection settings and reversible APK diagnostic integration."""
|
|
import os
|
|
import zipfile
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from frameport.analysis import elf
|
|
from frameport.analysis.stubgen import build_stub_library
|
|
from frameport.apk.workspace import ApkWorkspace
|
|
from frameport.build import apk_patches
|
|
from frameport.core.events import Reporter
|
|
from frameport.core.models import Recipe
|
|
from frameport.install.installer import install_context
|
|
from frameport.patches import base
|
|
from frameport.patches.frame import foveation
|
|
from frameport.patches.settings import adapter_settings, foveation_options
|
|
|
|
|
|
def context(params, **patches):
|
|
return install_context(Recipe("test.game", patches={"device.foveation": params, **patches}))
|
|
|
|
|
|
def test_default_profile_and_conflicting_native_path():
|
|
ctx = context({"mode": "tracked"}, **{
|
|
"device.lepton_env": {"env": {"FDM_DEBUG": "disable_offsets,debug", "VK_INSTANCE_LAYERS": "",
|
|
"LEPTON_ENV_DISABLE_VULKAN_FDM_INJECTION_LAYER": "1"}},
|
|
"adapter.foveation_fix": {"value": 0}})
|
|
assert ctx.env == {"ENABLE_VULKAN_FDM_INJECTION_LAYER": "1", "FDM_DEBUG": "enable"}
|
|
assert ctx.adapter_settings["foveation_fix"] == 1
|
|
assert adapter_settings({"adapter.foveation_fix": {"value": 0}})["foveation_fix"] == 0
|
|
|
|
|
|
def test_injection_keeps_swapchains_ordinary():
|
|
# GitHub #303: a SUBSAMPLED eye image under Valve's FDM showed garbage around the sharp area
|
|
for mode in ("tracked", "fixed"):
|
|
assert adapter_settings({"device.foveation": {"mode": mode}})["foveation_storage_fix"] == 1
|
|
for mode in ("", "off"):
|
|
assert "foveation_storage_fix" not in adapter_settings({"device.foveation": {"mode": mode}})
|
|
src = (Path(__file__).resolve().parents[1] / "native/adapter/frame_adapter.c").read_text()
|
|
assert "if (!hide_fb_foveation_vulkan && foveation_storage_fix) {" in src # not only when foveation_fix is off
|
|
|
|
|
|
def test_custom_size_and_fixed_mode_and_reset():
|
|
assert context({"mode": "tracked", "test": True}).env["LEPTON_ENV_FRAMEPORT_FOVEATION_TEST"] == "1"
|
|
assert "LEPTON_ENV_FRAMEPORT_FOVEATION_TEST" not in context({"mode": "tracked"}).env
|
|
assert context({"mode": "tracked", "radius": 0.35}).env["FOVE_LEVEL"] == "lo"
|
|
assert context({"mode": "tracked", "level": "hi"}).env["FOVE_LEVEL"] == "hi"
|
|
assert context({"mode": "fixed"}).env["FDM_DEBUG"] == "enable,disable_offsets"
|
|
assert context({"mode": "off"}).env == {
|
|
"VK_INSTANCE_LAYERS": "", "LEPTON_ENV_DISABLE_VULKAN_FDM_INJECTION_LAYER": "1"}
|
|
assert not context({"mode": "tracked"}, **{
|
|
"device.lepton_env": {"env": {"FOVE_INNER": "0.1", "FOVE_OUTER": "0.2"}}}).env.keys() & {
|
|
"FOVE_INNER", "FOVE_OUTER"}
|
|
|
|
|
|
@pytest.mark.parametrize("params", [
|
|
{"mode": "bad"}, {"mode": "off", "test": True}, {"mode": "", "radius": 0.2},
|
|
{"mode": "tracked", "level": "1"}, {"mode": "tracked", "level": 2},
|
|
{"mode": "off", "level": "lo"}, {"mode": "tracked", "test": "false"}])
|
|
def test_invalid_configuration_refused(params):
|
|
with pytest.raises(ValueError):
|
|
foveation_options(params)
|
|
|
|
|
|
def test_optional_layer_is_built_and_idempotent(tmp_path, quest_manifest):
|
|
source = tmp_path / "source.apk"
|
|
engine = build_stub_library(["game_main"], soname="libUE4.so")
|
|
with zipfile.ZipFile(source, "w") as z:
|
|
z.writestr("AndroidManifest.xml", quest_manifest)
|
|
z.writestr("lib/arm64-v8a/libUE4.so", engine)
|
|
patch = base.get("device.foveation")
|
|
assert patch in apk_patches({patch.id: {"mode": "tracked", "test": True}})
|
|
with ApkWorkspace(source) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="test.game"), {"mode": "tracked"}, Reporter(), {})
|
|
assert not patch.apply(ctx)
|
|
assert not ws.has(ws.lib(foveation.LAYER))
|
|
ctx.params["test"] = True
|
|
assert patch.apply(ctx)
|
|
assert foveation.LAYER in elf.needed(ws.read_lib("libUE4.so"))
|
|
assert not patch.apply(ctx)
|
|
assert all(ok for _, ok, _ in patch.validate(ctx))
|
|
# A normal rebuild from the unchanged source drops the optional diagnostic.
|
|
with ApkWorkspace(source) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="test.game"), {"mode": "tracked"}, Reporter(), {})
|
|
assert not patch.apply(ctx)
|
|
assert foveation.LAYER not in elf.needed(ws.read_lib("libUE4.so"))
|
|
|
|
|
|
def test_unrecognized_batman_library_refused():
|
|
with pytest.raises(ValueError, match="unsupported OVRPlugin"):
|
|
foveation.ordinary_storage(build_stub_library(["ovrp_fake"], soname="libOVRPlugin.so"))
|
|
|
|
|
|
@pytest.mark.parametrize("mode", ["tracked", "fixed"])
|
|
def test_climb_launcher_isolation(mode):
|
|
patch = {"device.foveation": {"mode": mode}}
|
|
climb = install_context(Recipe("com.crytek.climb2", patches=patch))
|
|
assert climb.env["FDM_DEBUG"].endswith(",yflip")
|
|
assert climb.env["LEPTON_ENV_FRAMEPORT_CLIMB_STEREO_OFFSETS"] == "1"
|
|
for package in ("com.camouflaj.manta", "test.re4", "test.deadpool", "test.asgard"):
|
|
other = install_context(Recipe(package, patches=patch))
|
|
assert not any("CLIMB" in key or "VRAPI" in key for key in other.env)
|
|
assert "yflip" not in other.env["FDM_DEBUG"]
|
|
with pytest.raises(ValueError, match="1.0 resolution"):
|
|
install_context(Recipe("com.crytek.climb2", patches={
|
|
**patch, "adapter.scale": {"value": 1.2}}))
|
|
|
|
|
|
def test_climb_private_bridge_guard_and_rebuild(tmp_path, quest_manifest, monkeypatch):
|
|
import hashlib
|
|
|
|
source = tmp_path / "climb.apk"
|
|
renderer = build_stub_library(["vkCreateInstance"], soname="libCryRenderVulkan.so")
|
|
original = build_stub_library(["vrapi_Initialize"], soname="libvrapi.so")
|
|
with zipfile.ZipFile(source, "w") as z:
|
|
z.writestr("AndroidManifest.xml", quest_manifest)
|
|
z.writestr("lib/arm64-v8a/libCryRenderVulkan.so", renderer)
|
|
z.writestr("lib/arm64-v8a/libvrapi.so", original)
|
|
patch = base.get("device.foveation")
|
|
# The private bridge must survive the regular bridge replacement.
|
|
assert patch.order > base.get("frame.vrapi_bridge").order
|
|
with ApkWorkspace(source) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="com.crytek.climb2"),
|
|
{"mode": "tracked"}, Reporter(), {})
|
|
with pytest.raises(ValueError, match="unsupported renderer"):
|
|
patch.apply(ctx)
|
|
assert ws.read_lib("libvrapi.so") == original
|
|
monkeypatch.setattr(foveation, "CLIMB_RENDERER", hashlib.sha256(renderer).hexdigest())
|
|
assert patch.apply(ctx)
|
|
assert foveation.LAYER in elf.needed(ws.read_lib("libvrapi.so"))
|
|
assert not patch.apply(ctx)
|
|
assert all(ok for _, ok, _ in patch.validate(ctx))
|
|
with ApkWorkspace(source) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="com.crytek.climb2"), {}, Reporter(), {})
|
|
assert patch.apply(ctx) # support is prepared even with the runtime preference at Default
|
|
assert foveation.LAYER in elf.needed(ws.read_lib("libvrapi.so"))
|
|
assert all(ok for _, ok, _ in patch.validate(ctx))
|
|
# Off retains verified APK support; the personal setting disables injection through the launcher.
|
|
with ApkWorkspace(source) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="com.crytek.climb2"),
|
|
{"mode": "off"}, Reporter(), {})
|
|
assert patch.apply(ctx)
|
|
assert ws.read_lib("libvrapi.so") != original
|
|
assert ws.has(ws.lib(foveation.LAYER))
|
|
|
|
|
|
def test_customize_does_not_offer_foveation_choice(monkeypatch):
|
|
import flet as ft
|
|
|
|
from frameport.core import library
|
|
from frameport.ui.views import game
|
|
|
|
recipe = Recipe("test.game", patches={"device.foveation": {"mode": "tracked"}, "frame.adapter": {}})
|
|
saved = []
|
|
monkeypatch.setattr(game.engine, "visible_patches", lambda *a: ([base.get("device.foveation")], []))
|
|
monkeypatch.setattr(library, "analysis_from_dict", lambda a: None)
|
|
monkeypatch.setattr(game.pipeline, "set_recipe", lambda package, r: saved.append(library.recipe_to_dict(r)))
|
|
monkeypatch.setattr(ft.Text, "update", lambda self: None)
|
|
view = SimpleNamespace(app=SimpleNamespace(has_game_settings=lambda g: False, open_game=lambda *a, **kw: None),
|
|
g={"recipe": library.recipe_to_dict(recipe), "analysis": {}},
|
|
package="test.game", show_all=False)
|
|
panel = game.GameView.advanced_panel(view)
|
|
|
|
def walk(control):
|
|
yield control
|
|
for child in getattr(control, "controls", []) or []:
|
|
yield from walk(child)
|
|
if getattr(control, "content", None) is not None:
|
|
yield from walk(control.content)
|
|
|
|
controls = list(walk(panel))
|
|
assert not any(isinstance(c, ft.Dropdown) and c.value == "tracked" for c in controls)
|
|
assert not saved
|
|
|
|
|
|
def test_verified_batman_storage_and_other_byte_change_rejected():
|
|
path = os.environ.get("FRAMEPORT_BATMAN_APK")
|
|
if not path:
|
|
pytest.skip("Optional local Batman APK")
|
|
with zipfile.ZipFile(Path(path)) as z:
|
|
original = z.read("lib/arm64-v8a/libOVRPlugin.so")
|
|
fixed = foveation.ordinary_storage(original)
|
|
assert original != fixed and foveation.ordinary_storage(fixed) == fixed
|
|
assert sum(a != b for a, b in zip(original, fixed, strict=True)) == 3
|
|
corrupt = bytearray(fixed)
|
|
corrupt[-1] ^= 1
|
|
with pytest.raises(ValueError, match="verified allocation fix"):
|
|
foveation.ordinary_storage(bytes(corrupt))
|
|
|
|
|
|
def test_verified_climb_apk():
|
|
path = os.environ.get("FRAMEPORT_CLIMB_APK")
|
|
if not path:
|
|
pytest.skip("Optional local Climb APK")
|
|
with ApkWorkspace(Path(path)) as ws:
|
|
ctx = base.ApkContext(ws, SimpleNamespace(package="com.crytek.climb2"),
|
|
{"mode": "tracked"}, Reporter(), {})
|
|
patch = base.get("device.foveation")
|
|
assert patch.apply(ctx)
|
|
assert all(ok for _, ok, _ in patch.validate(ctx))
|
|
assert not patch.apply(ctx)
|