Add com.codename1.gaming game development APIs#5166
Conversation
Rework the native barrier to the C/iOS backend's model: the JS host side is a
dumb, hard-reference table that never GCs on its own; GC applies only to the
Java side, and the Java side's finalizer frees the front-end resource it owns.
1. Stop crossing the barrier for values the Java side already knows. The paint
flush and layout were calling outputCanvas.getContext('2d') /
getWidth()/getHeight() and getDisplayWidth()/Height() (-> canvas.getWidth())
on every frame -- a continuous storm of round-trips whose responses
intermittently crossed into concurrent object reads (getDocument/getContext
resuming with a width/height number -> degraded receiver -> the ButtonTheme
hard-stall). Cache displayWidth/displayHeight (set in updateCanvasSize) and
the outputCanvas 2D context (stable for a canvas) in Java fields and reuse
them; getDisplayWidth/Height return the cached values. No dimension/context
round-trips during steady-state paint.
2. Drive host-ref release from the OWNING Java object's finalizer, not from JSO
wrapper GC. NativeImage owns its backing canvas / HTMLImageElement;
registerImageResource() arms a FinalizationRegistry keyed on that resource
(a single, stable wrapper held only by the image), and on collection the
worker posts releaseHostRef for its id. The host keeps a hard ref until then.
This replaces #5143's wrapper-refcount release, which raced: the host dedups
one id across many re-created worker wrappers and a raw __jsValue marker
could outlive them, so refcount-zero released canvases still in use ->
"Missing host receiver". Single-owner keying cannot do that.
browser_bridge.releaseHostRefs now evicts whatever id the dead owner held
(canvas or image) behind the never-release singleton guard.
Refs #5145.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rks (#5145) The late-suite screenshot wedge (after the #5143 host-ref fix) has two roots in the worker<->host postMessage channel under a dense paint burst: 1. Response-cross: an idempotent object read (document.createElement, canvas.getContext, getImageData, measureText, canvas.toDataURL) resumes with a corrupted value -- a number, an empty {} that lost its host-ref marker, null from a never-null method, or a thrown "Missing JS member"/"Missing host receiver". The old substitute-null turned each into a hard NPE / EDT deadlock (createElement -> null canvas; or an emit-time toDataURL throw escaping a lock). 2. Lost-response: a host callback never arrives, so the green thread parks on pendingHostCalls[id] forever (hard wedge, heartbeat alive but runnable=0). Mirroring the C/iOS backend model (the host is a thin, dumb pixel sink; the worker must never blindly trust it to always respond): - Barrier-model reductions to cut cross frequency at the source: cache the document Java-side (doc(); no per-createCanvas window.getDocument()); pass the known width/height into the HTML5Graphics ctor and BufferedGraphics instead of reading canvas.getWidth()/getHeight() back across the barrier; track the scratch-buffer dims Java-side. - invokeJsoBridge retry: re-issue an idempotent read up to 12x (with a growing backoff sleep so the concurrent numeric-getter burst that caused the cross drains before retrying) on a degraded result (number / empty-{} / null-for-never-null) -- or on a transient throw for ANY round-trip method (a "Missing JS member"/"Missing host receiver" throw means the call never executed, so re-issuing is side-effect-free; this is what recovers an emit-time canvas.toDataURL() cross). - Host-call watchdog: for bounded host natives (jso bridge, DOM-element create, ui-settle, canvas->PNG capture, etc.) a lost response resumes the parked thread with a transient error so the retry re-issues / the caller advances. Unbounded natives (image load, fetch, the cn1ss WebSocket) are never aborted. Zero-cost on a healthy channel. - Re-park LightweightPickerButtons (a lightweight-popup EDT deadlock, distinct from the cross, so the retry/watchdog can't rescue it); ValidatorLightweightPicker now runs clean and stays un-parked. Worker-liveness heartbeat + host-ref counters are gated to diag-only (zero production cost). Validated in CI (javascript-screenshots). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the portable, engine-managed-shader 3D graphics API surface: RenderView (PeerComponent-hosted GPU surface), Renderer callback, GraphicsDevice command layer, Material/Light/Camera, Mesh + vertex/index buffers (SIMD-aligned backing for zero-copy upload on ParparVM), Texture, RenderState, VertexFormat, Matrix4 math and Primitives helpers. Adds the impl seam (isOpenGLSupported/createGLPeer/glSetContinuous/ glRequestRender) defaulting to unsupported, plus narrow Display/CN accessors. All backends stubbed; core compiles at -source 1.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the 3D GraphicsDevice for the JavaSE simulator as a dependency-free software rasterizer: depth-buffered, perspective-correct attribute interpolation, back-face culling, texture sampling (nearest/bilinear, clamp/repeat) and per-pixel shading for UNLIT/LAMBERT/PHONG/SPRITE materials. Renders into a BufferedImage presented through a native peer surface (JavaSEGLSurface), with on-demand and continuous (timer-driven) render modes. A software renderer keeps the simulator dependency-free and makes 3D screenshots deterministic across machines and headless CI; native GPU backends are used on device. Adds camera eye getters and a graceful SIMD-alloc fallback in VertexBuffer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds three tests exercising com.codename1.gpu end-to-end through RenderView and the platform peer pipeline: - Gpu3DCubeScreenshotTest: deterministic Phong-lit cube screenshot - Gpu3DTexturedCubeScreenshotTest: deterministic textured (procedural checker) cube - Gpu3DAnimationTest: behavioral test asserting the continuous render loop drives multiple frames to the Renderer Registered in Cn1ssDeviceRunner. Tests degrade to a placeholder on platforms without a 3D backend so the suite never crashes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements the three native GPU backends behind the portable 3D API, plus a shared GLSL ES 1.00 shader generator in core (engine-managed shaders; WebGL1 and GLES2 share the language so Android and JS reuse it): - Android: AndroidGraphicsDevice on GLES20 + a GLSurfaceView peer; program/VBO/IBO caches, lazy upload from SIMD-aligned arrays via direct buffers. - iOS: IOSGraphicsDevice + native CN1GL3D Metal context (CAMetalLayer + CADisplayLink), descriptor-keyed pipeline cache, runtime MSL compilation from a Metal generator, depth attachment, zero-copy MTLBuffers over SIMD-aligned array pointers. Gated on CN1_USE_METAL; IOSNative bridge with the required _R_ wrappers. - JavaScript: HTML5GraphicsDevice over a WebGL canvas peer using the port's JSO interop; reuses the core GLSL generator. Each port overrides isOpenGLSupported/createGLPeer/glSetContinuous/glRequestRender. Verified: Android port BUILD SUCCESS; iOS app compiles+links for iphonesimulator; JS sources compile against the port classpath. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a '3D Graphics and Shaders' chapter covering concepts (engine-managed shaders), RenderView/Renderer, materials, meshes/buffers, camera and Matrix4, per-platform backends and threading. Included after the graphics chapter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…om CI The screenshot harness wrote received PNGs only to a runner temp dir; nothing uploaded them, so a faithful golden could not be (re)seeded from a CI render. Copy $CN1SS_WS_DIR/*.png into $ARTIFACTS_DIR/delivered/ on both the normal and timeout exit paths so they ride along in the javascript-ui-tests artifact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- SpotBugs: drop redundant null-check in AndroidGraphicsDevice.draw; remove unread viewport fields in IOSGraphicsDevice (value goes straight to native). - Docs: satisfy vale quality gate (remove adverb, capitalize heading after colon, use contractions) in the 3D guide chapter. - Tests: rewrite Gpu3DAnimationTest to drive frames via explicit on-demand requestRender() instead of continuous mode. The free-running CADisplayLink in continuous mode wedged the iOS screenshot suite; the two on-demand cube screenshot tests already render correctly on iOS Metal. This also de-risks the JS requestAnimationFrame path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…S suites - PMD: remove unnecessary Light no-arg constructor, foreach in VertexFormat.findByUsage, @OverRide on RenderView.initComponent, drop redundant fully-qualified PeerComponent names in Display. - LanguageTool: accept 'Phong' (lighting-model term) in the guide. - Tests: the iOS and HTML5 hellocodenameone suites run the full screenshot set under a tight per-job time budget; the 3D tests' added runtime cost tipped them over (hang point varied run-to-run, confirming a total-time-budget issue rather than a per-test hang). Skip the three 3D tests on iOS/HTML5; the iOS Metal backend was already verified rendering real screenshots in CI. They still run on the simulator/Android paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…seed cycle) StickyHeader (x3) and StatusBarTapDiagnostic self-skipped on HTML5 because the old console-log-chunked transport truncated their large composite PNGs. The suite now streams over WebSocket (handles large payloads), so the skip is obsolete. Remove it so these run on JS like every other platform. This is the seeding cycle: they deliver as goldenless extras (ignored by the comparator), and the CI delivered-screenshot upload captures their PNGs so the JS goldens can be seeded from a faithful render in the follow-up commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-skip tests The screenshot system could not capture a live GPU peer: an Android GLSurfaceView and a browser WebGL canvas each render to their own surface, separate from the view/output canvas the screenshot path reads, so 3D came out blank. - Android: the GL peer now reads back its framebuffer (glReadPixels) after each drawn frame; AndroidScreenshotTask composites the latest frame of every live GL peer onto the captured screenshot (works regardless of SurfaceView z-order/compositing). Also setZOrderMediaOverlay(true) for on-screen visibility. - JavaScript: WebGL contexts are created with preserveDrawingBuffer; the screenshot path composites each live WebGL peer canvas onto the output canvas before reading pixels. - iOS Metal capture already includes the child Metal layer. - Tests: un-skip the 3D screenshot tests on all platforms; the animation test now captures a deterministic mid-animation frame of the live GPU peer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ise budget Seed-cycle-1 confirmed the four un-skipped tests deliver cleanly over WebSocket (no chunk truncation): StickyHeaderScreenshotTest, StickyHeaderSlideTransition, StickyHeaderFadeTransition, StatusBarTapDiagnostic. Seed their JS goldens from that faithful CI delivery (375x667 deterministic composites) and bump CN1_JS_TIMEOUT_SECONDS 1800 -> 2400 so the four extra grid-composite tests fit the budget (seed-cycle-1 timed out only because the reverted budget was tight). Expected: JS screenshots 93 -> 97 matched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…old GL - Gpu3DAnimationTest now captures the live GPU RenderView at six pinned rotation angles (real device screenshots cropped to the view) composed into a 2x3 grid, matching the hellocodenameone animation-test convention of showing the stages of an animation deterministically rather than one timing-dependent frame. - The static cube/textured tests force a fresh GPU frame (requestRender) shortly before the screenshot so a cold GL surface that has not drawn yet cannot produce a blank capture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Metal backend cleared correctly but drew no geometry: the portable projection produces GL-convention clip space (Y up, Z in [-w, w]) while Metal's framebuffer is Y-down and clips Z to [0, w]. With Y unflipped the cube's front faces wound opposite to frontFacingWinding=CounterClockwise, so back-face culling removed every visible face (only the clear color showed). The generated MSL vertex shader now flips clip Y and remaps clip Z to Metal's range, matching the GL/software backends. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Temporary diagnostics to find why the iOS Metal cube renders only the clear color. To be reverted once the root cause is fixed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…scheduler gate Root cause of the screenshot off-by-one (dual-stream tests like ChatInput/ChatView capturing the previous/next test's form): the cn1ss emit parks the green thread on the __cn1_wait_for_ui_settle__ / __cn1_capture_canvas_png__ host round-trips, which span up to ~24 rAF frames on the host. While parked, drain() keeps stepping every other green thread; their fire-and-forget draw ops (host-call-batch) hit codenameone-canvas mid-sample, so the captured PNG shows the wrong form. The old atomicThread serialization that would have prevented this is dead code (removed to avoid a monitor deadlock). Add a capture-scoped scheduler gate instead of a workaround: - parparvm_runtime.js: captureGateOwner + beginCaptureGate()/endCaptureGate(); while held, drain() defers every OTHER green thread (held aside and restored in the finally, never lost) so none can paint during the capture. The gate is null in the steady state, so non-capture scheduling is byte-for-byte unchanged. - port.js: the cn1ss DOM-capture emit presents the form FIRST (ungated), then holds the gate across the settle+capture host calls in a try/finally. Deadlock-safe by construction: the owner is gated only while parked on the HOST (never on a monitor held by a deferred thread -- the form present runs before the gate), the host capture proceeds independently on the main thread, the HOST_CALL watchdog bounds a lost response, and endCaptureGate is in a finally so an aborted or throwing capture still frees the gate. This is the threading-model fix; no off-screen capture, forced repaint, or golden reseed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The committed TabsTheme_light golden actually contained "ListTheme / light" content -- an off-by-one capture baked into the golden during an earlier buggy run (master was "green" only because its capture was wrong the same way). With the capture-gate serialization in place the capture is now deterministically correct (TabsTheme tabs + "First tab content"), so it no longer matches the stale golden. Reseed from the corrected capture. The other 96 goldens already held correct content and still match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These dual-appearance tests were parked (chatInputEmitHijack/chatViewEmitHijack)
because their capture read the visible canvas while it still showed the PREVIOUS
test's form -- the off-by-one now fixed at the root by the capture-gate scheduler
serialization. Remove the forced-timeout entries so they run. Their existing JS
goldens hold the old off-by-one content, so this run is expected to mismatch on
ChatInput_/ChatView_ {dark,light}; the goldens will be reseeded from the
gate-corrected captures once verified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This reverts commit a90c891.
gl3dGetOrCreatePipeline is the only non-void gl3d native taking String args. ParparVM dispatches such methods through the plain (un-suffixed) C symbol, which was missing (only the _R_long wrapper existed), so the call resolved to null and threw NullPointerException every frame inside getOrCreatePipeline - the cube never drew (only the clear color showed). Added the plain-form symbol delegating to the _R_long impl in both the Metal and non-Metal branches, matching the createImageFromARGB convention in IOSNative.m.
…ark ChatInput/ChatView Root cause of ChatInput_dark capturing the next test's form (ImageViewer): the JS-port bridge (port.js runCn1ssResolvedTest) hard-coded a flat 10s per-test deadline, clobbering Cn1ssDeviceRunner.testTimeoutMs()'s existing rule that a DualAppearanceBaseTest gets 30s on HTML5 (its light+dark phases each pay registerReadyCallback's 1500ms + settle + capture). At 10s the runner force-advanced mid-dark-phase, and the pending dark emit then captured the NEXT test's form. Fix: the bridge passes 0 (sentinel) and awaitTestCompletion computes the type-aware deadline itself, so dual-appearance tests get their full 30s on HTML5 too and their second phase completes in their own slot. Un-park ChatInput/ChatView (their goldens, which hold the old wrong content, will be reseeded from the corrected captures once verified). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…correct content
With ChatInput/ChatView un-parked (dual-appearance deadline fix) and the
capture-gate serialization in place, all five captures now render their own
form correctly (verified visually from the CI delivery):
- ChatInput_{dark,light}: the +/Message/Mic/Send input bar
- ChatView_{dark,light}: the AI Travel Assistant conversation
- TabsTheme_dark: the Tabs theme (Home/Search/Info + first tab content)
Their committed goldens still held off-by-one content baked in by the old
buggy capture (ChatInput_dark had ImageViewer nav; TabsTheme_dark had a
DialogTheme dialog). Reseed from the corrected captures.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the exact convention of working String-arg non-void natives (createVideoComponent, getResourceSize): the plain symbol holds the real implementation and the _R_<rettype> form is a thin wrapper that delegates to it.
The committed golden held "TabsTheme / light" content (off-by-one baked in by the old buggy capture). With the capture-gate + dual-appearance deadline fixes the capture is now deterministically the ToolbarTheme dark form (Menu/Search/ overflow toolbar + "Body content under the Toolbar." -- verified from the CI delivery). Reseed from the corrected capture. It did not surface earlier only because the prior run timed out before reaching it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The iOS Metal 3D peer rendered blank because the pipeline cache was a freed object. The generated Codename One Objective-C is compiled without ARC, so the autoreleased [NSMutableDictionary dictionary] assigned to the strong _pipelineCache ivar was never retained and was deallocated when the autorelease pool drained. The first pipeline lookup then messaged a freed object, which the CN1 signal handler surfaced as a spurious NullPointerException. Allocate it owned with [[NSMutableDictionary alloc] init]. Also reverts the earlier red-herring "plain symbol + _R_ wrapper" change: ParparVM #defines the virtual_ dispatch straight to the _R_long symbol (confirmed in the generated com_codename1_impl_ios_IOSNative.h), so the gl3d native matches every other non-void native here with a single _R_long implementation. Removes all temporary CN1SS-prefixed draw/frame diagnostics from CN1GL3D.m, IOSGraphicsDevice.java and IOSGLSurface.java. The GL-to-Metal clip-space adaptation (Y flip + Z remap) stays in IOSMetalShaderGenerator; it is the other half of getting correct, front-facing geometry on Metal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…frozen-worker heartbeat The late-suite wedge (frozen=1/runnable=0, ~test 86, right after a capture's PNG is computed host-side) can't be isolated from current logs -- the heartbeat shows the worker is parked but not WHAT every thread is parked on, and Playwright can't attach to the worker. Store the symbol in pendingHostCalls and, on a frozen+idle heartbeat, dump the pending host-call symbols+counts (+ timedWakeups length, + captureGate ownership). A run that wedges will then name the lost-response native (suspected __cn1_capture_canvas_png__, whose 10s watchdog is somehow not firing). Diag-only (VM_DIAG_ENABLED); zero production cost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ImageData per capture The screenshot capture's pickBestCanvasSnapshot scores every tracked canvas, and canvasContentScore does 9 getImageData() GPU readbacks each. The suite leaks hundreds of off-screen mutable-image canvases (their FinalizationRegistry-based release never fires under back-to-back load -- canvasCount grows monotonically 18->783, releaseHostRef fires 0 times), so a late capture paid ~700x9 readbacks. Those multi-second captures pressure the worker<->host channel and contribute to the intermittent late-suite lost-response wedge. Cache the score on the per-canvas meta keyed on its last draw-op sequence (meta.lastSeq, already bumped on every op): a canvas not drawn since its last score returns the cached value, so dead off-screen canvases cost nothing. The display canvas is painted every frame (lastSeq advances) so it is always freshly scored -- capture correctness is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OT a lost host call) The WORKER_HB_FROZEN dump revealed the wedge has pendingHostCalls=0 and captureGate=0 -- so it is NOT a lost host-call response and NOT the capture gate. Instead the worker is parked with timedWakeups=1 that never fires (lastThread is the awaitTestCompletion poll's "Timeout Thread"). Extend the frozen dump to print each wakeup's kind + dueIn (negative=overdue) + cancelled, plus whether the backing setTimeout is scheduled (_wakeupTimer) and _wakeupAt. An overdue wakeup with no scheduled timer means _refreshTimedWakeupTimer lost the timer -- a scheduler bug. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s) freeze The first frozen dumps were polluted by legitimate multi-second waits: a thread parked on __cn1_delay__ (registerReadyCallback's 1500ms settle) plus a sleep with a FUTURE dueIn and wakeupTimerSet=1 -- i.e. the worker was waiting, not wedged, and recovered (frozen=0, suite progressed). Gate it behind a frozen streak of >=5 consecutive ~1.5s heartbeats so only a true never-recovering wedge dumps. This separates the two failure modes seen: (1) true hard wedge (frozen forever), (2) slow-but-progressing runs that just miss SUITE:FINISHED in budget. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ole scheduler
ROOT CAUSE of the intermittent late-suite wedge (frozen=1/runnable=0, no host
calls pending). Captured via the new frozen dump + a stack trace:
RuntimeException: host call timed out (jso bridge)
at wrapRawJsErrorAsRuntimeException
... Generator.throw <- drain <- resolveHostCall <- _processExpiredTimedWakeups
Sequence: a __cn1_jso_bridge__ call's 2s watchdog fires (the main thread was
briefly blocked by a heavy late-suite capture -- hundreds of leaked canvases),
resolveHostCall throws the timeout into the parked thread, it surfaces as an
uncaught java.lang.RuntimeException, propagates OUT of the drain loop into the
outer catch -> this.fail(), and dispatch stops. One green thread's uncaught
exception took down the ENTIRE worker -- the suite froze with no error reported
to the host, so ~half of runs never reached SUITE:FINISHED.
Fix: wrap the per-thread generator step so an uncaught exception TERMINATES ONLY
THAT THREAD (Java semantics: Thread.run() unwinds, others keep running) -- mark it
done, wake its joiners (notifyAll on the thread object so monitor/join waiters
don't hang), record it via fail() for diagnostics, and continue draining. A
watchdog timeout now at worst loses one frame instead of wedging the suite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… tests They were parked because ChartCombinedXY's canvasToBlob retry loop HUNG THE SUITE after ~88 fallback captures. That failure mode is now contained by the scheduler-resilience fix: an uncaught green-thread exception / watchdog timeout terminates only that thread, so a capture hang costs at most one frame instead of wedging the whole run. They have no JS goldens yet, so they deliver as ignored extras; goldens will be seeded once the captures are verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
…reenshot tests" This reverts commit 68266e2.
Android screenshot updatesCompared 125 screenshots: 121 matched, 1 updated, 3 missing references.
Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
…eature/gpu-3d-api
|
Compared 122 screenshots: 122 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
|
Compared 11 screenshots: 11 matched. |
Cloudflare Preview
|
The generated MSL negates clip.y so the GL-convention scene matches Metal's top-left framebuffer origin. That negation reverses on-screen triangle winding: a counter-clockwise (GL front) face is drawn clockwise on Metal. The encoder still declared MTLWindingCounterClockwise as front-facing, so back-face culling removed the front faces and kept the back faces - every mesh rendered inside-out. Declare front-facing as clockwise to match the post-Y-flip winding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eature/gpu-3d-api
Adds a game-oriented package built on top of existing Codename One facilities (the EDT animation system, the Graphics pipeline and the media APIs) rather than replacing them. Three subsystems: Game loop + sprites (pure core) - GameView: update(dt)/render(g) loop driven by the Animation system, framerate management, fixed-timestep + interpolation, pollable input via GameInput (no new public methods on existing classes - key capture rides on handlesInput() + the focused-component keyPressed path). - Sprite / SpriteSheet (cached frame slicing) / AnimatedSprite / Scene. Low latency audio (SoundPool / SoundEffect) - SPI com.codename1.media.SoundPoolPeer selected via new Display/CodenameOneImplementation.createSoundPool(int) (null -> fallback). - MediaSoundPoolPeer: pure cross-platform fallback over MediaManager. - Native backends: JavaSE software mixer (volume/pan/rate/polyphony), Android android.media.SoundPool, iOS AVAudioPlayer pool (CN1SoundPool.m + IOSNative glue). The JS port is not in this repo; the fallback covers it. Physics (com.codename1.gaming.physics) - Idiomatic wrapper: PhysicsWorld/PhysicsBody/BodyType/ContactListener/ PhysicsContact; bodies drive sprites via PhysicsLinkable (Sprite implements it). Pixels<->meters and y-flip centralized. - Engine is JBox2D shaded into com.codename1.gaming.physics.box2d (org.jbox2d repackaged, StrictMath->Math, @OverRide stripped for source 1.5; gwt/benchmark trees dropped). Pure Java, so it runs on every platform including iOS via ParparVM. BSD-2 headers retained, attributed in the new root NOTICE file. Docs: new "Game Development" chapter in the developer guide. Demo: Samples/GamingDemoSample (sprites + physics + SFX, no assets). Verified: core compiles at source 1.5; physics simulation (gravity, y-flip, contacts, sprite linkage) and the JavaSE audio mixer both runtime-tested. Android/iOS native audio backends still need device verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Docs style gate: convert the shaded JBox2D /** banners and Javadoc to plain /* block comments (the repo enforces /// markdown doc comments in CodenameOne/ and Ports/CLDC11/). BSD-2 notice text is preserved. This also fixes the JavaDoc and Hugo website builds, whose javadoc step produced no output (zip exit 12) from the malformed input. - CLDC11/ParparVM compatibility: the shaded engine used APIs absent from the Codename One VM. System.nanoTime -> currentTimeMillis (Timer), Math.atan2 -> the engine's fastAtan2 approximation, Math.random -> a shared java.util.Random, Float.floatToRawIntBits -> floatToIntBits. Fixes the core compile in the simulator/native test jobs. - Developer guide Vale/LanguageTool gate: fixed prose in the new chapter (contractions, "framerate" -> "frame rate", "y axis" -> "y-axis", "non blocking" -> "non-blocking", "0..1" -> "0 to 1", façade, removed "very", dropped a flagged proper name). Verified locally: markdown-docs validator passes; javadoc build produces output; Vale and LanguageTool report 0 issues on the chapter; physics simulation still behaves identically after the atan2 change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rgfile
The JavaDoc build (used by both the build-javadocs job and the Hugo
website build) piped `find` through `xargs javadoc`. Once the gaming
package pushed the source list past the runner's xargs command-line
limit, xargs split it across multiple javadoc invocations, each seeing
only a subset of the sources. Cross-package references then failed
en masse ("cannot find symbol", "package com.codename1.ui does not
exist"), javadoc produced no output, and the subsequent zip step exited
12 ("Nothing to do"). macOS xargs has a larger default limit, so the
failure did not reproduce locally.
Write the file list to an @argfile and hand it to one javadoc call, so
the whole source set is always processed together regardless of size.
Also mark the vendored com.codename1.gaming.physics.box2d package as an
intended doc exclusion (consistent with the existing com.codename1.impl
entry).
Verified locally with JDK 25: exit 0, full HTML output and a valid zip,
zero errors, and the gaming wrapper API docs resolve correctly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The JDK 8 unit-test job runs SpotBugs (threshold Low, failOnError) and reported 76 bugs from the new code. - Exclude the vendored com.codename1.gaming.physics.box2d package (lightly adapted JBox2D, BSD-2) from SpotBugs wholesale, the same way TarEntry and the gzip classes are already excluded. The idiomatic com.codename1.gaming.physics wrapper is still analyzed. - Fix the findings in our own code: replace new Integer(int) with Integer.valueOf(int) in GameInput and MediaSoundPoolPeer (DM_NUMBER_CTOR); make PhysicsWorld.ContactDispatcher a static nested class and hoist MediaSoundPoolPeer's loop-restart Runnable into a static RestartVoice class (SIC_INNER_SHOULD_BE_STATIC[_ANON]). Verified locally: `mvn -pl core-unittests -am verify` SpotBugs check is BUILD SUCCESS, and physics contacts still fire after the dispatcher change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The aggregated quality report (and the Android build jobs) run SpotBugs on the port modules and flagged the new Android GameSoundPool: - new Integer(int) -> Integer.valueOf(int) (DM_NUMBER_CTOR) across GameSoundPool (Android) and JavaSESoundPool (JavaSE), and the one new Long(...) added to IOSImplementation's sound-pool loader. - GameSoundPool ignored the boolean result of File.delete() on temp files (RV_RETURN_VALUE_IGNORED_BAD_PRACTICE); route both call sites through a deleteQuietly() helper that consumes the result and falls back to deleteOnExit(). Verified the JavaSE port still compiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
88bc9af to
810ff19
Compare
JavaScript port screenshot updatesCompared 106 screenshots: 103 matched, 3 missing references.
|
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
Rebased onto feature/gpu-3d-api, the sprite layer now renders on the GPU through com.codename1.gpu instead of the EDT Graphics pipeline, using the package's purpose-built 2D support (Material.Type.SPRITE, createTexture(Image), orthographic Camera, transparent RenderState, Primitives.quad). - New SpriteRenderer implements com.codename1.gpu.Renderer: an orthographic camera mapping one world unit to one pixel (top-left origin, y down), drawing each visible sprite as a textured quad via a per-sprite model matrix; images are uploaded to Textures lazily and cached. - Sprite is now a backend-agnostic data holder (image + position/rotation/scale + ARGB tint + normalized anchor + zOrder); the Graphics draw path is gone. It still implements PhysicsLinkable, so physics keeps driving sprites unchanged. - Scene drops its Graphics render method and exposes z-sorted iteration to the renderer; it still advances AnimatedSprites each frame. - GameView is now a com.codename1.gpu.RenderView hosting a SpriteRenderer over its Scene: the GPU drives the frame loop (display link on device, software rasterizer in the simulator), update(double) carries game logic, and there is no draw method, frame-rate, or no-sleep management. Lifecycle is start/stop/pause/resume via continuous rendering; fixed timestep + input unchanged. - Updated the demo and the "Game Development" guide chapter to the GPU model. Verified: core compiles at -source 1.5; the sprite model-matrix/camera math is numerically correct (centered sprite -> NDC origin, top-left sprite -> screen corner); physics still drives linked sprites; SpotBugs and the dev-guide Vale/LanguageTool gates pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mac native screenshot updatesCompared 125 screenshots: 122 matched, 3 missing references.
Benchmark Results
Detailed Performance Metrics
|













Overview
Adds a new
com.codename1.gamingpackage: a game-oriented surface built on top of existing Codename One facilities (the EDT animation system, theGraphicspipeline and the media APIs) rather than replacing them. Game developers get a tightupdate/renderloop, sprite primitives, pollable input, low-latency sound effects and rigid-body physics, all undercom.codename1.gaming(never underui).Everything is pure Java where it matters, so it runs unchanged on every target — including iOS via ParparVM.
What's included
1. Game loop + sprites (pure core)
GameView— subclass it, implementupdate(double dt)+render(Graphics g). Drives the loop through the existingAnimation/registerAnimatedsystem, manages framerate (saves/restores the global value), supports a fixed-timestep accumulator withgetInterpolationAlpha(), and auto-releases its framerate hold on detach. No new public methods on existing classes — key capture rides onhandlesInput()+ the focused-componentkeyPressedpath.GameInput— pollable keyboard/pointer state (level + per-frame edges, game-action aware).Sprite(anchor/rotation/scale/alpha via the affine transform, AABB),SpriteSheet(cached frame slicing),AnimatedSprite,Scene(z-ordered + camera).2. Low-latency audio —
SoundPool/SoundEffectcom.codename1.media.SoundPoolPeer, selected via newDisplay/CodenameOneImplementation.createSoundPool(int)(returnsnull→ fallback).MediaSoundPoolPeer— pure cross-platform fallback overMediaManager(works everywhere today).android.media.SoundPool, iOSAVAudioPlayerpool (CN1SoundPool.m+IOSNativeglue). The JS port isn't in this repo; the fallback covers it.SoundPool.isNativeAccelerated()reports the active path.3. Physics —
com.codename1.gaming.physicsPhysicsWorld/PhysicsBody/BodyType/ContactListener/PhysicsContact. Pixels↔meters and the y-axis flip are centralized so app code stays in screen coordinates.PhysicsLinkable(Spriteimplements it) —world.step()moves the sprite automatically.com.codename1.gaming.physics.box2d(org.jbox2drepackaged,StrictMath→Math,@Overridestripped for-source 1.5; GWT/benchmark trees dropped). BSD-2 license headers retained, attributed in the new rootNOTICE.Docs & demo
docs/developer-guide/Game-Development.asciidoc), wired into the master include after the graphics chapter.Samples/GamingDemoSample— ties it all together (tap to drop bouncing balls with per-drop pitch); generates its sprite images and sound at runtime, so it needs no assets.Verification
-source 1.5(164 shaded engine classes + the API).SoundPoolfunctional on those targets meanwhile.🤖 Generated with Claude Code