Skip to content

Run Unity 2D projects as Codename One applications - #5976

Open
shai-almog wants to merge 19 commits into
masterfrom
unity-compat
Open

shai-almog wants to merge 19 commits into
masterfrom
unity-compat

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Summary

A Unity 2D project (C# scripts, scenes, prefabs, sprites, project settings) dropped into common/src/main/unity, or imported with cn1:import-unity-project, builds unmodified as a Codename One application for every target. No Unity player and no .NET runtime ship with the app.

  • maven/cil-translator: reads the assembly the C# compiler produced and writes one class file per C# type; compiles scenes, prefabs, animator controllers, clips, tile maps and particle systems to reflection-free Java (UnityAppImpl).
  • maven/unity-compat: the runtime. A .NET base library in Java, the UnityEngine API over com.codename1.gaming (2D rendering, Box2D physics with exact casts and overlap queries, Animator, tile maps, particles, uGUI, touch, audio, PlayerPrefs, Resources), plus our own implementations of the documented TextMesh Pro and Cinemachine API.
  • Build wiring: UnityProjectBuilder in build-engine, the compile-unity and import-unity-project Maven goals, the Gradle compileUnity task, the archetype profile, and release publishing behind -Dunity-compat (the modules need the .NET SDK; the framework build does not).
  • Java interop: UnityApplication (createForm, onProjectInstalled, onStarted, callInFrame), so a game sits inside a Codename One form and uses the rest of the framework.
  • Docs: a new developer guide chapter, Unity-Interop.asciidoc.

Clean room: no Unity source, assemblies or package source were read or shipped; the stub UnityEngine.dll is compiled from our own sources.

Changes outside the new modules

Bugs this work uncovered, fixed at the root, each with a regression test:

  • ParparVM JavaScript: wide stack shuffles (POP2/DUP2/DUP_X2), MULTIANEWARRAY dimension pops, a call result compared via FCMP/DCMP evaluated more than once, SWAP losing the lower value.
  • ParparVM native: arraycopy/clone of an array of primitive arrays took the primitive path.
  • GameView: a key repeat released a held key after 800 ms; a focused hosted child kept the multi-touch arrays from the view.
  • Box2D: chain against chain threw in World.popContact; the broad phase allocated a merge buffer every step above 31 pairs (now an in-place sort, checked against Arrays.sort over randomized pair sets).

Behavior changes that affect every application:

  • Native builds compile generated C with -ffp-contract=off.
  • The JavaScript target rounds every float result with Math.fround.

Both make float arithmetic match the JVM bit for bit. Their performance cost on ordinary applications has not been measured.

Verification

  • Seven in-tree samples (scripts/unity-compat-samples) produce byte-identical traces on the JVM, ParparVM native and JavaScript (maven/integration-tests/unity-compat-test.sh --parparvm).
  • Five open-source Unity games build unmodified with identical traces on all three targets: Fruitopia, Asteroids, Pong, Snake, Sokoban. They are not in the tree.
  • cil-translator and unity-compat: SpotBugs 0, Checkstyle 0, PMD forbidden list clean, cast-semantics clean.
  • Core unit tests: 7,702 run, 1 failure (MapViewMarkerLabelTest timeout on a loaded machine; passes alone).
  • Developer guide gates pass locally (asciidoctor, Vale, LanguageTool, snippets, xrefs).

Not verified

  • .github/workflows/unity-compat.yml and the release.yml changes have never run on CI.
  • Nothing has run on a real device; audio, sign-in and share from the bridge samples are untested there.
  • The Java samples in the guide are copies of code that ran, but docs CI does not compile them.
  • The tree was rebased onto current master after the last local test run.
  • Core GameInput still reads edge flags that a press landing mid-frame can miss on ports that render off the EDT; left for a separate decision.

🤖 Generated with Claude Code

A Unity project's Assets and ProjectSettings in common/src/main/unity build
unmodified: the .NET SDK compiles the C# scripts, maven/cil-translator turns
the assembly into Java class files and compiles every scene, prefab,
animator controller, clip, tile map and particle system to reflection-free
Java, and maven/unity-compat is the runtime they run against (a .NET base
library, the UnityEngine API over com.codename1.gaming, and our own
TextMesh Pro and Cinemachine implementations). Maven (compile-unity,
import-unity-project) and Gradle (compileUnity) share UnityProjectBuilder.
Both modules sit behind the unity-compat profile and are published by the
release.

Float results have to match C# on every target, so native builds get
-ffp-contract=off and the JavaScript target rounds float results with
Math.fround.

Bugs this uncovered and fixes outside the new modules, each with a test:
- ParparVM JavaScript: wide stack shuffles, MULTIANEWARRAY dimension pops,
  a call result compared with FCMP/DCMP evaluated more than once, SWAP
  losing the lower value.
- ParparVM native: arraycopy/clone of an array of primitive arrays took
  the primitive path.
- GameView: a key repeat released a held key; a focused hosted child kept
  the multi-touch arrays from the view.
- Box2D: chain against chain threw in World.popContact; the broad phase
  allocated a merge buffer on every step above 31 pairs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 4ms = 13.5x speedup
SIMD float-mul (64K x300) java 56ms / native 3ms = 18.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 69.000 ms
Base64 CN1 decode 85.000 ms
Base64 SIMD encode 98.000 ms
Base64 encode ratio (SIMD/CN1) 1.420x (42.0% slower)
Base64 SIMD decode 93.000 ms
Base64 decode ratio (SIMD/CN1) 1.094x (9.4% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 25.000 ms
Image applyMask (SIMD on) 27.000 ms
Image applyMask ratio (SIMD on/off) 1.080x (8.0% slower)
Image modifyAlpha (SIMD off) 35.000 ms
Image modifyAlpha (SIMD on) 18.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.514x (48.6% faster)
Image modifyAlpha removeColor (SIMD off) 41.000 ms
Image modifyAlpha removeColor (SIMD on) 21.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.512x (48.8% faster)

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 178 screenshots: 178 matched.

Native Android coverage

  • 📊 Line coverage: 17.39% (30435/175026 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 15.69% (143769/916406), branch 10.65% (9883/92802), complexity 8.23% (7398/89894), method 11.98% (5125/42769), class 20.73% (1057/5098)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • com.codename1.androidcompat.rt.com.codename1.androidcompat.rt.TextXmlPullParser – 0.00% (0/623 lines covered)
      • com.codename1.androidcompat.androidx.constraintlayout.core.widgets.com.codename1.androidcompat.androidx.constraintlayout.core.widgets.Flow – 0.00% (0/576 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 17.39% (30435/175026 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 15.69% (143769/916406), branch 10.65% (9883/92802), complexity 8.23% (7398/89894), method 11.98% (5125/42769), class 20.73% (1057/5098)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • com.codename1.androidcompat.rt.com.codename1.androidcompat.rt.TextXmlPullParser – 0.00% (0/623 lines covered)
      • com.codename1.androidcompat.androidx.constraintlayout.core.widgets.com.codename1.androidcompat.androidx.constraintlayout.core.widgets.Flow – 0.00% (0/576 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 201ms / native 204ms = 0.9x speedup
SIMD float-mul (64K x300) java 175ms / native 116ms = 1.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 82.000 ms
Base64 CN1 decode 91.000 ms
Base64 native encode 449.000 ms
Base64 encode ratio (CN1/native) 0.183x (81.7% faster)
Base64 native decode 285.000 ms
Base64 decode ratio (CN1/native) 0.319x (68.1% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ✅ ByteCodeTranslator: 0 findings (no issues)
    • ✅ android: 0 findings (no issues)
    • ✅ android-compat: 0 findings (no issues)
    • ✅ android-res-compiler: 0 findings (no issues)
    • ✅ backend: 0 findings (no issues)
    • ✅ backend-test: 0 findings (no issues)
    • ✅ build-engine: 0 findings (no issues)
    • ✅ build-hint-catalog: 0 findings (no issues)
    • ✅ build-hint-tools: 0 findings (no issues)
    • ✅ codenameone-gradle-plugin: 0 findings (no issues)
    • ✅ codenameone-maven-plugin: 0 findings (no issues)
    • ✅ core-unittests: 0 findings (no issues)
    • ✅ ios: 0 findings (no issues)
    • ✅ javac: 0 findings (no issues)
    • ✅ project-model: 0 findings (no issues)
  • ✅ PMD: 0 findings (no issues) [Report archive]
  • ✅ Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 67ms / native 5ms = 13.4x speedup
SIMD float-mul (64K x300) java 100ms / native 5ms = 20.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 72.000 ms
Base64 CN1 decode 91.000 ms
Base64 SIMD encode 103.000 ms
Base64 encode ratio (SIMD/CN1) 1.431x (43.1% slower)
Base64 SIMD decode 102.000 ms
Base64 decode ratio (SIMD/CN1) 1.121x (12.1% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 26.000 ms
Image createMask (SIMD on) 6.000 ms
Image createMask ratio (SIMD on/off) 0.231x (76.9% faster)
Image applyMask (SIMD off) 43.000 ms
Image applyMask (SIMD on) 36.000 ms
Image applyMask ratio (SIMD on/off) 0.837x (16.3% faster)
Image modifyAlpha (SIMD off) 49.000 ms
Image modifyAlpha (SIMD on) 32.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.653x (34.7% faster)
Image modifyAlpha removeColor (SIMD off) 44.000 ms
Image modifyAlpha removeColor (SIMD on) 27.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.614x (38.6% faster)

ParparVM vs HotSpot (JDK 25): Windows x64

Runner CPU: Intel64 Family 6 Model 106 Stepping 6, GenuineIntel (baseline windows-x64@intel64-family-6-model-106-genuineintel)

1 performance regression:

  • translator (self) on all 4 CPUs: time 0.76x against a 0.63x baseline (+21.2%, tolerance 20%)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 15% (time) / 15% (RAM) away from its baseline in vm/selfhost/perf-baseline/ fails: above it is a regression, below it an improvement that has to be rebaselined (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (WinHelloMain) 4 1.34x (base 1.21x, +10.5%) 0.78x (base 0.76x, +2.2%) ok
translator (self) 4 0.76x (base 0.63x, +21.2%) 0.50x (base 0.54x, -6.1%) REGRESSION (time +21.2%)
intArithmetic 4 1.03x (base 1.03x, +0.1%) 0.06x (base 0.06x, +1.6%) ok
longArithmetic 4 1.00x (base 1.00x, -0.1%) 0.05x (base 0.05x, +0.9%) ok
mathTranscendental 4 0.81x (base 0.81x, -0.1%) 0.06x (base 0.06x, -4.4%) ok
arraySequential 4 1.47x (base 1.43x, +3.4%) 0.38x (base 0.38x, +0.1%) ok
arrayRandom 4 0.99x (base 1.00x, -0.5%) 0.23x (base 0.23x, +0.2%) ok
objectAllocation 4 1.20x (base 1.21x, -1.3%) 0.36x (base 0.32x, +12.4%) ok
valueEscape 4 0.13x (base 0.13x, -0.2%) 0.05x (base 0.04x, +3.4%) ok
hashMapChurn 4 1.17x (base 1.21x, -3.4%) 0.09x (base 0.10x, -10.0%) ok
stringBuilding 4 1.48x (base 1.44x, +3.5%) 0.27x (base 0.27x, -0.5%) ok
recursion 4 1.18x (base 1.26x, -6.2%) 0.06x (base 0.06x, +0.7%) ok
quicksort 4 1.09x (base 1.11x, -1.7%) 0.12x (base 0.12x, +0.4%) ok

A change that moves performance ON PURPOSE records the new baseline in vm/selfhost/perf-baseline/pr/5976.json, with the reason, from this job's perf-results.json. The rebaseline is then part of this pull request's diff:

python3 vm/selfhost/calibrate-perf-baseline.py --pr 5976 --reason "<why these benchmarks moved>" perf-results.json

Result: performance regression

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

ParparVM vs HotSpot (JDK 25): Linux x64

Runner CPU: Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz (baseline linux-x64@intel-xeon-platinum-8370c)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 15% (time) / 15% (RAM) away from its baseline in vm/selfhost/perf-baseline/ fails: above it is a regression, below it an improvement that has to be rebaselined (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (LinuxHelloMain) 4 0.93x (base 0.90x, +4.0%) 0.86x (base 0.78x, +10.4%) ok
translator (self) 4 0.54x (base 0.53x, +2.0%) 0.46x (base 0.51x, -9.7%) ok
intArithmetic 4 1.03x (base 1.03x, +0.0%) 0.06x (base 0.06x, -1.7%) ok
longArithmetic 4 1.01x (base 1.01x, +0.0%) 0.05x (base 0.05x, +1.8%) ok
mathTranscendental 4 1.03x (base 1.03x, -0.0%) 0.06x (base 0.06x, +0.8%) ok
arraySequential 4 2.44x (base 2.52x, -3.1%) 0.38x (base 0.35x, +5.7%) ok
arrayRandom 4 1.00x (base 1.01x, -0.6%) 0.21x (base 0.21x, -2.0%) ok
objectAllocation 4 1.20x (base 1.21x, -0.3%) 0.41x (base 0.50x, -19.0%) ok
valueEscape 4 0.13x (base 0.13x, -0.2%) 0.06x (base 0.04x, +33.1%) ok
hashMapChurn 4 1.00x (base 1.09x, -8.6%) 0.10x (base 0.10x, -0.8%) ok
stringBuilding 4 1.59x (base 1.56x, +2.1%) 0.26x (base 0.26x, -0.2%) ok
recursion 4 1.05x (base 1.30x, -18.9%) 0.06x (base 0.07x, -25.4%) ok
quicksort 4 1.09x (base 1.06x, +3.3%) 0.13x (base 0.13x, +3.2%) ok

Result: no regression

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

ParparVM vs HotSpot (JDK 25): Linux arm64

Runner CPU: Neoverse-N2 (baseline linux-arm64@neoverse-n2)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 15% (time) / 15% (RAM) away from its baseline in vm/selfhost/perf-baseline/ fails: above it is a regression, below it an improvement that has to be rebaselined (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (LinuxHelloMain) 4 0.99x (base 0.93x, +6.3%) 0.97x (base 0.87x, +11.9%) ok
translator (self) 4 0.73x (base 0.70x, +4.4%) 0.49x (base 0.45x, +9.7%) ok
intArithmetic 4 1.04x (base 1.04x, -0.0%) 0.03x (base 0.03x, -1.5%) ok
longArithmetic 4 0.79x (base 0.79x, -0.1%) 0.03x (base 0.03x, -1.3%) ok
mathTranscendental 4 1.10x (base 1.10x, +0.3%) 0.03x (base 0.03x, +1.7%) ok
arraySequential 4 0.36x (base 0.36x, -1.7%) 0.37x (base 0.37x, +0.1%) ok
arrayRandom 4 0.94x (base 0.94x, +0.2%) 0.20x (base 0.20x, -0.0%) ok
objectAllocation 4 1.89x (base 1.77x, +6.8%) 0.37x (base 0.37x, +1.2%) ok
valueEscape 4 0.51x (base 0.51x, -0.1%) 0.03x (base 0.03x, -2.2%) ok
hashMapChurn 4 0.84x (base 0.84x, +0.2%) 0.11x (base 0.12x, -8.9%) ok
stringBuilding 4 1.36x (base 1.36x, -0.3%) 0.26x (base 0.26x, -0.4%) ok
recursion 4 1.41x (base 1.43x, -1.6%) 0.03x (base 0.04x, -3.9%) ok
quicksort 4 1.00x (base 0.99x, +0.5%) 0.09x (base 0.09x, +0.9%) ok

Result: no regression

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 50ms / native 3ms = 16.6x speedup
SIMD float-mul (64K x300) java 48ms / native 4ms = 12.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 59.000 ms
Base64 CN1 decode 71.000 ms
Base64 SIMD encode 77.000 ms
Base64 encode ratio (SIMD/CN1) 1.305x (30.5% slower)
Base64 SIMD decode 73.000 ms
Base64 decode ratio (SIMD/CN1) 1.028x (2.8% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.333x (66.7% faster)
Image applyMask (SIMD off) 20.000 ms
Image applyMask (SIMD on) 19.000 ms
Image applyMask ratio (SIMD on/off) 0.950x (5.0% faster)
Image modifyAlpha (SIMD off) 17.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off) 22.000 ms
Image modifyAlpha removeColor (SIMD on) 16.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.727x (27.3% faster)

ParparVM vs HotSpot (JDK 25): Windows arm64

Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline windows-arm64@armv8-64-bit-family-8-model-d49-microsoft-corporation)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 15% (time) / 15% (RAM) away from its baseline in vm/selfhost/perf-baseline/ fails: above it is a regression, below it an improvement that has to be rebaselined (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (WinHelloMain) 4 1.08x (base 1.11x, -2.5%) 1.32x (base 1.26x, +4.7%) ok
translator (self) 4 0.84x (base 0.84x, +0.3%) 0.51x (base 0.45x, +14.0%) ok
intArithmetic 4 1.04x (base 1.04x, -0.1%) 0.06x (base 0.06x, +1.6%) ok
longArithmetic 4 0.80x (base 0.80x, +0.0%) 0.06x (base 0.06x, +1.9%) ok
mathTranscendental 4 0.73x (base 0.72x, +1.4%) 0.06x (base 0.06x, +0.5%) ok
arraySequential 4 0.36x (base 0.46x, -20.7%) 0.39x (base 0.39x, +0.0%) ok
arrayRandom 4 0.94x (base 0.94x, -0.1%) 0.23x (base 0.23x, +0.1%) ok
objectAllocation 4 1.75x (base 2.81x, -37.6%) 0.32x (base 0.43x, -26.2%) ok
valueEscape 4 0.76x (base 0.76x, +0.0%) 0.05x (base 0.05x, +0.7%) ok
hashMapChurn 4 1.02x (base 0.98x, +3.6%) 0.12x (base 0.12x, -2.1%) ok
stringBuilding 4 1.53x (base 1.43x, +7.3%) 0.27x (base 0.27x, -0.2%) ok
recursion 4 1.49x (base 1.46x, +2.1%) 0.06x (base 0.07x, -0.2%) ok
quicksort 4 1.00x (base 1.00x, +0.4%) 0.12x (base 0.12x, +0.0%) ok

Result: no regression

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T02:53:01.545364Z 55dbb30 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • ✅ Tests: 870 total, 0 failed, 57 skipped

Benchmark Results

  • Execution Time: 8846 ms

  • Hotspots (Top 20 sampled methods):

    • 10.16% java.lang.Object.hashCode (139 samples)
    • 6.94% java.util.ArrayList.indexOf (95 samples)
    • 6.65% com.codename1.tools.translator.BytecodeMethod.equals (91 samples)
    • 6.07% java.lang.System.identityHashCode (83 samples)
    • 5.77% com.codename1.tools.translator.IteratorEscape.ctorOnlyStoresParamsIntoThis (79 samples)
    • 3.44% com.codename1.tools.translator.IteratorEscape.mangle (47 samples)
    • 3.29% java.lang.StringBuilder.append (45 samples)
    • 2.41% java.util.HashMap.hash (33 samples)
    • 2.19% java.util.AbstractCollection.containsAll (30 samples)
    • 2.05% com.codename1.tools.translator.JavascriptReachability.enqueueResolved (28 samples)
    • 1.97% java.io.FileOutputStream.writeBytes (27 samples)
    • 1.68% com.codename1.tools.translator.BytecodeMethod.updateInlinableFieldDependencies (23 samples)
    • 1.61% com.codename1.tools.translator.classfile.analysis.Frame.execute (22 samples)
    • 1.46% java.lang.String.equals (20 samples)
    • 1.39% java.io.FileOutputStream.open0 (19 samples)
    • 1.32% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (18 samples)
    • 1.24% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (17 samples)
    • 1.17% com.codename1.tools.translator.DeadFieldElimination.run (16 samples)
    • 1.17% com.codename1.tools.translator.Parser.cullMethods (16 samples)
    • 1.02% com.codename1.tools.translator.BytecodeMethod.optimize (14 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5639acb341

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +102 to +104
UnityRuntime.begin();

view = new UnityGameView();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install view services before waking the scene

Calling UnityRuntime.begin() before the UnityGameView is created and initialized runs every script's Awake/OnEnable while PlayerPrefs still uses its transient in-memory fallback and AudioSource has no output. Consequently, saved preferences read in Awake appear missing, writes made there disappear once the store is installed, and every scene AudioSource with Play On Awake marks itself playing without ever sending audio to the device. Initialize these view-backed services before waking the first scene.

Useful? React with 👍 / 👎.

Comment on lines +92 to +93
if (negative) {
value = -value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Int32.MinValue as a valid parse result

For the valid input "-2147483648", negating the scanned magnitude produces Long.MIN_VALUE, which is also used as the invalid-input sentinel. Parse therefore throws FormatException, and TryParse returns false, instead of producing Integer.MIN_VALUE. Use a distinct parse-status indicator so the minimum Int32 remains representable.

Useful? React with 👍 / 👎.

Comment on lines +1539 to +1540
c.resumeAt = 0f;
c.phase = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer null yields until the following frame

When StartCoroutine is called from Start or Update, it immediately advances the iterator and adds it with phase == 0 and resumeAt == 0; the same frame's later runCoroutines(0) then advances it again. Thus a first yield return null resumes in the current frame rather than the next frame as promised, so initialization or frame-based loops can execute twice in one frame. Record the frame at which a null/default yield may next resume.

Useful? React with 👍 / 👎.

Comment on lines +86 to +87
public static float get_timeSinceLevelLoad() {
return time;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset timeSinceLevelLoad when changing scenes

get_timeSinceLevelLoad() returns the application-wide Time.time, but changeScene() never resets that clock. After loading a second scene, scripts therefore see time since application startup rather than time since the current scene was loaded, which breaks scene-local timers and spawn schedules. Track a separate scene-load timestamp or reset a dedicated level clock on every load.

Useful? React with 👍 / 👎.

Comment on lines +51 to +52
public static boolean get_isMobilePlatform() {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Report mobile platforms on device builds

Application.isMobilePlatform is hard-coded to false, including when the generated application runs on Android or iOS. Unity scripts commonly use this property to enable touch controls, mobile layouts, or device-specific behavior, so those paths remain disabled on the very device builds this compatibility layer targets. Have the host install or derive the actual platform value instead of returning a constant.

Useful? React with 👍 / 👎.

Comment on lines 147 to +148
xvfb-run -a mvn deploy -Psign-artifacts -Dgpg.passphrase=$MAVEN_GPG_PASSPHRASE \
-Plocal-dev-javase -Darchetype.test.skip=true -DskipTests \
-Plocal-dev-javase -Dunity-compat -Darchetype.test.skip=true -DskipTests \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Version the profile-only modules before release deployment

The release step first runs update-version.sh, whose mvn versions:set invocation does not activate unity-compat, and only activates this profile in the later deploy command. These two modules are therefore absent from the version-update reactor and retain their explicit 8.0-SNAPSHOT parent and artifact versions; once the root POM has the tag version, activating them for deploy produces a parent-version mismatch or publishes under snapshot coordinates instead of the tag. Run the version update with this profile active or explicitly update these two POMs before deployment.

Useful? React with 👍 / 👎.

Comment on lines +1026 to +1027
mv.visitJumpInsn(IFNE, ok);
invoke(INVOKESTATIC, Names.INTEROP, "divideByZero", "()V", false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject signed division overflow before emitting JVM division

The generated check covers only a zero divisor. For int.MinValue / -1 and long.MinValue / -1, CIL signed division must throw OverflowException, while JVM IDIV/LDIV silently returns the minimum value. Translated C# therefore continues with a corrupt arithmetic result and cannot catch the expected exception; add the minimum-value/negative-one check before emitting the division opcode.

Useful? React with 👍 / 👎.

Comment on lines +60 to +61
public static int Abs(int v) {
return v < 0 ? -v : v;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Throw on Math.Abs of the minimum signed value

System.Math.Abs(int.MinValue) must throw OverflowException, because the positive magnitude is not representable, but negating it here wraps and returns the same negative value; the long overload has the same defect. Scripts using the documented System.Math surface can therefore propagate a negative value from an operation guaranteed to be nonnegative. Detect the minimum values and throw the runtime's OverflowException before negating.

Useful? React with 👍 / 👎.

shai-almog and others added 2 commits October 9, 2026 19:55
… perf baseline

On the Ubuntu runner dotnet is linked into /usr/bin, so dropping every PATH
directory that holds it also dropped env, bash and mvn. The directory is now
shadowed by links to everything in it except dotnet.

objectAllocation RAM on Neoverse-N2 is bimodal; the row is recalibrated from
this run and three master runs with a wider RAM tolerance. No improvement is
claimed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review findings (rationale for the ones left alone is in comments at the
code concerned):
- Install the view's services (PlayerPrefs store, audio, platform) before
  the first scene wakes.
- A coroutine started from Start or Update resumes its first null yield on
  the next frame.
- Time.timeSinceLevelLoad restarts on a scene load.
- Application.platform and isMobilePlatform come from the host platform.
- Signed division and remainder of MinValue by -1 throw OverflowException,
  as does System.Math.Abs of MinValue.

JavaScript screenshots: XYChart rotated the canvas and rotated it back,
which with float-rounded angles leaves a transform 1e-14 off identity, and
the JavaScript port draws DOM text only for an exact identity. XYChart and
ChartComponent now restore the transform they were handed, and the port's
DOM text decision tolerates that residue.

A differential fixture against the JVM found four older JavaScript target
bugs: int to float/double read an un-normalized int, float/double to int
wrapped instead of saturating, a static read before its first write was
undefined, and Float.toString printed the double.

The offscreen check now runs on every scene sample in the integration
test. It found: a flick release delivered at the press point because
CodenameOneImplementation aliased the shared pointer arrays, GameView
missing single finger presses after the first drag, and a sub-image cache
keyed by corner only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e numeric fixture

The JavaScript target now truncates a float product the way the JVM does, so
the six theme screens that size from one move by a pixel; the goldens are this
run's CI captures. JsNumericDifferentialApp compares narrowing compound
assignments against the JVM on purpose and joins the other compiler fixtures
CodeQL does not read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 214 screenshots: 214 matched.
✅ JavaScript-port screenshot tests passed.

…lLoad

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Apple TV (tvOS / Metal)

Compared 171 screenshots: 163 matched, 8 missing references.

  • UnityCompatAnimator — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatAnimator.png.

    UnityCompatAnimator
    Preview info: JPEG preview quality 70; JPEG preview quality 70; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatAnimator.png in workflow artifacts.

  • UnityCompatCanvas — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatCanvas.png.

    UnityCompatCanvas
    Preview info: JPEG preview quality 30; JPEG preview quality 30; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatCanvas.png in workflow artifacts.

  • UnityCompatCinemachine — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatCinemachine.png.

    UnityCompatCinemachine
    Preview info: JPEG preview quality 30; JPEG preview quality 30; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatCinemachine.png in workflow artifacts.

  • UnityCompatParticles — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatParticles.png.

    UnityCompatParticles
    Preview info: JPEG preview quality 70; JPEG preview quality 70; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatParticles.png in workflow artifacts.

  • UnityCompatPhysics — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatPhysics.png.

    UnityCompatPhysics
    Preview info: JPEG preview quality 70; JPEG preview quality 70; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatPhysics.png in workflow artifacts.

  • UnityCompatSprites — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatSprites.png.

    UnityCompatSprites
    Preview info: JPEG preview quality 50; JPEG preview quality 50; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatSprites.png in workflow artifacts.

  • UnityCompatTextMeshPro — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatTextMeshPro.png.

    UnityCompatTextMeshPro
    Preview info: JPEG preview quality 20; JPEG preview quality 20; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatTextMeshPro.png in workflow artifacts.

  • UnityCompatTilemap — missing reference. Reference screenshot missing at /Users/runner/work/CodenameOne/CodenameOne/scripts/ios/screenshots-tv/UnityCompatTilemap.png.

    UnityCompatTilemap
    Preview info: JPEG preview quality 70; JPEG preview quality 70; downscaled to 1920x1080.
    Full-resolution PNG saved as UnityCompatTilemap.png in workflow artifacts.

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 167 screenshots: 167 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 449 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 71ms / native 3ms = 23.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 59.000 ms
Base64 CN1 decode 58.000 ms
Base64 native encode 845.000 ms
Base64 encode ratio (CN1/native) 0.070x (93.0% faster)
Base64 native decode 283.000 ms
Base64 decode ratio (CN1/native) 0.205x (79.5% faster)
Base64 SIMD encode 50.000 ms
Base64 encode ratio (SIMD/CN1) 0.847x (15.3% faster)
Base64 SIMD decode 50.000 ms
Base64 decode ratio (SIMD/CN1) 0.862x (13.8% faster)
Base64 encode ratio (SIMD/native) 0.059x (94.1% faster)
Base64 decode ratio (SIMD/native) 0.177x (82.3% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.571x (42.9% faster)
Image applyMask (SIMD off) 62.000 ms
Image applyMask (SIMD on) 50.000 ms
Image applyMask ratio (SIMD on/off) 0.806x (19.4% faster)
Image modifyAlpha (SIMD off) 36.000 ms
Image modifyAlpha (SIMD on) 29.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.806x (19.4% faster)
Image modifyAlpha removeColor (SIMD off) 47.000 ms
Image modifyAlpha removeColor (SIMD on) 44.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.936x (6.4% faster)

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 236 screenshots: 236 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 168 screenshots: 168 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 2833 seconds

Build and Run Timing

Metric Duration
Simulator Boot 83000 ms
Simulator Boot (Run) 2000 ms
App Install 18000 ms
App Launch 5000 ms
Test Execution 443000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 3ms = 23.3x speedup
SIMD float-mul (64K x300) java 77ms / native 3ms = 25.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 73.000 ms
Base64 CN1 decode 71.000 ms
Base64 native encode 1005.000 ms
Base64 encode ratio (CN1/native) 0.073x (92.7% faster)
Base64 native decode 404.000 ms
Base64 decode ratio (CN1/native) 0.176x (82.4% faster)
Base64 SIMD encode 58.000 ms
Base64 encode ratio (SIMD/CN1) 0.795x (20.5% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.887x (11.3% faster)
Base64 encode ratio (SIMD/native) 0.058x (94.2% faster)
Base64 decode ratio (SIMD/native) 0.156x (84.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.286x (71.4% faster)
Image applyMask (SIMD off) 66.000 ms
Image applyMask (SIMD on) 76.000 ms
Image applyMask ratio (SIMD on/off) 1.152x (15.2% slower)
Image modifyAlpha (SIMD off) 249.000 ms
Image modifyAlpha (SIMD on) 72.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.289x (71.1% faster)
Image modifyAlpha removeColor (SIMD off) 427.000 ms
Image modifyAlpha removeColor (SIMD on) 87.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.204x (79.6% faster)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a51fcb4b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +87 to +88
public static boolean Any(IEnumerable_1 source, Class t) {
return walk(source).MoveNext();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Dispose enumerators used by LINQ terminal operators

When the source is a custom enumerator or a compiler-generated iterator with a finally block, terminal operations that stop early—such as this Any, First, Contains, ElementAt, and Take—abandon the enumerator without calling Dispose(), even though IEnumerator_1 implements IDisposable. The iterator's cleanup therefore never runs after an early result or predicate exception; wrap enumeration in try/finally and dispose the enumerator on every exit path.

Useful? React with 👍 / 👎.

Comment on lines +94 to +98
public static Object GetValues(Type enumType) {
Type t = enumType(enumType);
int[] out = new int[t.values.length];
for (int i = 0; i < out.length; i++) {
out[i] = (int) t.values[i];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return arrays matching the enum's underlying type

For any enum whose underlying type is not int—for example, enum E : long { Huge = 1L << 40 }—Enum.GetValues always returns an int[] and truncates every member. Translated callers cast the result to the enum's normalized JVM array type (for this example, long[]), so the usual (E[])Enum.GetValues(typeof(E)) either fails with a cast error or receives corrupted values. Construct the primitive array corresponding to the enum's recorded underlying type instead.

Useful? React with 👍 / 👎.

Comment on lines +100 to +104
for (int i = 1; i < out.length; i++) {
int v = out[i];
int j = i - 1;
while (j >= 0 && out[j] > v) {
out[j + 1] = out[j];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sort enum values by unsigned magnitude

When an enum contains negative members, such as the common flags value All = -1, this signed comparison places that member before zero. Enum.GetValues orders members by the unsigned magnitude of their binary representation, so { All = -1, None = 0, One = 1 } must enumerate as None, One, All, whereas this code returns All, None, One. Use an unsigned comparison appropriate to the enum's underlying width.

Useful? React with 👍 / 👎.

Comment on lines +139 to +143
} else if (c == '}') {
if (i + 1 < n && format.charAt(i + 1) == '}') {
i++;
}
sb.append('}');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject unmatched closing braces in String.Format

For a malformed composite format such as "value }", .NET throws FormatException, but this branch appends the unmatched brace and returns normally. This changes control flow for scripts that validate or recover from user-provided format strings; only a doubled }} should emit a literal closing brace, while a standalone } must be rejected.

Useful? React with 👍 / 👎.

Comment on lines +55 to +59
public static Type $of(Class type, String fullName) {
Type t = (Type) byClass.get(type);
if (t == null) {
t = new Type(fullName, type, null, null, false);
byClass.put(type, t);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep primitive System.Type instances distinct

The cache key is only the JVM Class, but multiple distinct .NET primitives share one box class: int and uint both use Integer.class, long and ulong use Long.class, and byte and sbyte use Byte.class. Consequently, whichever type is requested first is returned for both, making typeof(int) == typeof(uint) true and potentially reporting the wrong FullName. Include the .NET type identity in the cache key rather than keying solely by the erased Java class.

Useful? React with 👍 / 👎.

Comment on lines +43 to +45
static long number(Object value) {
if (value instanceof EnumBox) {
return ((EnumBox) value).value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve enum type identity in reflective operations

This conversion discards the EnumBox.type, so APIs such as Enum.IsDefined, Enum.GetName, HasFlag, and CompareTo accept a boxed member of an unrelated enum and operate only on its numeric value. For example, firstEnum.HasFlag(secondEnum) can return true when their bits overlap, whereas .NET throws ArgumentException for differing enum types. Validate the box's enum type against the requested or receiver type before extracting its number.

Useful? React with 👍 / 👎.

Comment on lines +230 to +232
public void set_time(float value) {
startedAt = Time.unscaledTime - value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Seek the audio output when setting AudioSource.time

When a playing source executes audio.time = value, this setter only changes the runtime's bookkeeping and never seeks the device's Media, so the audible clip continues from its old position while scripts observe a different position. The bookkeeping is also immediately wrong when pitch != 1, because get_time() multiplies elapsed time by pitch but this setter does not divide the requested clip position by pitch. Add a seek operation to AudioOutput and derive startedAt consistently with the playback rate.

Useful? React with 👍 / 👎.

Comment on lines +360 to +362
public static String Trim(String s) {
return s.trim();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Trim the full .NET whitespace set

String.trim() only removes characters at or below U+0020, whereas System.String.Trim() removes Unicode whitespace. Inputs surrounded by characters such as non-breaking space (U+00A0) or em space (U+2003) therefore remain untrimmed, even though this runtime already recognizes them in Char_.IsWhiteSpace. Scan the ends using the .NET whitespace predicate instead of delegating to Java's trim().

Useful? React with 👍 / 👎.

Comment on lines +121 to +128
String wanted = value.trim();
for (int i = 0; i < t.names.length; i++) {
if (ignoreCase ? t.names[i].equalsIgnoreCase(wanted) : t.names[i].equals(wanted)) {
return new EnumBox(t, t.values[i]);
}
}
try {
return new EnumBox(t, Long.parseLong(wanted));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse comma-separated enum member names

Enum.Parse accepts a comma-separated list of member names, which is the standard way to parse flags such as "Read, Write"; this implementation only checks the entire string against one member and then attempts numeric parsing. Valid combined values therefore throw ArgumentException. Split on commas, trim each name, resolve each with the requested case sensitivity, and combine their underlying values.

Useful? React with 👍 / 👎.

Comment on lines +57 to +60
if (t instanceof NegativeArraySizeException) {
return new OverflowException();
}
return t;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wrap Java argument errors for translated catch blocks

Several public runtime APIs throw java.lang.IllegalArgumentException for invalid C# calls, including PlayerPrefs, SceneManager, Input, physics queries, and Instantiate, but this dispatcher leaves that throwable unchanged. Because generated catch clauses test against the custom System.Exception hierarchy, even catch (Exception) cannot catch these failures and the raw JVM exception escapes the script. Throw the corresponding compatibility exception at each API or map Java argument exceptions here.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 168 screenshots: 168 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 2333 seconds

Build and Run Timing

Metric Duration
Simulator Boot 2000 ms
Simulator Boot (Run) 1000 ms
App Install 17000 ms
App Launch 2000 ms
Test Execution 484000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 234ms / native 4ms = 58.5x speedup
SIMD float-mul (64K x300) java 455ms / native 8ms = 56.8x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 83.000 ms
Base64 CN1 decode 117.000 ms
Base64 native encode 2100.000 ms
Base64 encode ratio (CN1/native) 0.040x (96.0% faster)
Base64 native decode 1236.000 ms
Base64 decode ratio (CN1/native) 0.095x (90.5% faster)
Base64 SIMD encode 87.000 ms
Base64 encode ratio (SIMD/CN1) 1.048x (4.8% slower)
Base64 SIMD decode 92.000 ms
Base64 decode ratio (SIMD/CN1) 0.786x (21.4% faster)
Base64 encode ratio (SIMD/native) 0.041x (95.9% faster)
Base64 decode ratio (SIMD/native) 0.074x (92.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 10.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.800x (20.0% faster)
Image applyMask (SIMD off) 138.000 ms
Image applyMask (SIMD on) 433.000 ms
Image applyMask ratio (SIMD on/off) 3.138x (213.8% slower)
Image modifyAlpha (SIMD off) 87.000 ms
Image modifyAlpha (SIMD on) 61.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.701x (29.9% faster)
Image modifyAlpha removeColor (SIMD off) 69.000 ms
Image modifyAlpha removeColor (SIMD on) 58.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.841x (15.9% faster)

…seline

Base library: LINQ operators dispose their enumerators on every exit, Enum
values keep their underlying type and unsigned order, Enum.Parse takes comma
lists, HasFlag/CompareTo/IsDefined refuse another enum's member, String.Format
rejects a lone closing brace, Trim follows Char.IsWhiteSpace, typeof(int) and
typeof(uint) are distinct, a float key's two zeros are one key, and a Java
exception thrown by the runtime is caught by a script's catch (Exception).
AudioSource.time seeks the device's media and accounts for pitch.

The macOS draw-gradient golden is this branch's CI capture: with
-ffp-contract=off the mutable-image ellipse edge now equals the on-screen one.

Linux x64 perf baseline rows recalibrated per runner CPU from PR and master
runs; the shift is present on master at the base commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f0abf10218

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +931 to +934
Image image = (Image) paintedTexts.get(key);
if (image == null) {
image = paint(d, width, height);
paintedTexts.put(key, image);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the cache for dynamically changing text

When a Text component displays a changing value such as a timer, score, or frame counter, every distinct string creates an Image and stores it permanently in paintedTexts; the map is never cleared or evicted. A frequently updated label therefore retains thousands of raster images and can exhaust device memory during an otherwise normal game session. Keep only current/recent entries or use a bounded cache.

Useful? React with 👍 / 👎.

Comment on lines +1578 to +1582
if (yielded instanceof WaitForSeconds) {
c.resumeAt = Time.time + ((WaitForSeconds) yielded).seconds;
} else if (yielded instanceof Coroutine) {
c.waitingFor = (Coroutine) yielded;
} else if (yielded instanceof WaitForFixedUpdate) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Execute yielded IEnumerator instances before resuming parents

When a coroutine uses the common yield return ChildRoutine() form, the yielded value is an IEnumerator, but this chain recognizes only a started Coroutine; it consequently treats the child iterator like null, never executes it, and resumes the parent on the next frame. Add nested-enumerator handling so the parent waits while the yielded iterator is driven to completion.

Useful? React with 👍 / 👎.

Comment on lines +69 to +70
public static float get_unscaledDeltaTime() {
return unscaledDeltaTime;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return the fixed unscaled step during FixedUpdate

When Time.unscaledDeltaTime is read from FixedUpdate, Unity reports the unscaled duration of that fixed step, but this getter always returns the enclosing render frame's dt. At nonmatching render/physics rates—or when several fixed steps run in one frame—fixed-update timers and movement therefore advance by the render delta once per physics step, producing speed and scheduling errors.

Useful? React with 👍 / 👎.

Comment on lines +572 to +575
if (f.isDirectory()) {
collectStamped(f, prefix + f.getName() + "/", out);
} else {
out.add(prefix + f.getName() + ":" + f.length() + ":" + f.lastModified());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include file contents in the Unity build digest

In the new Unity incremental build cache, a source or asset rewritten with the same byte length and preserved modification time—such as after restoring or synchronizing files with timestamps intact—produces the same stamp even if its contents changed. run() then accepts state.txt and skips compilation, leaving stale translated classes or resources in the application; hash the file bytes rather than only name, size, and timestamp.

Useful? React with 👍 / 👎.

Comment on lines +39 to +44
public List_1() {
items = new Object[4];
}

public List_1(int capacity) {
items = new Object[capacity < 4 ? 4 : capacity];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Implement the IEnumerable List constructor

When valid C# uses new List<T>(source)—including the new List<int>(numbers) array case explicitly anticipated by ArrayView—the translator emits a call to the erased List_1(IEnumerable_1) constructor, but this runtime class exposes only the parameterless and capacity constructors. The Unity project therefore fails translation with a missing runtime method instead of constructing the list; add the enumerable constructor and dispose its enumerator after copying.

Useful? React with 👍 / 👎.

Comment on lines +850 to +855
String simple = meta.getName().substring(0, meta.getName().length() - ".cs.meta".length());
for (CilAssembly assembly : scripts) {
for (TypeDef t : assembly.types()) {
String full = t.fullName();
if (full.equals(simple) || full.endsWith("." + simple)) {
scriptsByGuid.put(guid, t);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve same-named scripts by their source file

When a valid Unity project contains two scripts with the same filename in different folders and namespaces, each .cs.meta GUID is mapped by simple class name and this loop always selects the first matching TypeDef. Both GUIDs can therefore resolve to the same script class, causing scene objects to receive the wrong behaviour and serialized fields from the other script to be dropped. Use source/PDB information or another unambiguous file-to-type mapping.

Useful? React with 👍 / 👎.

Comment on lines +733 to +738
GameObject go = (GameObject) objects.get(i);
if (go.destroyed || !go.activeInHierarchy()) {
continue;
}
if ((name != null && !go.name.equals(name)) || (tag != null && !go.tag.equals(tag))) {
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Traverse hierarchy paths in GameObject.Find

When a script calls GameObject.Find("Canvas/Panel/Button"), Unity interprets the slash-separated argument as a hierarchy path, but this implementation compares the whole argument only against each object's leaf name. Such lookups always return null unless an object literally contains slashes in its name, breaking a standard way scripts locate nested scene objects; resolve the root and traverse children as the existing Transform.Find implementation does.

Useful? React with 👍 / 👎.

Comment on lines +204 to +208
public java.lang.Object GetComponentInChildren(Class type) {
if (!active) {
return null;
}
java.lang.Object c = GetComponent(type);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Search the inactive root in child component queries

When GetComponentInChildren is invoked on an inactive GameObject, Unity still searches that root object even though the one-argument overload excludes its inactive descendants, but this early return skips the root as well. Managers querying an inactive prefab or temporarily disabled object therefore receive null despite the requested component being attached directly to it; GetComponentsInChildren has the same issue through collect, so both traversals should always inspect their starting object.

Useful? React with 👍 / 👎.

Comment on lines +62 to +64
public static boolean IsDigit(char c) {
return c >= '0' && c <= '9';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize Unicode decimal digits in Char.IsDigit

For decimal digits outside ASCII, such as Arabic-Indic ٣ or full-width 5, System.Char.IsDigit must return true because they have the Unicode decimal-digit category, but this implementation accepts only 0 through 9. Translated validation and parsing code therefore rejects digit strings that work in .NET; use a Unicode digit-category check rather than an ASCII range.

Useful? React with 👍 / 👎.

shai-almog and others added 5 commits October 10, 2026 08:37
Text images are released when a label stops showing them: the core sprite
renderer gains releaseTexture, and the Unity view's text cache is bounded.
yield return of a bare IEnumerator runs the child before the parent resumes.
Time's unscaled values follow the fixed step inside FixedUpdate.
List and Dictionary copy constructors. GameObject.Find walks a path.
GetComponentInChildren always searches the object asked. Char.IsDigit covers
the Unicode decimal digits. Two scripts with one file name are told apart by
their source path. The incremental build stamp hashes content and holds no
times or absolute paths, so a built project can move between machines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gallery project of eight scenes, one per feature area, is compiled into the
test app; each test advances a held clock a fixed number of frames and
captures. scripts/unity-gallery-prebuilt.sh packs the translated project so
a job without the .NET SDK can build the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y port

unity-gallery-prebuilt.yml builds the pack behind a cache and uploads it;
every workflow that builds the hellocodenameone app unpacks it before the
build, so no macOS, Windows or Android job needs the .NET SDK. A consumer job
in unity-compat.yml holds the no-SDK path. A build whose compiled output does
not match its sources or jars now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the pack digest

Linux, Windows and JavaScript builds of the test app listed their class
directories by hand and never saw a dependency jar; the common module now
stages its runtime class path where they read it. The compile-unity tool
class path took the translator's test dependencies, so the pack's digest
covered the consumer's own core jar; it is runtime scope now, and a mismatch
names the part and the first entry that differs. The eight Unity tests are a
port-status feature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Unity gallery's Canvas scene has a panel that is black at 40% over the
camera's background. Android and Windows rendered 0.6 x background; Linux
rendered it LIGHTER than the background and JavaScript a third too light.

Both used one glBlendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA) for all four
channels, so the frame's alpha became sa*sa + (1-sa) = 0.76 under the panel.
The colour was right; the frame was then composited with that alpha -- by
cairo over the form on Linux, as a premultiplied canvas on JavaScript.

The alpha channel now takes ONE / ONE_MINUS_SRC_ALPHA on Linux, JavaScript,
Android, JavaSE (JOGL) and Metal, which is what Direct3D already did, and
RenderState.BlendMode documents it. The JavaSE software device weights
additive light by the source alpha, as every GPU backend does.

GpuBlendContractTest runs the blend equation with each backend's factors;
parparvm-tests.yml names the sources it reads. The offscreen scene check
asserts the panel against the background.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

…s for

The Unity gallery's TextMeshPro scene draws a bold and an italic line, and
both came out in the regular face on native Linux and Windows. The runtime
asks the portable way -- createTrueTypeFont("native:MainBold", ...)
.derive(px, STYLE_PLAIN) -- so the style is in the name alone.

Linux mapped every native: name to regular Sans. Windows read the style at
load and then rebuilt the derived font from derive's weight only. Both now
keep it; graphics-draw-string on those ports draws its MainBold, MainBlack
and Italic* lines styled for the first time and will need reseeding.

NativeFontStyleContractTest compiles the two name parsers alone and holds
the derive paths by source; parparvm-tests.yml names the files it reads.

Also: the complete license header on four files the blend fix touched, and
the Unity gallery goldens for Android and JavaScript, plus the six scenes
without text for Linux (x64, arm64) and Windows, from CI run captures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 179 screenshots: 179 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 419 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 72ms / native 4ms = 18.0x speedup
SIMD float-mul (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 42.000 ms
Base64 CN1 decode 57.000 ms
Image encode benchmark iterations 100
Image createMask (SIMD off) 13.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.615x (38.5% faster)
Image applyMask (SIMD off) 75.000 ms
Image applyMask (SIMD on) 70.000 ms
Image applyMask ratio (SIMD on/off) 0.933x (6.7% faster)
Image modifyAlpha (SIMD off) 64.000 ms
Image modifyAlpha (SIMD on) 45.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.703x (29.7% faster)
Image modifyAlpha removeColor (SIMD off) 56.000 ms
Image modifyAlpha removeColor (SIMD on) 62.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.107x (10.7% slower)

ParparVM vs HotSpot (JDK 25): macOS arm64

Runner CPU: Apple M1 (Virtual) (baseline macos-arm64)

Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 15% (time) / 15% (RAM) away from its baseline in vm/selfhost/perf-baseline/ fails: above it is a regression, below it an improvement that has to be rebaselined (a row whose calibration runs were noisier carries a wider tolerance), and for RAM the change must also exceed 0.05x in absolute terms. Both run unpinned on all of the runner's CPUs, with their own default thread counts.

Benchmark Cores Time RAM Status
hello (HelloCodenameOne) 3 0.95x (base 0.96x, -0.6%) 1.43x (base 1.34x, +6.5%) ok
translator (self) 3 0.60x (base 0.62x, -4.0%) 0.56x (base 0.57x, -0.5%) ok
intArithmetic 3 1.06x (base 1.03x, +3.1%) 0.12x (base 0.12x, -0.2%) ok
longArithmetic 3 1.06x (base 1.03x, +3.1%) 0.11x (base 0.07x, +58.2%) ok
mathTranscendental 3 0.98x (base 1.01x, -2.8%) 0.12x (base 0.12x, -0.4%) ok
arraySequential 3 0.42x (base 0.42x, +0.7%) 0.53x (base 0.53x, +0.0%) ok
arrayRandom 3 0.96x (base 0.99x, -3.0%) 0.32x (base 0.32x, -0.1%) ok
objectAllocation 3 1.14x (base 1.11x, +2.6%) 0.83x (base 0.81x, +2.1%) ok
valueEscape 3 0.51x (base 0.51x, +0.1%) 0.08x (base 0.09x, -10.3%) ok
hashMapChurn 3 1.05x (base 1.18x, -10.4%) 0.07x (base 0.07x, +0.3%) ok
stringBuilding 3 0.96x (base 0.84x, +14.0%) 0.47x (base 0.46x, +0.6%) ok
recursion 3 1.23x (base 1.24x, -0.0%) 0.12x (base 0.12x, -0.6%) ok
quicksort 3 0.99x (base 1.01x, -2.4%) 0.11x (base 0.11x, +1.3%) ok

Result: no regression

…rame runs

A text command was painted on the frame's thread. On iOS that is the main
thread, while the port keeps one "image being drawn into" for the whole
process: a component painted to the screen meanwhile ends it, and a string
on its way to the text's image is dropped silently. A text is painted once
and cached, so a line lost that way never came back -- the right-aligned
line of the TextMeshPro gallery scene, in the iOS capture.

A frame off the event dispatch thread now asks it for the text by
callSerially and never waits; the pixels come back as an RGBImage. Until
they arrive a label shows its previous text. A frame on the event dispatch
thread paints at once, as before.

SceneShots.checkOffEdt holds the event dispatch thread and draws frames
off it for every gallery scene; the offscreen checks that assert a whole
frame now draw it on the event dispatch thread.

Also: Unity goldens for iOS (Metal, newest runtime) and the Canvas and
TextMeshPro scenes on Linux and Windows; reseeds on Linux and Windows for
the screens whose native: bold and italic fonts are now honoured; and a
recalibrated Windows x64 translator row in the PR's perf overlay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

https://github.com/codenameone/CodenameOne/blob/1a18fdf9e9e3e23549793fcd0dacc20684b5ab59/maven/unity-compat/src/main/csharp/UnityEngine/Values.cs#L711
P2 Badge Use Unity's near-zero tolerance in Approximately

The fixed 1e-5f floor makes Mathf.Approximately treat almost every pair near zero as equal—for example, Approximately(0f, 1e-6f) returns true. Unity's comparison uses the relative 1e-6 term with only a few floating-point epsilon units as its absolute floor, so gameplay checks around zero can now trigger roughly ten units too early. Replace this floor with the epsilon-based tolerance.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (names.norm(tp).isPrimitive()) {
Jvm.unbox(mv, names, tp);
} else {
mv.visitTypeInsn(CHECKCAST, names.classRef(tp));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Copy erased struct arguments before invoking delegates

When handwritten generic runtime code invokes a delegate with a struct value, this adapter only casts the erased object and passes the same instance to the target. For example, LINQ obtains a Vector2 directly from List_1 and calls a Func<Vector2, bool>; if the lambda mutates its by-value parameter, it consequently mutates the element stored in the source list, unlike C# value semantics. Copy concrete struct arguments here before invoking the translated target.

Useful? React with 👍 / 👎.

Comment on lines +188 to +190
public void set_loop(boolean value) {
loop = value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate loop changes to the active audio voice

When AudioSource.loop is changed during playback, this setter updates only runtime bookkeeping and never changes UnityGameAudio's looping map. Turning looping on leaves the device voice non-looping while isPlaying remains true indefinitely; turning it off leaves the device restarting the clip even after runtime bookkeeping declares it finished. Update or restart the active output voice when this property changes.

Useful? React with 👍 / 👎.

Comment on lines +50 to +52
public static void set_pause(boolean value) {
paused = value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply AudioListener.pause to active voices

When a game pauses audio through AudioListener.pause, this setter merely records the flag: it neither pauses device voices nor freezes each AudioSource's playback clock. Audio therefore continues audibly, and non-looping sources can expire while the listener is supposedly paused, breaking the common pause-menu flow. Propagate this state to active sources and compensate their timing when playback resumes.

Useful? React with 👍 / 👎.


public static bool Approximately(float a, float b)
{
return Abs(b - a) < Max(1e-6f * Max(Abs(a), Abs(b)), 1e-5f);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use Unity's near-zero tolerance in Approximately

The fixed 1e-5f floor makes Mathf.Approximately treat almost every pair near zero as equal—for example, Approximately(0f, 1e-6f) returns true. Unity's comparison uses the relative 1e-6 term with only a few floating-point epsilon units as its absolute floor, so gameplay checks around zero can now trigger roughly ten units too early. Replace this floor with the epsilon-based tolerance.

Useful? React with 👍 / 👎.

}

public void Play(String stateName) {
Play(stateName, 0, 0f);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve state time in default Animator.Play overloads

When a script repeatedly calls Play for the state that is already active, this overload supplies normalized time zero and restarts the animation on every call. Unity's omitted normalizedTime value is a negative-infinity sentinel that preserves the current state's time, so calling Play("Run") from Update, for example, should not freeze the animation on its first frame. Pass and honor the preserve-time sentinel instead of forcing zero.

Useful? React with 👍 / 👎.

Comment on lines +57 to +58
Item1 = other.Item1;
Item2 = other.Item2;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Copy nested struct fields in generic value tuples

When either tuple element is itself a translated struct, $copyValue() reaches this assignment and preserves the same mutable element object in both tuple copies. Mutating that element through the copied tuple therefore also changes the original tuple, whereas copying a ValueTuple<Vector2, int> in C# copies the nested Vector2 value. Copy elements implementing Struct instead of assigning their erased references directly.

Useful? React with 👍 / 👎.

Comment on lines +1111 to +1113
if (hasParent && parent != null) {
t.SetParent(parent, position == null && worldSpace);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve world pose when instantiating a child

When original is a child object and the caller uses Instantiate(original) or requests world-space parenting, copyTree has copied the child's local transform onto an unparented root, and this block either leaves that local pose as its world pose or preserves it while assigning the new parent. The clone consequently jumps relative to the source's parent instead of retaining the source's world position and rotation. Capture and apply the source world pose for the no-parent and worldSpace cases.

Useful? React with 👍 / 👎.

Comment on lines +770 to +772
// Unity copies a serializable class field by field;
// here the two scripts share the one object.
helper = def != null && (def.flags & TYPE_SERIALIZABLE) != 0 ? "" : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Deep-copy serialized class fields during Instantiate

When a live object with a field such as [Serializable] Stats stats is instantiated, this branch deliberately selects a plain field assignment for the serialized class. The source and clone therefore share the same Stats instance, so changing runtime state on one clone changes the source and every other clone, whereas Unity serializes these classes by value when cloning an object. Recreate the serialized object graph and copy its serialized fields instead of retaining the reference.

Useful? React with 👍 / 👎.

Comment on lines +1710 to +1712
} else if (yielded instanceof WaitForFixedUpdate) {
c.phase = 1;
} else if (yielded instanceof WaitForEndOfFrame) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer WaitForFixedUpdate started inside FixedUpdate

When StartCoroutine is called from FixedUpdate and its first yield is WaitForFixedUpdate, advance marks it for phase 1 and the same physics step's later runCoroutines(1) immediately resumes it. The yield is supposed to wait until the following fixed update, so physics logic after the yield runs twice in one step. Track the fixed-step generation just as normal next-frame yields track frameCount.

Useful? React with 👍 / 👎.

Comment on lines +178 to +180
return y >= 0 ? Atan(y / x) + PI : Atan(y / x) - PI;
}
return y > 0 ? PI / 2 : y < 0 ? -PI / 2 : 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve signed-zero quadrants in Atan2

For signed-zero inputs, these ordinary comparisons discard the quadrant encoded in the sign bits: for example, Math.Atan2(0.0, -0.0) returns zero here instead of positive π, and Math.Atan2(-0.0, -1.0) returns positive π instead of negative π. This also affects Mathf.Atan2 and angle calculations at axis boundaries; handle the IEEE signed-zero cases explicitly.

Useful? React with 👍 / 👎.

shai-almog and others added 6 commits October 11, 2026 15:18
… pause and loop

Translator and base library, each held by a console line compared with
dotnet or a sample trace line:
- Instantiate gives a clone its own copy of a [Serializable] class field,
  and of the elements of an array or list of them.
- Generated struct equality treats NaN fields as equal, as
  ValueType.Equals does.
- A view over a struct array hands out copies: LINQ over one saw later
  writes to the array, and ToList shared its values.
- A null out of erased code reads as default(T), for a struct and for a
  primitive: FirstOrDefault on an empty list of structs was null.
- Copying a value tuple copies a struct it holds.
- Atan2 and Atan keep the sign of zero and answer for infinities.
- Mathf.Approximately uses the relative term with a floor of eight
  epsilons; the floor of 1e-5 made everything near zero equal.

Runtime:
- WaitForFixedUpdate asked for inside a fixed step waits for the next.
- Instantiate of a child, with no parent or in world space, keeps the
  original's world position and rotation.
- Animator.Play without a time leaves a state that is playing alone.
- AudioListener.pause stops every source's clock and holds the output's
  voices; AudioSource.loop set while playing reaches the voice.

A delegate adapter does not copy a struct argument: a translated target
already copies one it writes, and the comment there says so.

Also: the prebuilt gallery workflow listed its archive into grep -q, which
failed the pipeline whenever grep left first; and hello RAM rows for two
Windows runners in the PR's perf overlay, where the suite now runs the
Unity scenes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The eight gallery scenes on iOS (Metal), tvOS and Mac Catalyst, and the
TextMeshPro scene on the newest iOS runtime, which was held back until its
right-aligned line was drawn. All from CI captures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row moved hello RAM on Xeon Platinum 8573C from 0.732x to 0.905x
because master at the base commit measured the same. Master's base now
carries 0.876x for it at the same 20% tolerance, which this pull
request's 0.905x is inside, so the overlay no longer needs to say
anything about that row -- and saying it from the old value is what the
gate refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
port-status-nightly.yml: master's run lines without xvfb, with this
branch's unpack of the Unity gallery prebuilt between them.

The perf overlay drops its translator RAM row for EPYC 7763: master's
pr/6006.json rebaselines the same row from the same value, and the gate
refuses two overlays that do. The row is to be measured again on top of
that one if this pull request's runs fall outside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…XCTest

The iOS screenshot legs sit at their 95 minute limit, on master as on this
branch. Measured, the screenshot suite is 7.5 minutes of it; the rest is
compiling, and two thirds of what is compiled is not the iOS app.

- The embedded watch app was built for arm64 AND x86_64 whenever the
  destination was an iPhone: 9,880 of 14,834 compile units, 21 to 34
  minutes. The CI builds now hold it to the host's slice through the
  xcconfig they already pass. The generated project is not changed.
- The screenshot test server was built by every Apple job, 3 to 10 minutes
  each. The port build makes it once and the jobs take it from the bundle,
  as Linux and Windows already do; a job without it builds it as before.
- The simulator booted ahead of the Xcode 27 leg's compile is shut down
  once the boot is proven; the test script boots it when it needs it.
- The packaging job no longer runs the local notification XCTest, which
  the native job runs from the same sources: 23 to 38 minutes of a second
  compile for the same 0.17 seconds of tests.

No timeout is raised.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

Native fidelity (Android, Material 3)

54 pairs compared -- median 95.6%, worst 91.3% (FlatButton_pressed_dark), 25th pct 94.9%, mean 95.7%.

Distribution -- >=99%: 2 | 95-99%: 37 | 90-95%: 15 | <90%: 0

Component State Appearance Material Fidelity SSIM mean delta vs base Geometry
FlatButton pressed dark normal 91.3% 0.899 3.10 0.0 ok
Tabs normal light normal 92.3% 0.914 3.05 0.0 ok
Button pressed dark normal 92.6% 0.949 4.32 0.0 ok
FlatButton normal dark normal 93.2% 0.938 2.69 0.0 ok
Button disabled dark normal 93.3% 0.928 2.20 0.0 ok
Button pressed light normal 93.3% 0.952 3.68 0.0 ok
FlatButton normal light normal 93.7% 0.941 2.29 0.0 ok
FlatButton pressed light normal 93.8% 0.942 2.56 0.0 ok
FloatingActionButton pressed light normal 94.4% 0.927 2.82 0.0 OFF (h 0.88)
RadioButton normal dark normal 94.5% 0.963 2.19 0.0 ok
RadioButton normal light normal 94.7% 0.964 1.85 0.0 ok
CheckBox selected dark normal 94.7% 0.950 2.60 0.0 ok
RadioButton selected dark normal 94.8% 0.963 2.42 0.0 ok
CheckBox normal dark normal 94.9% 0.952 2.69 0.0 ok
Button normal dark normal 94.9% 0.949 3.16 0.0 ok
CheckBox normal light normal 95.0% 0.953 2.30 0.0 ok
Toolbar normal dark normal 95.1% 0.906 1.60 0.0 ok
RaisedButton pressed dark normal 95.2% 0.950 2.39 0.0 ok
CheckBox disabled dark normal 95.2% 0.954 1.47 0.0 ok
CheckBox disabled light normal 95.2% 0.956 1.34 0.0 ok
Tabs normal dark normal 95.2% 0.913 3.65 0.0 ok
RadioButton disabled dark normal 95.3% 0.963 1.18 0.0 ok
RadioButton selected light normal 95.3% 0.964 1.84 0.0 ok
CheckBox selected light normal 95.4% 0.952 2.06 0.0 ok
Switch selected light normal 95.4% 0.966 1.59 0.0 ok
Switch selected dark normal 95.5% 0.966 1.86 0.0 ok
RadioButton disabled light normal 95.5% 0.966 1.08 0.0 ok
Switch disabled dark normal 95.6% 0.961 0.85 0.0 ok
Dialog normal light normal 95.7% 0.930 2.30 0.0 ok
RaisedButton pressed light normal 95.8% 0.958 2.16 0.0 ok
Button normal light normal 95.8% 0.953 2.46 0.0 ok
Dialog normal dark normal 95.8% 0.932 2.30 0.0 ok
Switch normal light normal 96.0% 0.961 1.45 0.0 ok
FloatingActionButton normal light normal 96.1% 0.937 1.14 0.0 ok
FloatingActionButton pressed dark normal 96.2% 0.951 2.45 0.0 ok
Switch normal dark normal 96.2% 0.962 1.39 0.0 ok
TextField disabled dark normal 96.2% 0.965 0.76 0.0 ok
RaisedButton normal dark normal 96.4% 0.952 1.78 0.0 ok
Switch disabled light normal 96.4% 0.970 0.61 0.0 ok
Button disabled light normal 96.8% 0.960 1.06 0.0 ok
ProgressBar normal dark normal 96.9% 0.967 2.03 0.0 OFF (h 1.50)
RaisedButton disabled dark normal 97.0% 0.955 0.89 0.0 ok
FloatingActionButton normal dark normal 97.1% 0.952 1.43 0.0 ok
ProgressBar normal light normal 97.3% 0.974 1.53 0.0 OFF (h 1.50)
RaisedButton disabled light normal 97.3% 0.961 0.79 0.0 ok
TextField disabled light normal 97.3% 0.965 0.79 0.0 ok
RaisedButton normal light normal 97.3% 0.961 1.40 0.0 ok
TextField normal dark normal 97.6% 0.958 1.83 0.0 ok
TextField normal light normal 97.6% 0.958 1.63 0.0 ok
Slider normal dark normal 98.4% 0.990 0.87 0.0 ok
Toolbar normal light normal 98.7% 0.974 1.28 0.0 ok
Slider normal light normal 99.0% 0.991 0.47 0.0 ok
Slider disabled dark normal 99.6% 0.993 0.22 0.0 ok
Slider disabled light normal 99.6% 0.993 0.18 0.0 ok
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Component State Appearance bbox dx,dy (px) w ratio h ratio center off (px) radius native->cn1 (px)
FloatingActionButton pressed light +0,+0 0.929 0.881 4.0 -
FloatingActionButton normal light +0,+0 0.946 0.912 2.9 -
Button pressed dark +0,+0 0.947 0.975 2.6 -
Button disabled dark +0,+0 0.947 0.975 2.6 -
Button pressed light +0,+0 0.947 0.975 2.6 -
Button normal dark +0,+0 0.947 0.975 2.6 -
RaisedButton pressed dark +0,+0 0.945 0.975 2.6 -
RaisedButton pressed light +0,+0 0.945 0.975 2.6 -
Button normal light +0,+0 0.947 0.975 2.6 -
RaisedButton normal dark +0,+0 0.945 0.975 2.6 -
Button disabled light +0,+0 0.947 0.975 2.6 -
RaisedButton disabled dark +0,+0 0.945 0.975 2.6 -
RaisedButton disabled light +0,+0 0.945 0.975 2.6 -
RaisedButton normal light +0,+0 0.945 0.975 2.6 -
RadioButton normal dark +1,+1 1.029 1.000 2.2 -
RadioButton normal light +1,+1 1.029 1.000 2.2 -
RadioButton selected dark +1,+1 1.029 1.000 2.2 -
RadioButton disabled dark +1,+1 1.029 1.000 2.2 -
RadioButton selected light +1,+1 1.029 1.000 2.2 -
Switch selected light +0,+0 1.080 1.063 2.2 -
RadioButton disabled light +1,+1 1.029 1.000 2.2 -
CheckBox selected dark +1,+1 1.013 1.000 1.8 -
CheckBox normal dark +1,+1 1.013 1.000 1.8 -
CheckBox normal light +1,+1 1.013 1.000 1.8 -
CheckBox disabled dark +1,+1 1.013 1.000 1.8 -
CheckBox disabled light +1,+1 1.013 1.000 1.8 -
CheckBox selected light +1,+1 1.013 1.000 1.8 -
Switch selected dark +0,+0 1.060 1.031 1.6 -
Switch disabled dark +0,-1 1.060 1.031 1.6 -
Dialog normal light +0,+0 1.007 0.982 1.6 -
Dialog normal dark +0,+0 1.007 0.982 1.6 -
Switch normal light +0,-1 1.060 1.031 1.6 -
Switch normal dark +0,-1 1.060 1.031 1.6 -
Switch disabled light +0,-1 1.060 1.031 1.6 -
FloatingActionButton pressed dark +0,+0 0.963 0.963 1.4 -
FloatingActionButton normal dark +0,+0 0.963 0.963 1.4 -
Toolbar normal light -1,+1 1.000 1.000 1.4 -
FlatButton pressed dark +0,+0 0.977 0.975 1.1 -
FlatButton normal dark +0,+0 0.977 0.975 1.1 -
FlatButton normal light +0,+0 0.977 0.975 1.1 -
FlatButton pressed light +0,+0 0.977 0.975 1.1 -
TextField normal dark +0,+0 1.015 1.036 1.1 -
TextField normal light +0,+0 1.015 1.036 1.1 -
Toolbar normal dark +0,+0 1.000 1.032 1.0 -
ProgressBar normal dark +0,+0 1.000 1.500 1.0 -
ProgressBar normal light +0,+0 1.000 1.500 1.0 -
TextField disabled dark +0,+0 1.015 1.018 0.7 -
TextField disabled light +0,+0 1.015 1.018 0.7 -
Tabs normal light +0,+1 1.000 0.984 0.5 -
Slider normal dark +1,+0 0.996 1.000 0.5 -
Slider normal light +1,+0 0.996 1.000 0.5 -
Tabs normal dark +0,+0 1.000 1.000 0.0 -
Slider disabled dark +0,+0 1.000 1.000 0.0 -
Slider disabled light +0,+0 1.000 1.000 0.0 -

Side-by-side comparisons (worst first)

  • FlatButton_pressed_dark -- 91.25% fidelity (SSIM 0.8985) (no change)

    native FlatButton_pressed_dark cn1 FlatButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • Tabs_normal_light -- 92.28% fidelity (SSIM 0.9140) (no change)

    native Tabs_normal_light cn1 Tabs_normal_light
    Left: native widget. Right: Codename One render.

  • Button_pressed_dark -- 92.61% fidelity (SSIM 0.9485) (no change)

    native Button_pressed_dark cn1 Button_pressed_dark
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_dark -- 93.24% fidelity (SSIM 0.9381) (no change)

    native FlatButton_normal_dark cn1 FlatButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_disabled_dark -- 93.26% fidelity (SSIM 0.9278) (no change)

    native Button_disabled_dark cn1 Button_disabled_dark
    Left: native widget. Right: Codename One render.

  • Button_pressed_light -- 93.34% fidelity (SSIM 0.9521) (no change)

    native Button_pressed_light cn1 Button_pressed_light
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_light -- 93.72% fidelity (SSIM 0.9410) (no change)

    native FlatButton_normal_light cn1 FlatButton_normal_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_light -- 93.77% fidelity (SSIM 0.9424) (no change)

    native FlatButton_pressed_light cn1 FlatButton_pressed_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_pressed_light -- 94.44% fidelity (SSIM 0.9273) (no change)

    native FloatingActionButton_pressed_light cn1 FloatingActionButton_pressed_light
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_dark -- 94.46% fidelity (SSIM 0.9630) (no change)

    native RadioButton_normal_dark cn1 RadioButton_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_light -- 94.69% fidelity (SSIM 0.9643) (no change)

    native RadioButton_normal_light cn1 RadioButton_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_dark -- 94.71% fidelity (SSIM 0.9502) (no change)

    native CheckBox_selected_dark cn1 CheckBox_selected_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_dark -- 94.79% fidelity (SSIM 0.9630) (no change)

    native RadioButton_selected_dark cn1 RadioButton_selected_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_dark -- 94.93% fidelity (SSIM 0.9516) (no change)

    native CheckBox_normal_dark cn1 CheckBox_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_normal_dark -- 94.94% fidelity (SSIM 0.9491) (no change)

    native Button_normal_dark cn1 Button_normal_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_light -- 95.03% fidelity (SSIM 0.9531) (no change)

    native CheckBox_normal_light cn1 CheckBox_normal_light
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_dark -- 95.07% fidelity (SSIM 0.9059) (no change)

    native Toolbar_normal_dark cn1 Toolbar_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_dark -- 95.19% fidelity (SSIM 0.9501) (no change)

    native RaisedButton_pressed_dark cn1 RaisedButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_dark -- 95.20% fidelity (SSIM 0.9538) (no change)

    native CheckBox_disabled_dark cn1 CheckBox_disabled_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_light -- 95.21% fidelity (SSIM 0.9562) (no change)

    native CheckBox_disabled_light cn1 CheckBox_disabled_light
    Left: native widget. Right: Codename One render.

  • Tabs_normal_dark -- 95.23% fidelity (SSIM 0.9125) (no change)

    native Tabs_normal_dark cn1 Tabs_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_dark -- 95.29% fidelity (SSIM 0.9630) (no change)

    native RadioButton_disabled_dark cn1 RadioButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_light -- 95.33% fidelity (SSIM 0.9642) (no change)

    native RadioButton_selected_light cn1 RadioButton_selected_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_light -- 95.37% fidelity (SSIM 0.9524) (no change)

    native CheckBox_selected_light cn1 CheckBox_selected_light
    Left: native widget. Right: Codename One render.

  • Switch_selected_light -- 95.37% fidelity (SSIM 0.9658) (no change)

    native Switch_selected_light cn1 Switch_selected_light
    Left: native widget. Right: Codename One render.

  • Switch_selected_dark -- 95.45% fidelity (SSIM 0.9656) (no change)

    native Switch_selected_dark cn1 Switch_selected_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_light -- 95.46% fidelity (SSIM 0.9657) (no change)

    native RadioButton_disabled_light cn1 RadioButton_disabled_light
    Left: native widget. Right: Codename One render.

  • Switch_disabled_dark -- 95.57% fidelity (SSIM 0.9613) (no change)

    native Switch_disabled_dark cn1 Switch_disabled_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_light -- 95.65% fidelity (SSIM 0.9300) (no change)

    native Dialog_normal_light cn1 Dialog_normal_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_light -- 95.78% fidelity (SSIM 0.9578) (no change)

    native RaisedButton_pressed_light cn1 RaisedButton_pressed_light
    Left: native widget. Right: Codename One render.

  • Button_normal_light -- 95.79% fidelity (SSIM 0.9528) (no change)

    native Button_normal_light cn1 Button_normal_light
    Left: native widget. Right: Codename One render.

  • Dialog_normal_dark -- 95.79% fidelity (SSIM 0.9322) (no change)

    native Dialog_normal_dark cn1 Dialog_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_light -- 95.97% fidelity (SSIM 0.9612) (no change)

    native Switch_normal_light cn1 Switch_normal_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_normal_light -- 96.09% fidelity (SSIM 0.9367) (no change)

    native FloatingActionButton_normal_light cn1 FloatingActionButton_normal_light
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_pressed_dark -- 96.16% fidelity (SSIM 0.9513) (no change)

    native FloatingActionButton_pressed_dark cn1 FloatingActionButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_dark -- 96.17% fidelity (SSIM 0.9616) (no change)

    native Switch_normal_dark cn1 Switch_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_disabled_dark -- 96.21% fidelity (SSIM 0.9648) (no change)

    native TextField_disabled_dark cn1 TextField_disabled_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_dark -- 96.41% fidelity (SSIM 0.9516) (no change)

    native RaisedButton_normal_dark cn1 RaisedButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_disabled_light -- 96.43% fidelity (SSIM 0.9698) (no change)

    native Switch_disabled_light cn1 Switch_disabled_light
    Left: native widget. Right: Codename One render.

  • Button_disabled_light -- 96.80% fidelity (SSIM 0.9596) (no change)

    native Button_disabled_light cn1 Button_disabled_light
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_dark -- 96.91% fidelity (SSIM 0.9669) (no change)

    native ProgressBar_normal_dark cn1 ProgressBar_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_dark -- 97.02% fidelity (SSIM 0.9549) (no change)

    native RaisedButton_disabled_dark cn1 RaisedButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • FloatingActionButton_normal_dark -- 97.07% fidelity (SSIM 0.9521) (no change)

    native FloatingActionButton_normal_dark cn1 FloatingActionButton_normal_dark
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_light -- 97.26% fidelity (SSIM 0.9739) (no change)

    native ProgressBar_normal_light cn1 ProgressBar_normal_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_light -- 97.26% fidelity (SSIM 0.9611) (no change)

    native RaisedButton_disabled_light cn1 RaisedButton_disabled_light
    Left: native widget. Right: Codename One render.

  • TextField_disabled_light -- 97.29% fidelity (SSIM 0.9649) (no change)

    native TextField_disabled_light cn1 TextField_disabled_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_light -- 97.32% fidelity (SSIM 0.9613) (no change)

    native RaisedButton_normal_light cn1 RaisedButton_normal_light
    Left: native widget. Right: Codename One render.

  • TextField_normal_dark -- 97.61% fidelity (SSIM 0.9583) (no change)

    native TextField_normal_dark cn1 TextField_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_normal_light -- 97.62% fidelity (SSIM 0.9582) (no change)

    native TextField_normal_light cn1 TextField_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_dark -- 98.39% fidelity (SSIM 0.9900) (no change)

    native Slider_normal_dark cn1 Slider_normal_dark
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_light -- 98.69% fidelity (SSIM 0.9739) (no change)

    native Toolbar_normal_light cn1 Toolbar_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_light -- 98.95% fidelity (SSIM 0.9908) (no change)

    native Slider_normal_light cn1 Slider_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_disabled_dark -- 99.56% fidelity (SSIM 0.9927) (no change)

    native Slider_disabled_dark cn1 Slider_disabled_dark
    Left: native widget. Right: Codename One render.

  • Slider_disabled_light -- 99.59% fidelity (SSIM 0.9932) (no change)

    native Slider_disabled_light cn1 Slider_disabled_light
    Left: native widget. Right: Codename One render.

@shai-almog

shai-almog commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

Native fidelity (iOS Modern, Metal)

68 pairs compared -- median 95.0%, worst 83.5% (Tabs_normal_dark), 25th pct 92.5%, mean 94.6%.

Distribution -- >=99%: 3 | 95-99%: 31 | 90-95%: 29 | <90%: 5

Component State Appearance Material Fidelity SSIM mean delta vs base Geometry
Tabs normal dark glass 83.5% 0.884 11.66 0.0 ok
Tabs normal light glass 86.4% 0.896 8.00 0.0 ok
Toolbar normal light glass 87.6% 0.951 4.24 0.0 ok
Toolbar normal dark glass 87.7% 0.950 3.69 0.0 ok
Button pressed dark glass 89.9% 0.958 4.21 0.0 OFF (off 12px, w 1.10)
TextField normal light normal 90.0% 0.958 2.78 -0.4 ok
Spinner normal dark normal 90.8% 0.881 3.39 -0.7 ok
TextField disabled light normal 91.1% 0.962 2.24 -0.2 ok
Spinner normal light normal 91.6% 0.910 4.11 -0.2 ok
RaisedButton disabled light glass 91.8% 0.961 3.09 +0.2 ok
Button normal dark glass 91.8% 0.958 3.52 0.0 OFF (off 12px, w 1.10)
Slider disabled dark normal 92.1% 0.943 2.45 -0.3 ok
Switch selected light normal 92.1% 0.981 1.47 0.0 ok
CheckBox normal light normal 92.2% 0.994 0.68 0.0 ok
RadioButton normal light normal 92.2% 0.994 0.68 0.0 ok
FlatButton normal dark glass 92.2% 0.983 2.21 +4.3 ok
Button normal light glass 92.4% 0.961 3.65 0.0 OFF (off 12px, w 1.10)
Slider normal dark normal 92.5% 0.944 2.63 -0.2 ok
RaisedButton disabled dark glass 92.7% 0.965 2.72 +1.3 ok
CheckBox disabled light normal 92.7% 0.995 0.50 0.0 ok
RadioButton disabled light normal 92.7% 0.995 0.50 0.0 ok
Button pressed light glass 92.9% 0.962 3.29 0.0 OFF (off 12px, w 1.10)
FlatButton pressed dark glass 92.9% 0.985 1.26 +2.3 ok
TabsGeom normal dark normal 93.0% 0.892 8.75 0.0 ok
TabsGeom normal light normal 93.0% 0.886 7.09 0.0 ok
FlatButton pressed light glass 93.2% 0.982 1.90 +4.3 ok
Button disabled dark glass 93.5% 0.962 2.47 0.0 OFF (off 12px, w 1.10)
Slider disabled light normal 94.0% 0.963 1.75 -0.1 OFF (h 4.50)
FlatButton normal light glass 94.2% 0.983 1.79 +1.3 ok
Button disabled light glass 94.4% 0.964 2.36 0.0 OFF (off 12px, w 1.10)
RaisedButton pressed dark glass 94.6% 0.965 2.07 +1.5 ok
RaisedButton normal dark glass 94.6% 0.966 2.08 +1.7 ok
RaisedButton pressed light glass 94.8% 0.967 2.22 +2.0 ok
RadioButton selected light normal 94.8% 0.992 0.86 0.0 ok
RaisedButton normal light glass 95.0% 0.967 2.19 +2.4 ok
Slider normal light normal 95.1% 0.959 1.56 0.0 ok
Switch normal light normal 95.5% 0.986 0.70 0.0 ok
CheckBox disabled dark normal 95.8% 0.993 0.25 0.0 ok
RadioButton disabled dark normal 95.8% 0.993 0.25 0.0 ok
CheckBox selected light normal 95.8% 0.994 0.67 0.0 ok
Switch selected dark normal 95.9% 0.985 1.02 0.0 ok
Switch disabled light normal 96.4% 0.990 0.53 0.0 ok
CheckBox normal dark normal 96.5% 0.993 0.35 0.0 ok
RadioButton normal dark normal 96.5% 0.993 0.35 0.0 ok
GlassPanelPhoto normal light glass 96.7% 0.980 7.95 +0.6 ok
GlassPanelPhoto normal dark glass 96.8% 0.983 8.78 +0.6 ok
TabOne normal dark normal 96.8% 0.953 4.44 +0.7 ok
Switch normal dark normal 96.8% 0.986 0.82 0.0 ok
RadioButton selected dark normal 96.8% 0.991 0.57 0.0 ok
TextField normal dark normal 96.9% 0.949 3.30 -0.2 ok
TextField disabled dark normal 97.2% 0.953 2.37 -0.2 ok
ProgressBar normal dark normal 97.5% 0.997 0.60 +3.0 ok
TabOne normal light normal 97.5% 0.961 2.90 +1.9 ok
ProgressBar normal light normal 97.6% 0.999 0.61 +2.2 ok
CheckBox selected dark normal 97.8% 0.993 0.38 0.0 ok
Dialog normal dark normal 97.9% 0.961 1.38 +0.9 ok
Dialog normal light normal 98.0% 0.963 1.42 +0.9 ok
Switch disabled dark normal 98.0% 0.987 0.41 +2.5 ok
GlassIcon normal dark normal 98.6% 0.984 3.55 0.0 ok
GlassText normal dark normal 98.6% 0.985 3.48 0.0 ok
GlassIcon normal light normal 98.7% 0.986 3.54 0.0 ok
GlassText normal light normal 98.7% 0.987 3.54 0.0 ok
GlassPanelGrad normal light normal 98.9% 0.994 4.38 +0.7 ok
GlassPanelGrad normal dark normal 99.0% 0.993 3.84 +0.5 ok
GlassPanelGrey normal dark normal 99.0% 0.992 3.30 +0.6 ok
GlassPanelGrey normal light normal 99.1% 0.994 3.29 +0.7 ok
GlassPanelRed normal dark normal 99.1% 0.993 3.22 +0.5 ok
GlassPanelRed normal light normal 99.1% 0.994 3.49 +0.7 ok
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Component State Appearance bbox dx,dy (px) w ratio h ratio center off (px) radius native->cn1 (px)
Button pressed dark +0,+0 1.104 1.000 11.5 45.4 -> 45.3
Button normal dark +0,+0 1.104 1.000 11.5 45.8 -> 45.2
Button normal light +0,+0 1.104 1.000 11.5 44.5 -> 45.3
Button pressed light +0,+0 1.104 1.000 11.5 48.1 -> 45.4
Button disabled dark +0,+0 1.104 1.000 11.5 45.1 -> 45.2
Button disabled light +0,+0 1.104 1.000 11.5 44.0 -> 45.3
TabOne normal dark -3,+0 1.007 0.948 4.9 80.3 -> 77.3
TabOne normal light -3,+0 1.007 0.948 4.9 79.1 -> 77.0
Toolbar normal light +4,+7 0.987 0.926 3.5 62.5 -> 56.1
Toolbar normal dark +4,+7 0.987 0.919 3.2 60.5 -> 55.5
Switch selected light +0,+0 1.028 1.026 2.7 -
RaisedButton disabled dark +0,+0 1.024 1.011 2.6 49.8 -> 46.1
Slider disabled light +0,-29 1.000 4.500 2.5 -
CheckBox disabled light +1,-3 1.000 1.023 2.2 -
RadioButton disabled light +1,-3 1.000 1.023 2.2 -
CheckBox disabled dark +1,-3 1.000 1.023 2.2 -
RadioButton disabled dark +1,-3 1.000 1.023 2.2 -
RaisedButton disabled light +0,+0 1.019 1.011 2.1 54.1 -> 96.6
RaisedButton pressed dark +0,+0 1.019 1.011 2.1 44.5 -> 44.3
RaisedButton normal dark +0,+0 1.019 1.011 2.1 44.4 -> 44.3
RaisedButton pressed light +0,+0 1.019 1.011 2.1 44.7 -> 44.3
RaisedButton normal light +0,+0 1.019 1.011 2.1 44.5 -> 44.1
Switch normal light +0,+0 1.023 1.013 2.1 -
Switch disabled light +0,+0 1.023 1.013 2.1 -
Switch normal dark +0,+0 1.023 1.013 2.1 -
Switch disabled dark +0,+0 1.023 1.013 2.1 -
CheckBox normal dark +0,-2 1.000 1.000 2.0 -
RadioButton normal dark +0,-2 1.000 1.000 2.0 -
RadioButton selected dark +0,-2 1.000 1.000 2.0 -
CheckBox selected dark +0,-2 1.000 1.000 2.0 -
Tabs normal dark +9,+0 0.973 1.012 1.8 79.7 -> 80.7
Tabs normal light +9,+0 0.973 1.012 1.8 79.2 -> 80.3
TabsGeom normal dark +9,+0 0.973 1.012 1.8 80.5 -> 80.9
TabsGeom normal light +9,+0 0.973 1.012 1.8 79.3 -> 80.6
FlatButton normal dark +4,+0 0.966 1.011 1.6 91.8 -> 95.7
FlatButton pressed dark +4,+0 0.966 1.011 1.6 95.3 -> 97.9
Switch selected dark +0,+0 1.017 1.013 1.6 -
CheckBox normal light +0,-2 1.000 1.011 1.5 -
RadioButton normal light +0,-2 1.000 1.011 1.5 -
RadioButton selected light +0,-2 1.000 1.011 1.5 -
CheckBox selected light +0,-2 1.000 1.011 1.5 -
Spinner normal light +11,-9 0.978 1.055 1.1 -
GlassIcon normal dark +0,+1 0.999 1.000 1.1 92.1 -> 91.5
GlassText normal dark +0,+1 0.999 1.000 1.1 92.1 -> 91.5
Spinner normal dark +12,-9 0.977 1.055 1.0 -
GlassPanelGrey normal dark +0,+1 1.000 1.000 1.0 26.4 -> 26.9
TextField disabled light +0,+1 0.999 0.996 0.7 13.9 -> 38.7
FlatButton normal light +0,+0 0.993 1.011 0.7 91.9 -> 51.8
Slider disabled dark +0,-1 1.000 1.015 0.5 -
FlatButton pressed light +1,+0 0.986 1.011 0.5 95.4 -> 65.6
Slider normal light +0,+1 1.000 0.964 0.5 -
GlassPanelPhoto normal light +0,+0 1.000 1.005 0.5 25.6 -> 31.4
GlassPanelPhoto normal dark +0,+0 1.000 1.005 0.5 23.0 -> 27.0
ProgressBar normal dark +0,+0 1.000 0.900 0.5 -
ProgressBar normal light +0,+0 1.000 0.900 0.5 -
GlassIcon normal light +0,+0 1.000 1.005 0.5 91.6 -> 92.3
GlassText normal light +0,+0 1.000 1.005 0.5 91.6 -> 92.3
GlassPanelGrad normal light +0,+0 1.000 1.005 0.5 22.3 -> 25.9
GlassPanelGrad normal dark +1,+1 0.998 0.995 0.5 25.7 -> 22.5
GlassPanelGrey normal light +0,+0 1.000 1.005 0.5 22.4 -> 26.0
GlassPanelRed normal dark +1,+1 0.998 0.995 0.5 26.6 -> 22.8
GlassPanelRed normal light +0,+0 1.000 1.005 0.5 22.1 -> 26.1
TextField normal light +0,+0 1.000 1.000 0.0 13.4 -> 40.5
Slider normal dark +0,+0 1.000 1.000 0.0 -
TextField normal dark +0,+0 1.000 1.000 0.0 6.4 -> 15.1
TextField disabled dark +0,+0 1.000 1.000 0.0 6.4 -> 15.9
Dialog normal dark +0,+0 1.000 1.000 0.0 -
Dialog normal light +0,+0 1.000 1.000 0.0 -

Side-by-side comparisons (worst first)

  • Tabs_normal_dark -- 83.45% fidelity (SSIM 0.8842) (no change)

    native Tabs_normal_dark cn1 Tabs_normal_dark
    Left: native widget. Right: Codename One render.

  • Tabs_normal_light -- 86.44% fidelity (SSIM 0.8961) (no change)

    native Tabs_normal_light cn1 Tabs_normal_light
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_light -- 87.64% fidelity (SSIM 0.9511) (no change)

    native Toolbar_normal_light cn1 Toolbar_normal_light
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_dark -- 87.70% fidelity (SSIM 0.9495) (no change)

    native Toolbar_normal_dark cn1 Toolbar_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_pressed_dark -- 89.93% fidelity (SSIM 0.9576) (no change)

    native Button_pressed_dark cn1 Button_pressed_dark
    Left: native widget. Right: Codename One render.

  • TextField_normal_light -- 90.03% fidelity (SSIM 0.9576) (-0.37 vs baseline)

    native TextField_normal_light cn1 TextField_normal_light
    Left: native widget. Right: Codename One render.

  • Spinner_normal_dark -- 90.78% fidelity (SSIM 0.8814) (-0.69 vs baseline)

    native Spinner_normal_dark cn1 Spinner_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_disabled_light -- 91.13% fidelity (SSIM 0.9619) (-0.22 vs baseline)

    native TextField_disabled_light cn1 TextField_disabled_light
    Left: native widget. Right: Codename One render.

  • Spinner_normal_light -- 91.58% fidelity (SSIM 0.9099) (-0.21 vs baseline)

    native Spinner_normal_light cn1 Spinner_normal_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_light -- 91.76% fidelity (SSIM 0.9613) (+0.15 vs baseline)

    native RaisedButton_disabled_light cn1 RaisedButton_disabled_light
    Left: native widget. Right: Codename One render.

  • Button_normal_dark -- 91.84% fidelity (SSIM 0.9583) (no change)

    native Button_normal_dark cn1 Button_normal_dark
    Left: native widget. Right: Codename One render.

  • Slider_disabled_dark -- 92.13% fidelity (SSIM 0.9434) (-0.31 vs baseline)

    native Slider_disabled_dark cn1 Slider_disabled_dark
    Left: native widget. Right: Codename One render.

  • Switch_selected_light -- 92.13% fidelity (SSIM 0.9806) (no change)

    native Switch_selected_light cn1 Switch_selected_light
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_light -- 92.16% fidelity (SSIM 0.9938) (no change)

    native CheckBox_normal_light cn1 CheckBox_normal_light
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_light -- 92.16% fidelity (SSIM 0.9938) (no change)

    native RadioButton_normal_light cn1 RadioButton_normal_light
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_dark -- 92.21% fidelity (SSIM 0.9833) (+4.25 vs baseline)

    native FlatButton_normal_dark cn1 FlatButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_normal_light -- 92.37% fidelity (SSIM 0.9609) (no change)

    native Button_normal_light cn1 Button_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_dark -- 92.52% fidelity (SSIM 0.9441) (-0.22 vs baseline)

    native Slider_normal_dark cn1 Slider_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_dark -- 92.65% fidelity (SSIM 0.9653) (+1.25 vs baseline)

    native RaisedButton_disabled_dark cn1 RaisedButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_light -- 92.71% fidelity (SSIM 0.9953) (no change)

    native CheckBox_disabled_light cn1 CheckBox_disabled_light
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_light -- 92.71% fidelity (SSIM 0.9953) (no change)

    native RadioButton_disabled_light cn1 RadioButton_disabled_light
    Left: native widget. Right: Codename One render.

  • Button_pressed_light -- 92.88% fidelity (SSIM 0.9617) (no change)

    native Button_pressed_light cn1 Button_pressed_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_dark -- 92.90% fidelity (SSIM 0.9849) (+2.31 vs baseline)

    native FlatButton_pressed_dark cn1 FlatButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • TabsGeom_normal_dark -- 93.02% fidelity (SSIM 0.8921) (+0.01 vs baseline)

    native TabsGeom_normal_dark cn1 TabsGeom_normal_dark
    Left: native widget. Right: Codename One render.

  • TabsGeom_normal_light -- 93.02% fidelity (SSIM 0.8861) (no change)

    native TabsGeom_normal_light cn1 TabsGeom_normal_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_light -- 93.20% fidelity (SSIM 0.9819) (+4.33 vs baseline)

    native FlatButton_pressed_light cn1 FlatButton_pressed_light
    Left: native widget. Right: Codename One render.

  • Button_disabled_dark -- 93.52% fidelity (SSIM 0.9618) (no change)

    native Button_disabled_dark cn1 Button_disabled_dark
    Left: native widget. Right: Codename One render.

  • Slider_disabled_light -- 93.96% fidelity (SSIM 0.9625) (-0.05 vs baseline)

    native Slider_disabled_light cn1 Slider_disabled_light
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_light -- 94.18% fidelity (SSIM 0.9825) (+1.32 vs baseline)

    native FlatButton_normal_light cn1 FlatButton_normal_light
    Left: native widget. Right: Codename One render.

  • Button_disabled_light -- 94.35% fidelity (SSIM 0.9636) (no change)

    native Button_disabled_light cn1 Button_disabled_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_dark -- 94.56% fidelity (SSIM 0.9653) (+1.46 vs baseline)

    native RaisedButton_pressed_dark cn1 RaisedButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_dark -- 94.57% fidelity (SSIM 0.9659) (+1.69 vs baseline)

    native RaisedButton_normal_dark cn1 RaisedButton_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_light -- 94.80% fidelity (SSIM 0.9665) (+2.04 vs baseline)

    native RaisedButton_pressed_light cn1 RaisedButton_pressed_light
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_light -- 94.84% fidelity (SSIM 0.9916) (no change)

    native RadioButton_selected_light cn1 RadioButton_selected_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_light -- 95.01% fidelity (SSIM 0.9673) (+2.40 vs baseline)

    native RaisedButton_normal_light cn1 RaisedButton_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_light -- 95.09% fidelity (SSIM 0.9588) (-0.04 vs baseline)

    native Slider_normal_light cn1 Slider_normal_light
    Left: native widget. Right: Codename One render.

  • Switch_normal_light -- 95.51% fidelity (SSIM 0.9863) (no change)

    native Switch_normal_light cn1 Switch_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_dark -- 95.76% fidelity (SSIM 0.9928) (no change)

    native CheckBox_disabled_dark cn1 CheckBox_disabled_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_dark -- 95.76% fidelity (SSIM 0.9928) (no change)

    native RadioButton_disabled_dark cn1 RadioButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_light -- 95.80% fidelity (SSIM 0.9938) (no change)

    native CheckBox_selected_light cn1 CheckBox_selected_light
    Left: native widget. Right: Codename One render.

  • Switch_selected_dark -- 95.90% fidelity (SSIM 0.9850) (no change)

    native Switch_selected_dark cn1 Switch_selected_dark
    Left: native widget. Right: Codename One render.

  • Switch_disabled_light -- 96.35% fidelity (SSIM 0.9896) (+0.04 vs baseline)

    native Switch_disabled_light cn1 Switch_disabled_light
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_dark -- 96.52% fidelity (SSIM 0.9927) (no change)

    native CheckBox_normal_dark cn1 CheckBox_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_dark -- 96.52% fidelity (SSIM 0.9927) (no change)

    native RadioButton_normal_dark cn1 RadioButton_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelPhoto_normal_light -- 96.72% fidelity (SSIM 0.9804) (+0.64 vs baseline)

    native GlassPanelPhoto_normal_light cn1 GlassPanelPhoto_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelPhoto_normal_dark -- 96.76% fidelity (SSIM 0.9832) (+0.59 vs baseline)

    native GlassPanelPhoto_normal_dark cn1 GlassPanelPhoto_normal_dark
    Left: native widget. Right: Codename One render.

  • TabOne_normal_dark -- 96.77% fidelity (SSIM 0.9526) (+0.73 vs baseline)

    native TabOne_normal_dark cn1 TabOne_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_dark -- 96.80% fidelity (SSIM 0.9855) (no change)

    native Switch_normal_dark cn1 Switch_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_dark -- 96.81% fidelity (SSIM 0.9908) (no change)

    native RadioButton_selected_dark cn1 RadioButton_selected_dark
    Left: native widget. Right: Codename One render.

  • TextField_normal_dark -- 96.88% fidelity (SSIM 0.9492) (-0.20 vs baseline)

    native TextField_normal_dark cn1 TextField_normal_dark
    Left: native widget. Right: Codename One render.

  • TextField_disabled_dark -- 97.19% fidelity (SSIM 0.9526) (-0.15 vs baseline)

    native TextField_disabled_dark cn1 TextField_disabled_dark
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_dark -- 97.47% fidelity (SSIM 0.9973) (+3.03 vs baseline)

    native ProgressBar_normal_dark cn1 ProgressBar_normal_dark
    Left: native widget. Right: Codename One render.

  • TabOne_normal_light -- 97.51% fidelity (SSIM 0.9608) (+1.89 vs baseline)

    native TabOne_normal_light cn1 TabOne_normal_light
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_light -- 97.60% fidelity (SSIM 0.9989) (+2.19 vs baseline)

    native ProgressBar_normal_light cn1 ProgressBar_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_dark -- 97.79% fidelity (SSIM 0.9932) (no change)

    native CheckBox_selected_dark cn1 CheckBox_selected_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_dark -- 97.86% fidelity (SSIM 0.9614) (+0.89 vs baseline)

    native Dialog_normal_dark cn1 Dialog_normal_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_light -- 97.95% fidelity (SSIM 0.9634) (+0.86 vs baseline)

    native Dialog_normal_light cn1 Dialog_normal_light
    Left: native widget. Right: Codename One render.

  • Switch_disabled_dark -- 98.01% fidelity (SSIM 0.9869) (+2.48 vs baseline)

    native Switch_disabled_dark cn1 Switch_disabled_dark
    Left: native widget. Right: Codename One render.

  • GlassIcon_normal_dark -- 98.59% fidelity (SSIM 0.9841) (no change)

    native GlassIcon_normal_dark cn1 GlassIcon_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassText_normal_dark -- 98.64% fidelity (SSIM 0.9848) (+0.05 vs baseline)

    native GlassText_normal_dark cn1 GlassText_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassIcon_normal_light -- 98.67% fidelity (SSIM 0.9860) (no change)

    native GlassIcon_normal_light cn1 GlassIcon_normal_light
    Left: native widget. Right: Codename One render.

  • GlassText_normal_light -- 98.74% fidelity (SSIM 0.9874) (+0.05 vs baseline)

    native GlassText_normal_light cn1 GlassText_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelGrad_normal_light -- 98.91% fidelity (SSIM 0.9935) (+0.67 vs baseline)

    native GlassPanelGrad_normal_light cn1 GlassPanelGrad_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelGrad_normal_dark -- 98.98% fidelity (SSIM 0.9928) (+0.55 vs baseline)

    native GlassPanelGrad_normal_dark cn1 GlassPanelGrad_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelGrey_normal_dark -- 98.99% fidelity (SSIM 0.9917) (+0.57 vs baseline)

    native GlassPanelGrey_normal_dark cn1 GlassPanelGrey_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelGrey_normal_light -- 99.05% fidelity (SSIM 0.9936) (+0.67 vs baseline)

    native GlassPanelGrey_normal_light cn1 GlassPanelGrey_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelRed_normal_dark -- 99.09% fidelity (SSIM 0.9926) (+0.52 vs baseline)

    native GlassPanelRed_normal_dark cn1 GlassPanelRed_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelRed_normal_light -- 99.10% fidelity (SSIM 0.9939) (+0.67 vs baseline)

    native GlassPanelRed_normal_light cn1 GlassPanelRed_normal_light
    Left: native widget. Right: Codename One render.

@shai-almog

shai-almog commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator Author

Native fidelity (ios-27-metal)

68 pairs compared -- median 94.6%, worst 74.5% (Toolbar_normal_dark), 25th pct 91.7%, mean 93.3%.

Distribution -- >=99%: 0 | 95-99%: 31 | 90-95%: 25 | <90%: 12

Component State Appearance Material Fidelity SSIM mean delta vs base Geometry
Toolbar normal dark glass 74.5% 0.930 7.16 0.0 ok
Button normal dark glass 79.3% 0.938 7.66 0.0 OFF (off 11px)
Button disabled dark glass 79.6% 0.942 7.41 0.0 OFF (off 11px)
RaisedButton disabled dark glass 81.2% 0.954 6.32 0.0 ok
Tabs normal dark glass 86.2% 0.900 6.73 +0.3 ok
Button pressed dark glass 86.3% 0.948 5.27 0.0 OFF (off 11px)
Tabs normal light glass 86.4% 0.895 7.61 +0.3 ok
TextField normal light normal 86.5% 0.950 3.38 0.0 ok
Toolbar normal light glass 86.9% 0.949 3.85 0.0 ok
TextField disabled light normal 89.4% 0.954 2.56 0.0 ok
Button normal light glass 89.5% 0.959 4.54 0.0 OFF (off 11px)
Button pressed light glass 89.5% 0.959 4.33 0.0 OFF (off 11px)
Spinner normal dark normal 90.7% 0.880 3.47 0.0 ok
FlatButton normal dark glass 90.9% 0.981 2.38 0.0 ok
Button disabled light glass 91.6% 0.962 3.21 0.0 OFF (off 11px)
Spinner normal light normal 91.6% 0.910 4.20 0.0 ok
CheckBox normal light normal 91.7% 0.994 0.72 0.0 ok
RadioButton normal light normal 91.7% 0.994 0.72 0.0 ok
Slider disabled dark normal 92.1% 0.943 2.45 0.0 ok
Switch selected light normal 92.1% 0.981 1.47 0.0 ok
CheckBox disabled light normal 92.3% 0.995 0.52 0.0 ok
RadioButton disabled light normal 92.3% 0.995 0.52 0.0 ok
FlatButton pressed dark glass 92.4% 0.982 1.37 0.0 ok
RaisedButton disabled light glass 92.4% 0.960 2.43 0.0 ok
Slider normal dark normal 92.5% 0.944 2.63 0.0 ok
TabsGeom normal light normal 93.1% 0.885 6.48 +0.1 OFF (off 6px)
FlatButton pressed light glass 93.3% 0.981 1.90 0.0 ok
TabsGeom normal dark normal 93.8% 0.893 4.47 +0.1 ok
FlatButton normal light glass 93.9% 0.981 1.88 0.0 ok
Slider disabled light normal 94.0% 0.963 1.75 0.0 OFF (h 4.50)
RaisedButton pressed dark glass 94.1% 0.966 2.29 0.0 ok
RaisedButton normal dark glass 94.1% 0.966 2.32 0.0 ok
RadioButton selected light normal 94.3% 0.990 0.95 0.0 ok
RaisedButton pressed light glass 94.5% 0.963 2.16 0.0 ok
GlassPanelPhoto normal dark glass 94.6% 0.951 13.16 0.0 ok
RaisedButton normal light glass 94.6% 0.963 2.17 0.0 ok
GlassPanelPhoto normal light glass 94.7% 0.951 11.57 0.0 ok
Slider normal light normal 95.1% 0.959 1.56 0.0 ok
CheckBox disabled dark normal 95.4% 0.992 0.25 0.0 ok
RadioButton disabled dark normal 95.4% 0.992 0.25 0.0 ok
Switch normal light normal 95.5% 0.986 0.70 0.0 ok
CheckBox selected light normal 95.8% 0.995 0.65 0.0 ok
CheckBox normal dark normal 95.8% 0.992 0.39 0.0 ok
RadioButton normal dark normal 95.8% 0.992 0.39 0.0 ok
Switch selected dark normal 95.9% 0.985 1.02 0.0 ok
TextField normal dark normal 96.0% 0.937 4.53 0.0 ok
RadioButton selected dark normal 96.1% 0.989 0.67 0.0 ok
Switch disabled light normal 96.4% 0.990 0.53 0.0 ok
TextField disabled dark normal 96.4% 0.940 3.39 0.0 ok
Switch normal dark normal 96.8% 0.986 0.82 0.0 ok
TabOne normal light normal 96.9% 0.948 2.57 0.0 OFF (off 6px, w 0.86)
GlassIcon normal dark normal 97.2% 0.955 3.32 0.0 ok
GlassText normal dark normal 97.3% 0.956 3.27 +0.1 ok
GlassPanelGrey normal dark normal 97.4% 0.959 3.41 0.0 ok
TabOne normal dark normal 97.4% 0.953 1.49 0.0 OFF (w 0.86)
ProgressBar normal dark normal 97.5% 0.997 0.60 0.0 ok
ProgressBar normal light normal 97.6% 0.999 0.61 0.0 ok
GlassPanelRed normal dark normal 97.7% 0.980 6.75 0.0 ok
CheckBox selected dark normal 97.9% 0.995 0.37 0.0 ok
Dialog normal dark normal 97.9% 0.961 1.38 0.0 ok
GlassPanelGrad normal dark normal 97.9% 0.975 4.86 0.0 ok
Dialog normal light normal 98.0% 0.963 1.42 0.0 ok
Switch disabled dark normal 98.0% 0.987 0.41 0.0 ok
GlassIcon normal light normal 98.2% 0.975 3.65 0.0 ok
GlassText normal light normal 98.3% 0.978 3.58 +0.1 ok
GlassPanelGrad normal light normal 98.4% 0.988 5.52 0.0 ok
GlassPanelGrey normal light normal 98.7% 0.987 3.64 0.0 ok
GlassPanelRed normal light normal 98.8% 0.990 4.14 0.0 ok
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Component State Appearance bbox dx,dy (px) w ratio h ratio center off (px) radius native->cn1 (px)
Button normal dark +0,-6 1.094 1.067 10.9 90.5 -> 45.2
Button disabled dark +0,-6 1.094 1.067 10.9 92.0 -> 45.2
Button pressed dark +0,-6 1.094 1.080 10.8 79.4 -> 45.3
Button normal light +0,+0 1.094 1.000 10.5 45.7 -> 45.3
Button pressed light +0,+0 1.094 1.000 10.5 87.5 -> 45.4
Button disabled light +0,+0 1.094 1.000 10.5 49.1 -> 45.3
TabsGeom normal light +3,+0 0.990 1.069 6.1 79.8 -> 83.6
TabOne normal light +19,+0 0.863 1.069 6.1 80.9 -> 83.6
TabsGeom normal dark +3,+0 0.990 1.064 5.6 98.6 -> 99.1
TabOne normal dark +19,+0 0.863 1.064 5.6 95.2 -> 95.6
Tabs normal light +4,+0 0.988 1.064 5.5 85.9 -> 87.9
GlassIcon normal dark +1,+8 0.998 0.976 5.5 102.3 -> 102.4
GlassText normal dark +1,+8 0.998 0.976 5.5 102.3 -> 102.4
Tabs normal dark +4,+0 0.987 1.058 5.1 92.9 -> 93.7
Toolbar normal dark +6,-3 0.983 1.009 3.5 90.3 -> 55.5
Toolbar normal light +6,+7 0.984 0.926 3.2 70.9 -> 56.1
RaisedButton disabled dark +0,-6 1.009 1.079 2.7 89.6 -> 46.1
Switch selected light +0,+0 1.028 1.026 2.7 -
Slider disabled light +0,-29 1.000 4.500 2.5 -
Switch normal light +0,+0 1.023 1.013 2.1 -
Switch disabled light +0,+0 1.023 1.013 2.1 -
Switch normal dark +0,+0 1.023 1.013 2.1 -
Switch disabled dark +0,+0 1.023 1.013 2.1 -
Spinner normal light +11,-8 0.978 1.052 1.6 -
RaisedButton disabled light +0,+0 1.014 1.011 1.6 71.9 -> 96.6
FlatButton normal light +0,+0 0.980 1.011 1.6 86.2 -> 51.8
Switch selected dark +0,+0 1.017 1.013 1.6 -
Spinner normal dark +12,-8 0.977 1.052 1.5 -
CheckBox normal light +0,+0 0.977 0.977 1.4 -
RadioButton normal light +0,+0 0.977 0.977 1.4 -
RadioButton selected light +0,+0 0.977 0.977 1.4 -
CheckBox selected light +0,+0 0.977 0.977 1.4 -
CheckBox normal dark +0,+0 0.977 0.977 1.4 -
RadioButton normal dark +0,+0 0.977 0.977 1.4 -
RadioButton selected dark +0,+0 0.977 0.977 1.4 -
CheckBox selected dark +0,+0 0.977 0.977 1.4 -
FlatButton pressed light +1,+0 0.973 1.011 1.1 94.6 -> 65.6
RaisedButton pressed dark +0,+0 1.009 1.011 1.1 44.6 -> 44.3
RaisedButton normal dark +0,+0 1.009 1.011 1.1 44.5 -> 44.3
CheckBox disabled dark +1,+0 0.966 0.977 1.1 -
RadioButton disabled dark +1,+0 0.966 0.977 1.1 -
RaisedButton pressed light +0,+0 1.009 1.000 1.0 43.3 -> 44.3
RaisedButton normal light +0,+0 1.009 1.000 1.0 43.3 -> 44.1
FlatButton normal dark +4,+0 0.952 1.011 0.7 86.1 -> 95.7
FlatButton pressed dark +4,+0 0.952 1.011 0.7 94.7 -> 97.9
GlassPanelPhoto normal light +1,+1 0.997 0.995 0.7 35.1 -> 33.5
GlassPanelGrey normal light +1,+1 0.997 0.995 0.7 30.0 -> 27.4
Slider disabled dark +0,-1 1.000 1.015 0.5 -
CheckBox disabled light +1,+0 0.977 0.988 0.5 -
RadioButton disabled light +1,+0 0.977 0.988 0.5 -
Slider normal light +0,+1 1.000 0.964 0.5 -
GlassPanelGrey normal dark +1,+2 0.998 0.986 0.5 101.8 -> 102.4
ProgressBar normal dark +0,+0 1.000 0.900 0.5 -
ProgressBar normal light +0,+0 1.000 0.900 0.5 -
GlassPanelGrad normal dark +1,+0 0.998 0.995 0.5 102.0 -> 102.1
GlassIcon normal light +1,+0 0.998 1.005 0.5 91.7 -> 92.8
GlassText normal light +1,+0 0.998 1.005 0.5 91.7 -> 92.8
GlassPanelGrad normal light +1,+0 0.998 1.005 0.5 24.3 -> 26.8
GlassPanelRed normal light +1,+0 0.998 1.005 0.5 24.4 -> 25.7
TextField normal light +0,+0 1.000 1.000 0.0 13.4 -> 40.0
TextField disabled light +0,+0 1.000 1.000 0.0 13.9 -> 40.4
Slider normal dark +0,+0 1.000 1.000 0.0 -
GlassPanelPhoto normal dark +2,+1 0.996 0.991 0.0 67.3 -> 67.5
TextField normal dark +0,+0 1.000 1.000 0.0 6.4 -> 15.1
TextField disabled dark +0,+0 1.000 1.000 0.0 6.4 -> 15.9
GlassPanelRed normal dark +1,-2 0.998 1.020 0.0 102.2 -> 102.5
Dialog normal dark +0,+0 1.000 1.000 0.0 -
Dialog normal light +0,+0 1.000 1.000 0.0 -

Side-by-side comparisons (worst first)

  • Toolbar_normal_dark -- 74.49% fidelity (SSIM 0.9295) (no change)

    native Toolbar_normal_dark cn1 Toolbar_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_normal_dark -- 79.28% fidelity (SSIM 0.9380) (no change)

    native Button_normal_dark cn1 Button_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_disabled_dark -- 79.63% fidelity (SSIM 0.9423) (no change)

    native Button_disabled_dark cn1 Button_disabled_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_dark -- 81.15% fidelity (SSIM 0.9544) (no change)

    native RaisedButton_disabled_dark cn1 RaisedButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • Tabs_normal_dark -- 86.22% fidelity (SSIM 0.8999) (+0.33 vs baseline)

    native Tabs_normal_dark cn1 Tabs_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_pressed_dark -- 86.28% fidelity (SSIM 0.9483) (no change)

    native Button_pressed_dark cn1 Button_pressed_dark
    Left: native widget. Right: Codename One render.

  • Tabs_normal_light -- 86.43% fidelity (SSIM 0.8954) (+0.31 vs baseline)

    native Tabs_normal_light cn1 Tabs_normal_light
    Left: native widget. Right: Codename One render.

  • TextField_normal_light -- 86.49% fidelity (SSIM 0.9502) (no change)

    native TextField_normal_light cn1 TextField_normal_light
    Left: native widget. Right: Codename One render.

  • Toolbar_normal_light -- 86.94% fidelity (SSIM 0.9488) (no change)

    native Toolbar_normal_light cn1 Toolbar_normal_light
    Left: native widget. Right: Codename One render.

  • TextField_disabled_light -- 89.39% fidelity (SSIM 0.9540) (no change)

    native TextField_disabled_light cn1 TextField_disabled_light
    Left: native widget. Right: Codename One render.

  • Button_normal_light -- 89.45% fidelity (SSIM 0.9588) (no change)

    native Button_normal_light cn1 Button_normal_light
    Left: native widget. Right: Codename One render.

  • Button_pressed_light -- 89.45% fidelity (SSIM 0.9588) (no change)

    native Button_pressed_light cn1 Button_pressed_light
    Left: native widget. Right: Codename One render.

  • Spinner_normal_dark -- 90.72% fidelity (SSIM 0.8800) (no change)

    native Spinner_normal_dark cn1 Spinner_normal_dark
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_dark -- 90.88% fidelity (SSIM 0.9805) (no change)

    native FlatButton_normal_dark cn1 FlatButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Button_disabled_light -- 91.57% fidelity (SSIM 0.9621) (no change)

    native Button_disabled_light cn1 Button_disabled_light
    Left: native widget. Right: Codename One render.

  • Spinner_normal_light -- 91.58% fidelity (SSIM 0.9097) (no change)

    native Spinner_normal_light cn1 Spinner_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_light -- 91.66% fidelity (SSIM 0.9935) (no change)

    native CheckBox_normal_light cn1 CheckBox_normal_light
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_light -- 91.66% fidelity (SSIM 0.9935) (no change)

    native RadioButton_normal_light cn1 RadioButton_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_disabled_dark -- 92.13% fidelity (SSIM 0.9434) (no change)

    native Slider_disabled_dark cn1 Slider_disabled_dark
    Left: native widget. Right: Codename One render.

  • Switch_selected_light -- 92.13% fidelity (SSIM 0.9806) (no change)

    native Switch_selected_light cn1 Switch_selected_light
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_light -- 92.31% fidelity (SSIM 0.9948) (no change)

    native CheckBox_disabled_light cn1 CheckBox_disabled_light
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_light -- 92.31% fidelity (SSIM 0.9948) (no change)

    native RadioButton_disabled_light cn1 RadioButton_disabled_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_dark -- 92.38% fidelity (SSIM 0.9821) (no change)

    native FlatButton_pressed_dark cn1 FlatButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_disabled_light -- 92.40% fidelity (SSIM 0.9596) (no change)

    native RaisedButton_disabled_light cn1 RaisedButton_disabled_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_dark -- 92.52% fidelity (SSIM 0.9441) (no change)

    native Slider_normal_dark cn1 Slider_normal_dark
    Left: native widget. Right: Codename One render.

  • TabsGeom_normal_light -- 93.13% fidelity (SSIM 0.8851) (+0.14 vs baseline)

    native TabsGeom_normal_light cn1 TabsGeom_normal_light
    Left: native widget. Right: Codename One render.

  • FlatButton_pressed_light -- 93.29% fidelity (SSIM 0.9811) (no change)

    native FlatButton_pressed_light cn1 FlatButton_pressed_light
    Left: native widget. Right: Codename One render.

  • TabsGeom_normal_dark -- 93.81% fidelity (SSIM 0.8925) (+0.14 vs baseline)

    native TabsGeom_normal_dark cn1 TabsGeom_normal_dark
    Left: native widget. Right: Codename One render.

  • FlatButton_normal_light -- 93.91% fidelity (SSIM 0.9811) (no change)

    native FlatButton_normal_light cn1 FlatButton_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_disabled_light -- 93.96% fidelity (SSIM 0.9625) (no change)

    native Slider_disabled_light cn1 Slider_disabled_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_dark -- 94.08% fidelity (SSIM 0.9659) (no change)

    native RaisedButton_pressed_dark cn1 RaisedButton_pressed_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_dark -- 94.13% fidelity (SSIM 0.9660) (no change)

    native RaisedButton_normal_dark cn1 RaisedButton_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_light -- 94.26% fidelity (SSIM 0.9903) (no change)

    native RadioButton_selected_light cn1 RadioButton_selected_light
    Left: native widget. Right: Codename One render.

  • RaisedButton_pressed_light -- 94.53% fidelity (SSIM 0.9633) (no change)

    native RaisedButton_pressed_light cn1 RaisedButton_pressed_light
    Left: native widget. Right: Codename One render.

  • GlassPanelPhoto_normal_dark -- 94.60% fidelity (SSIM 0.9505) (no change)

    native GlassPanelPhoto_normal_dark cn1 GlassPanelPhoto_normal_dark
    Left: native widget. Right: Codename One render.

  • RaisedButton_normal_light -- 94.64% fidelity (SSIM 0.9632) (no change)

    native RaisedButton_normal_light cn1 RaisedButton_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelPhoto_normal_light -- 94.66% fidelity (SSIM 0.9507) (no change)

    native GlassPanelPhoto_normal_light cn1 GlassPanelPhoto_normal_light
    Left: native widget. Right: Codename One render.

  • Slider_normal_light -- 95.09% fidelity (SSIM 0.9588) (no change)

    native Slider_normal_light cn1 Slider_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_disabled_dark -- 95.41% fidelity (SSIM 0.9924) (no change)

    native CheckBox_disabled_dark cn1 CheckBox_disabled_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_disabled_dark -- 95.41% fidelity (SSIM 0.9924) (no change)

    native RadioButton_disabled_dark cn1 RadioButton_disabled_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_light -- 95.51% fidelity (SSIM 0.9863) (no change)

    native Switch_normal_light cn1 Switch_normal_light
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_light -- 95.80% fidelity (SSIM 0.9951) (no change)

    native CheckBox_selected_light cn1 CheckBox_selected_light
    Left: native widget. Right: Codename One render.

  • CheckBox_normal_dark -- 95.84% fidelity (SSIM 0.9922) (no change)

    native CheckBox_normal_dark cn1 CheckBox_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_normal_dark -- 95.84% fidelity (SSIM 0.9922) (no change)

    native RadioButton_normal_dark cn1 RadioButton_normal_dark
    Left: native widget. Right: Codename One render.

  • Switch_selected_dark -- 95.90% fidelity (SSIM 0.9850) (no change)

    native Switch_selected_dark cn1 Switch_selected_dark
    Left: native widget. Right: Codename One render.

  • TextField_normal_dark -- 96.00% fidelity (SSIM 0.9370) (no change)

    native TextField_normal_dark cn1 TextField_normal_dark
    Left: native widget. Right: Codename One render.

  • RadioButton_selected_dark -- 96.13% fidelity (SSIM 0.9894) (no change)

    native RadioButton_selected_dark cn1 RadioButton_selected_dark
    Left: native widget. Right: Codename One render.

  • Switch_disabled_light -- 96.35% fidelity (SSIM 0.9896) (no change)

    native Switch_disabled_light cn1 Switch_disabled_light
    Left: native widget. Right: Codename One render.

  • TextField_disabled_dark -- 96.35% fidelity (SSIM 0.9395) (no change)

    native TextField_disabled_dark cn1 TextField_disabled_dark
    Left: native widget. Right: Codename One render.

  • Switch_normal_dark -- 96.80% fidelity (SSIM 0.9855) (no change)

    native Switch_normal_dark cn1 Switch_normal_dark
    Left: native widget. Right: Codename One render.

  • TabOne_normal_light -- 96.90% fidelity (SSIM 0.9480) (no change)

    native TabOne_normal_light cn1 TabOne_normal_light
    Left: native widget. Right: Codename One render.

  • GlassIcon_normal_dark -- 97.18% fidelity (SSIM 0.9547) (no change)

    native GlassIcon_normal_dark cn1 GlassIcon_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassText_normal_dark -- 97.27% fidelity (SSIM 0.9563) (+0.06 vs baseline)

    native GlassText_normal_dark cn1 GlassText_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelGrey_normal_dark -- 97.35% fidelity (SSIM 0.9588) (no change)

    native GlassPanelGrey_normal_dark cn1 GlassPanelGrey_normal_dark
    Left: native widget. Right: Codename One render.

  • TabOne_normal_dark -- 97.36% fidelity (SSIM 0.9531) (no change)

    native TabOne_normal_dark cn1 TabOne_normal_dark
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_dark -- 97.47% fidelity (SSIM 0.9973) (no change)

    native ProgressBar_normal_dark cn1 ProgressBar_normal_dark
    Left: native widget. Right: Codename One render.

  • ProgressBar_normal_light -- 97.60% fidelity (SSIM 0.9989) (no change)

    native ProgressBar_normal_light cn1 ProgressBar_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelRed_normal_dark -- 97.73% fidelity (SSIM 0.9795) (no change)

    native GlassPanelRed_normal_dark cn1 GlassPanelRed_normal_dark
    Left: native widget. Right: Codename One render.

  • CheckBox_selected_dark -- 97.85% fidelity (SSIM 0.9945) (no change)

    native CheckBox_selected_dark cn1 CheckBox_selected_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_dark -- 97.86% fidelity (SSIM 0.9614) (no change)

    native Dialog_normal_dark cn1 Dialog_normal_dark
    Left: native widget. Right: Codename One render.

  • GlassPanelGrad_normal_dark -- 97.91% fidelity (SSIM 0.9752) (no change)

    native GlassPanelGrad_normal_dark cn1 GlassPanelGrad_normal_dark
    Left: native widget. Right: Codename One render.

  • Dialog_normal_light -- 97.95% fidelity (SSIM 0.9634) (no change)

    native Dialog_normal_light cn1 Dialog_normal_light
    Left: native widget. Right: Codename One render.

  • Switch_disabled_dark -- 98.01% fidelity (SSIM 0.9869) (no change)

    native Switch_disabled_dark cn1 Switch_disabled_dark
    Left: native widget. Right: Codename One render.

  • GlassIcon_normal_light -- 98.16% fidelity (SSIM 0.9750) (no change)

    native GlassIcon_normal_light cn1 GlassIcon_normal_light
    Left: native widget. Right: Codename One render.

  • GlassText_normal_light -- 98.31% fidelity (SSIM 0.9779) (+0.06 vs baseline)

    native GlassText_normal_light cn1 GlassText_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelGrad_normal_light -- 98.41% fidelity (SSIM 0.9876) (no change)

    native GlassPanelGrad_normal_light cn1 GlassPanelGrad_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelGrey_normal_light -- 98.70% fidelity (SSIM 0.9865) (no change)

    native GlassPanelGrey_normal_light cn1 GlassPanelGrey_normal_light
    Left: native widget. Right: Codename One render.

  • GlassPanelRed_normal_light -- 98.78% fidelity (SSIM 0.9900) (no change)

    native GlassPanelRed_normal_light cn1 GlassPanelRed_normal_light
    Left: native widget. Right: Codename One render.

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55dbb30cf4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +223 to +224
default:
return "java/lang/Integer";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve unsigned primitive identity when boxing

When signed and unsigned primitives enter object or erased generic storage, this maps int and uint to the same Integer representation; the same collision occurs for long/ulong, byte/sbyte, and char/ushort. Consequently, ((object)(int)1).Equals((object)(uint)1) returns true instead of false, (uint)(object)(int)1 can unbox successfully instead of throwing, and object-keyed dictionaries conflate distinct .NET values. Use distinct box representations that retain the original primitive type.

Useful? React with 👍 / 👎.

Comment on lines +289 to +290
double whole = java.lang.Math.floor(b);
if (whole == b && b >= -1024 && b <= 1024) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle negative bases with large integral exponents

When the base is negative and the integral exponent has magnitude greater than 1024, this bound skips the integer-power branch and the later negative-base path returns NaN. For example, both Math.Pow(-1, 1025) and Mathf.Pow(-1f, 1025f) should return -1 but return NaN here; large powers also lose the correct signed infinity or zero result. Preserve integer parity for all representable integral exponents instead of routing them through the non-integral negative-base case.

Useful? React with 👍 / 👎.

Comment on lines +51 to +52
Delegate[] x = a.invocationList();
Delegate[] y = b.invocationList();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject combining different delegate types

When both arguments are non-null but have different delegate types, .NET's Delegate.Combine throws ArgumentException; this code instead builds a multicast delegate using a's type and embeds b in its invocation list. A call such as Delegate.Combine(anAction, aFunc) therefore succeeds unexpectedly and only fails later during invocation with a cast error, changing both timing and exception handling. Validate that both operands have the same delegate type before constructing the combined list.

Useful? React with 👍 / 👎.

t.getOutputDirectory().set(new File(layout.buildDir(), "generated/sources/cn1-unity"));
t.getClassesDirectory().set(new File(layout.buildDir(), "cn1-unity/classes"));
t.getStateDirectory().set(new File(layout.buildDir(), "cn1-unity/state"));
t.onlyIf(x -> com.codename1.maven.UnityProjectBuilder.isUnityProject(unityDir));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove stale Unity outputs when the project disappears

In an incremental Gradle build where src/main/unity is deleted or ceases to contain both required directories after a successful Unity build, this onlyIf skips the task entirely and leaves its declared generated-source and classes directories untouched. Those directories are still registered with the main source set and application output, so stale generated scenes, translated classes, and assets can continue to compile or be packaged until the user happens to run clean; run a cleanup path when the Unity project is absent instead of skipping all task action.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants