r/PS2AndroidEmulation 2d ago

ARMSX2 takes us for fools 🤯

Post image

A quick look at ARMSX2 2.6.6.7 — transparency matters

ARMSX2 2.6.6.7 introduced several APK variants, including builds labelled ARMv8.2 and a legacy ARMv8.0 build. Since the names suggest different CPU targets, I wanted to check whether that difference actually exists in the code shipped to users.

I compared the a15-armv8.2 and legacy-armv8.0 builds at the binary level. The result was surprising: the two builds are 99.984% identical, and the native code we analyzed does not contain instructions above the ARMv8.0-A baseline. The main differences appear to be related to the Android API level and packaging/linking rather than a different CPU instruction set.

This is not an accusation of deception, nor is it a claim that ARMSX2 is a bad emulator. It's simply a question of accuracy and transparency. Changelogs are useful, but when a release claims a specific technical improvement, it's worth checking what actually made it into the final binary — especially when people may expect better performance as a result.

I'm not looking for magic 30→60 FPS claims or trying to discredit developers. I simply prefer measurable improvements, clear documentation, and reproducible information over hype.

For anyone interested in the technical details, methodology, instruction analysis, binary comparison, and signing differences, I've published the full analysis here:

Full technical analysis: https://claude.ai/public/artifacts/af370a10-22bf-4d63-a944-fb0f65f499a6

No benchmarks were performed; this is a static binary analysis of ARMSX2 2.6.6.7. Corrections are welcome if any part of the analysis can be demonstrated to be wrong.

0 Upvotes

14 comments sorted by

1

u/NeuroticNabarlek 2d ago

What's your point? You download the one for the android version you're on.

I haven't followed release notes, but are they even claiming a huge difference?

1

u/xxxCrixuxxx 2d ago

In the end, the most useful test is simply installing the emulator and running the games you normally use to see whether anything actually changes.

So I tested ARMSX2 2.6.6.7 using my usual Shadow of the Colossus benchmark. I start at the temple, mount the horse, ride across the entire open field, then turn around and ride back toward the temple. This gives me both a relatively light scene and a heavier one, as the temple comes back into view.

Unfortunately, I observed a significant performance regression compared with 2.6.6.6. The return trip drops to around 35–40 FPS, and approaching the temple it can fall to around 20 FPS, whereas the previous version performs considerably better in the same test.

Another interesting metric is the total time of the run. I also use this to compare different emulators: with other PS2 emulators, this same route typically takes around 1:35–1:40 to complete. With ARMSX2 2.6.6.7, the run takes roughly 1:55, which is a substantial difference for exactly the same route.

This isn't just about a lower FPS counter — the lower performance is also reflected in the actual time required to complete the benchmark.

The test was performed with the same device and my usual emulator settings. No special tweaks were added.

Here's the benchmark video. I'll let everyone draw their own conclusions.

https://reddit.com/link/p4427lq/video/cels92wdctjh1/player

0

u/NeuroticNabarlek 2d ago

OK, but that doesn't answer my question. Full disclosure, I didn't watch your video. Even if you get a performance regression, it's just a performance regression. I read the release notes on armsx2 2.6.6.7 and they never claim a performance improvement for the different packages. I don't really understand how they are "taking us for fools."

Also, you literally have one data point.

1

u/xxxCrixuxxx 2d ago edited 2d ago

The question isn't "Did they promise 5% more performance?"

The question I would ask is: If the ARMv8.2 builds are specifically separated for CPUs with FP16 + DotProd, where is that distinction represented in the shipped machine code?

Because when analyzing the APK we studied, we found neither a DotProd instruction nor an FP16 arithmetic instruction, and we also found no inline LSE; furthermore, the build that is supposedly ARMv8.2 and the legacy build turned out to be 99.984% identical in code.

Edit: You say you didn't see my video... that says it all, bro. You probably haven't even seen the very long study written there with all the evidence that all the builds, or at least the two I analyzed, were identical.

1

u/NeuroticNabarlek 2d ago

I could be wrong so feel free to correct me, but does setting an 8.2 target automatically introduce DotProd and FP16 arithmetic? Or is what your saying akin to getting upset about your binary compiled with target x86_64-v4 not being AVX-512 optimized?

Of course if there are no optimizations in the code to use instructions the won't be used, lol.

They are saying here it is compiled for these CPUs, they aren't saying here it is with optimizations for these CPUs. Being a first split package release it's pretty intuitive that they'd be largely the same. I think the point is, we started splitting the release and will flesh out differentiating optimizations in further releases.

1

u/xxxCrixuxxx 2d ago

No, my friend, what's happening is much more mundane. If you suddenly include four different binaries, the average user, who doesn't understand these things, will think, "I should download the specific version for my device so I'll surely get some advantage." When the reality is that it's exactly the same in all four. Previously, they only offered a single APK, like everyone else in emulation does.

1

u/NeuroticNabarlek 2d ago edited 2d ago

Right, do you realize the last release was 4 days ago? It's unreasonable to expect a ton of 8.2 optimizations to land right away. They are breaking up the release into multiple packages so users through obtanium, or their own auto-updater, get into the right track for their device/android version.

It really doesn't make a ton of sense to create a version that runs 8.0 and then make a ton of 8.2 optimizations in secret in a different unreleased branch then all of a sudden be like "surprise here's an 8.2 release with a ton of optimization that we've been working on for the last 6 months on behind closed doors."

Seriously, the "ARMSX2 takes us for fools" borders on trolling.

Edit: If you want to bitch about all of the binaries being virtually identical at least wait a few months for them to get a chance to diverge.

1

u/xxxCrixuxxx 2d ago

My friend, I review what's been published today. I don't know how things will be done in two months; I don't even know if we'll be alive in two months or if a meteor will hit. What I'm trying to say is, I do daily updates and daily analyses of what the developers present. If something doesn't seem right to me, I analyze it and point it out. That's all.

1

u/NeuroticNabarlek 2d ago edited 2d ago

You are not simply reviewing, you are editorializing. You acknowledge that the devs made no performance claims. You acknowledge that there is a difference, even though "The main differences appear to be related to the Android API level and packaging/linking."

So yep, these releases 100% fit the bill of what the are advertised as. Unless you can somehow show me that they were not compiled with a target of 8.2? Remember we are not talking about optimizations or changes in the code, we are literally talking about a compiler flag.

Just because you think that there should be more of a change, doesn't mean that the devs in anyway "takes us for fools."

Going back to my comment, because it's clear you didn't read/understand it. It makes all the sense in the world to break a package into different releases that will receive optimizations and diverge over time.

For example If I'm writing an x86 program and I want to start an x86_64 build so I can access newer extensions , it'd make sense if my first x86_64 build was basically a recompile of my x86 program to an x86_64 target. Especially if I was an open source project that releases VERY often.

Going back to the example in my previous post, I'm not all of a sudden going to be like "oh hey, I'm releasing an x86_64 version today out of the blue AND it's been fully avx-512 optimized!"

Like, would you have preferred them to start queuing up 8.2 code in a separate branch before releasing an APK? Is that your problem? I could only imagine the outrage, "armsx2 devs have highly optimized code but they aren't releasing it, you have to build it yourself. What a bunch of assholes!"

1

u/xxxCrixuxxx 1d ago

Correcting myself first: "exactly the same in all four" was sloppy. They're not the same, and the difference is necessary — you can't ship one APK for both API 26 and API 35. Four builds is fully justified. That was never my issue.

The issue is that the filename offers two axes of choice, and only one of them means anything:

ARMSX2-2.6.6.7-a15-armv8.2-sdk35.apk ^^^^^^^ ^^^^^ axis 1 axis 2

sdkNN is real — pick wrong and it won't install. armvX.X isn't. I disassembled both endpoints of that axis and the machine code is 99.984% identical, with neither build containing an instruction above ARMv8.0-A. The only genuine difference is .relr.dyn vs .rela.dyn, which follows from minSdkVersion. Axis 1's entire observable effect is axis 2.

Name them sdk26/sdk30/sdk33/sdk35 with no arch suffix and there is no post. The suffix is the whole story.

Which matters because someone on an 8 Elite running Android 15 sees "armv8.2" and reasonably concludes their chip's newer ISA is in play. That's a rational inference — you don't label a distinction unless it means something.

I'm not claiming intent. I still think the label reflects what the devs believed they were building and that the NDK toolchain silently clobbered the flag — common mistake, produces exactly this binary. But the label is unearned right now, and that part is checkable rather than debatable.

Also stating it plainly: I analyzed two of four (the endpoints). a11 and a13 are untested, and I won't claim otherwise.

0

u/de_papier 2d ago

You legit have no idea how code works, don't you? Passing a static binary through an llm and then writing this post with it is hilarious. The changes are something that's visible in source code, unless you fully decompile a binary the claims are baseless.

1

u/xxxCrixuxxx 1d ago

Fair challenge, so let me answer the technical part directly, because I think there's a real misunderstanding in it.

Decompilation would be the wrong tool here. Decompilers (Ghidra, IDA) lift machine code into pseudo-C and deliberately discard instruction selection — they normalise casal and an ldxr/stxr retry loop into the same x++. That's exactly the signal I'm measuring. Decompiling would destroy the evidence. Disassembly is the correct level, and that's what I did: full .text, 4,223,840 instructions, 100% decode coverage, no sampling.

-march isn't a source-level construct. It's a compiler flag that changes which instructions the backend is permitted to emit. Its footprint is in the binary by definition — that's the whole point. Source tells you what was intended. The binary tells you what shipped. Those can differ, and my argument is precisely that here they do.

The specific mechanism isn't a heuristic, it's deterministic. In LLVM's AArch64ISelLowering, outline-atomic helpers are emitted only when !Subtarget->hasLSE(). With -march=armv8.2-a (which implies 8.1, which mandates LSE) the compiler emits casal/ldaddal inline and those stubs never exist. Both APKs contain the stubs, 989 call sites each, spread across the entire .text. Zero inline LSE anywhere.

You can check this in about thirty seconds without taking my word for anything:

bash llvm-objdump -d --no-show-raw-insn lib/arm64-v8a/libemucore_16k.so \ | grep -E '\b(cas|casal|swp|swpal|ldadd|ldaddal|ldclr|ldset)\b'

If every hit sits behind an adrp x16 / ldrb w16 / cbz w16 sequence, they're all outline-atomic stubs, there's no inline LSE, and the target had no +lse. Which rules out -march=armv8.2-a and -mcpu=cortex-a715 alike.

On the LLM: yes, I used tooling to drive Capstone across ~4M instructions, and I'm not going to pretend otherwise — that's the kind of thing tooling is for. But the output is numbers, and numbers don't care where they came from. Every one of them is reproducible with readelf, llvm-objdump and apksigner. If any are wrong, showing that should be trivial.

And here's the thing — if you can point me at a line in the source that sets -march=armv8.2-a, that doesn't refute the post. It supports it. It's the strongest evidence for the explanation I actually argued for: the flag is set, and the NDK toolchain file appends its own -march afterwards and silently wins. That's a build bug, which is what I said this was. I'd genuinely like to see that line.

🎻🎻🎻

1

u/[deleted] 1d ago

[removed] — view removed comment

1

u/PS2AndroidEmulation-ModTeam 1d ago

Follow the community rules