More performance work.
This commit is contained in:
parent
3b4effb95a
commit
07f0e4f503
19 changed files with 1062 additions and 32 deletions
|
|
@ -317,6 +317,90 @@ perf-directive memory with the outcome.
|
||||||
scratch, 13-20% of the op; + optional CopyMaskedMark fusion), R10
|
scratch, 13-20% of the op; + optional CopyMaskedMark fusion), R10
|
||||||
fused drawPixel+mark asm entry (~820 -> ~300 cyc), optional R7
|
fused drawPixel+mark asm entry (~820 -> ~300 cyc), optional R7
|
||||||
saveAndDraw consolidation, optional R12 IIgs C-residue reshapes.
|
saveAndDraw consolidation, optional R12 IIgs C-residue reshapes.
|
||||||
- [ ] Phase 3
|
- [x] Phase 3 LANDED 2026-07-07 (goldens refrozen 49 -> 50 per the
|
||||||
- [ ] Phase 4
|
Phase-0 procedure; final gate 50/50 x4). jlTileMapPaste in
|
||||||
|
include/joey/tile.h + tile.c: tileset + row-major uint8_t index map,
|
||||||
|
TILE_MAP_SKIP=0xFF, one validation + one dirty union (68k/DOS);
|
||||||
|
IIgs loop specialized (hoisted stage test, base + (idx<<5) tileset
|
||||||
|
indexing with a sizeof proof -- tiles[idx] cost a 32-bit multiply
|
||||||
|
per cell on the 65816 and initially made the batch SLOWER than the
|
||||||
|
loop). New golden'd row `jlTileMapPaste 10x5` (cross-identity
|
||||||
|
52B9646B x4). RESULTS tiles/s batch vs loop: ST 8,150 (+106%,
|
||||||
|
gap 1.6-1.9x) / Amiga 7,650 (+104%, gap 1.40x = INSIDE the 1.5x
|
||||||
|
target) / DOS 22,500 (+74%) / IIgs 3,000 (+2%; per-tile cost is
|
||||||
|
~93% inner JSL post-Phase-2 -- whole-map asm walker is Phase 4's
|
||||||
|
lever there). SPRITE BATCHES DESCOPED: gate measures only the
|
||||||
|
tilemap row and post-R5 sprite dispatch overhead is ~3-6%; re-add
|
||||||
|
only against a measured need + Scott's API sign-off. API shape
|
||||||
|
SIGNED OFF by Scott 2026-07-07 (tileset + row-major uint8_t map,
|
||||||
|
TILE_MAP_SKIP=0xFF, caller-owned index validity) -- the public
|
||||||
|
header contract in include/joey/tile.h is final.
|
||||||
|
- [x] Phase 4 COMPLETE 2026-07-07 (all four arms resolved; ST landed
|
||||||
|
below). IIgs WHOLE-MAP WALKER LANDED
|
||||||
|
(gate 50/50 + iigs-verify): iigsTileMapPasteMark in joeyDraw.s --
|
||||||
|
one JSL walks the index map with an incrementally-advanced dst
|
||||||
|
offset (ONE gRowOffsetLut read; +4/column, +1280/tile-row), a
|
||||||
|
22-byte stacked D-frame for the register-starved inner (map ptr,
|
||||||
|
tile-src ptr, bases, counters -- X survives the 16-store body and
|
||||||
|
carries the dst offset; Y is clobbered so the map index lives in
|
||||||
|
the frame), bank-carry-safe idx<<5 tileset add, brl on all three
|
||||||
|
wrapping branches, ONE band union tail. RESULT: IIgs jlTileMapPaste
|
||||||
|
10x5 60 -> 140 maps/s = 7,000 tiles/s (+133%), ~400 cyc/tile --
|
||||||
|
BELOW the 480-cyc single-tile hand-coded ceiling (the walker
|
||||||
|
amortizes what per-tile calls still pay). Batch row across ports:
|
||||||
|
ST 8,150 / Amiga 7,650 / IIgs 7,000 / DOS 22,500 tiles/s.
|
||||||
|
AMIGA BLITTER COOKIE-CUT SPRITE: recon complete, DEFERRED with the
|
||||||
|
design recorded -- the source side is net-new (sprite data is
|
||||||
|
fast-RAM chunky + CPU immediates; a chip-RAM pre-shift subsystem
|
||||||
|
is required), the synchronous per-call WaitBlit contract forfeits
|
||||||
|
the parallelism win below ~32x32 (all current UBER sprite rows are
|
||||||
|
16x16 = at/below the crossover), and the honest experiment needs a
|
||||||
|
new >=32x32 golden'd row measured with AND without the fence. Full
|
||||||
|
register program (BLTCON0 0x0F00|shift|0xCA cookie-cut, per-plane B,
|
||||||
|
shared A mask) + risk list in the recon (session 2026-07-07).
|
||||||
|
ST ARM LANDED 2026-07-07 (4-agent recon + adversary, then C-first
|
||||||
|
per the binding feedback): the "movem row engine" hypothesis is
|
||||||
|
DEAD -- the tile dst is a byte scatter movem cannot address, and
|
||||||
|
byte-pair word fusion loses on a barrel-shifterless 68000 (lsl.w #8
|
||||||
|
= 22 cyc > the move.b it replaces). The C incremental pair walker
|
||||||
|
(stTileMapPaste, stTile.h) measured +17% (adversary predicted the
|
||||||
|
+15-23% cap: gcc already hoists the row mulu; the 632-cyc copy body
|
||||||
|
dominates) -- that measured C ceiling justified asm, and the real
|
||||||
|
weapon is MOVEP: stTileMapPasteMovep (src/atarist/tileMap.s) does
|
||||||
|
move.l (a0)+ / movep.l d0,d16(a1) = one tile row's 4 plane bytes in
|
||||||
|
36 cyc. ST batch row 163 -> 346 ops/s = 17,300 tiles/s (2.12x, 462
|
||||||
|
cyc/tile), ABOVE the Phase-0 12,700-15,500 ceiling (it assumed
|
||||||
|
byte-move bodies). C walker stays as the odd-tileset-pointer
|
||||||
|
fallback (move.l needs an even src; jlTileT is align-1). Gate: ST
|
||||||
|
50/50 (52B9646B held) + 12-case on-emulator TILETEST proof of the
|
||||||
|
head/tail/w1/h1/skip/odd-ptr paths the golden never exercises, each
|
||||||
|
with border-ring containment. RESTORE-FROM-CLEAN-BUFFER: EXPERIMENT
|
||||||
|
RUN AND PASSED 2026-07-07 (Scott approved same day; sign-off also
|
||||||
|
ratified the jlTileMapPaste shape). jlSurfaceCopyRect is PUBLIC API
|
||||||
|
(include/joey/surface.h): same-position rect copy, clip + outward
|
||||||
|
16-px group snap UNIFORM on every port (uniform snap is what keeps
|
||||||
|
cross-port hashes identical), pixels only, dirty-marked via
|
||||||
|
surfaceMarkDirtyRect; generic chunky row-memcpy engine (DOS + both
|
||||||
|
IIgs flavors -- the IIgs stage IS plain-C writable through its
|
||||||
|
bank-$01 pixels pointer, adversary-verified), ST = strided
|
||||||
|
interleaved row memcpys, Amiga = per-plane CPU row copies (blitter
|
||||||
|
modulo variant recorded as its upgrade; CopyMemQuick unusable at
|
||||||
|
16-px grain). Documented caller hazards: snapped erase can touch
|
||||||
|
content up to 15 px outside the rect and invalidates overlapping
|
||||||
|
live sprite backups -- erase-all-then-draw-all, one mechanism per
|
||||||
|
region. STRATEGY row `gameFrameClean composite` golden'd (contract
|
||||||
|
50 -> 51, cross-identity C51FE171 x4): vs gameFrame ST 48/22 =
|
||||||
|
2.18x (bar was 1.4x) / DOS 195/19 = 10.3x / IIgs 18/10 = 1.8x /
|
||||||
|
Amiga 34/25 = 1.36x (least, as the adversary predicted: blitter
|
||||||
|
fillRect meant less repaint to delete + chip-chip copy vs fast-RAM
|
||||||
|
backup restore). A library-transparent variant stays REJECTED (the
|
||||||
|
ST present source is the composited shadow, sprites corrupt it
|
||||||
|
pre-present; a clean source means a third 32KB buffer + routing
|
||||||
|
API by another name). IIGS
|
||||||
|
PEI-SLAM: evaluated and REJECTED -- models ~+28% on the map walker
|
||||||
|
but holds SEI for the whole map (~5.6 ms at 10x5; audio ISR jitter)
|
||||||
|
and the VBL-driven bench clock cannot honestly measure SEI ops
|
||||||
|
(SEI-inflation artifact); the landed walker already beats the
|
||||||
|
480-cyc per-tile ceiling. Batch row after Phase 4: DOS 22,500 / ST
|
||||||
|
17,300 / Amiga 7,650 / IIgs 7,000 tiles/s.
|
||||||
- [ ] Phase 5
|
- [ ] Phase 5
|
||||||
|
|
|
||||||
164
PERF.md
164
PERF.md
|
|
@ -102,6 +102,110 @@ half-buffer IRQ), IIgs 0% (no handler installed until PlayMod). ALL
|
||||||
pre-2026-07-07 ST comparisons -- including the Phase-0 ceiling gaps
|
pre-2026-07-07 ST comparisons -- including the Phase-0 ceiling gaps
|
||||||
and the Phase-1 relative results -- understated the ST by ~2x.
|
and the Phase-1 relative results -- understated the ST by ~2x.
|
||||||
|
|
||||||
|
**2026-07-07, NATIVE-PERF PHASE 4 (IIgs arm) LANDED -- whole-map asm
|
||||||
|
walker:** iigsTileMapPasteMark (joeyDraw.s) replaces the IIgs
|
||||||
|
stage-arm per-tile loop of jlTileMapPaste with ONE JSL that walks the
|
||||||
|
index map: dst offset advances incrementally (+4/column, +1280/tile
|
||||||
|
row) from a single gRowOffsetLut read, the register-starved inner
|
||||||
|
lives in a 22-byte stacked D-frame (X survives the 16-store body and
|
||||||
|
carries the dst offset; the map index sits in the frame because Y is
|
||||||
|
clobbered), the idx << 5 tileset add carries into the bank byte, all
|
||||||
|
three wrapping branches are brl, and ONE band union closes the call.
|
||||||
|
Gated 50/50 + make iigs-verify. RESULT: IIgs batch row 60 -> 140
|
||||||
|
maps/s = 7,000 tiles/s (+133%), ~400 cyc/tile -- BELOW the 480-cyc
|
||||||
|
single-tile hand-coded ceiling, because the walker amortizes the
|
||||||
|
JSL/derive/mark that even a hand-coded per-tile call pays. Batch row
|
||||||
|
across all ports: ST 8,150 / Amiga 7,650 / IIgs 7,000 / DOS 22,500
|
||||||
|
tiles/sec (the morning's per-call loop numbers were 1,600 / 2,300 /
|
||||||
|
1,750 / 10,000). The Amiga blitter cookie-cut sprite was recon'd and
|
||||||
|
DEFERRED with a full design on record: the synchronous WaitBlit the
|
||||||
|
hash contract requires erases the parallelism win at <= 16x16 (every
|
||||||
|
current sprite row), and the chip-RAM pre-shifted source data is a
|
||||||
|
net-new subsystem -- the honest experiment is a >= 32x32 golden'd row
|
||||||
|
measured with and without the fence.
|
||||||
|
|
||||||
|
**2026-07-07, NATIVE-PERF PHASE 4 CLOSED -- clean-buffer experiment
|
||||||
|
PASSED, jlSurfaceCopyRect is public API:** Scott signed off the
|
||||||
|
jlTileMapPaste shape and approved the clean-buffer experiment; both
|
||||||
|
landed same-day. jlSurfaceCopyRect(dst, src, x, y, w, h) is a
|
||||||
|
SAME-POSITION rect copy (clip, then snap outward to the 16-px group
|
||||||
|
grid on every port -- uniform snap keeps cross-port hashes
|
||||||
|
byte-identical): core wrapper + dirty mark in surface.c, generic
|
||||||
|
chunky row-memcpy engine (serves DOS and both IIgs surface flavors --
|
||||||
|
the IIgs stage pixels pointer reaches bank $01 through ordinary
|
||||||
|
large-model stores, recon-verified against the sprite-restore
|
||||||
|
precedent), ST override = strided interleaved row memcpys (the
|
||||||
|
raw-span restore recipe), Amiga override = per-plane CPU row copies
|
||||||
|
(CopyMemQuick is unusable at 16-px grain: word alignment only;
|
||||||
|
the modulo-driven blitter variant is the recorded production
|
||||||
|
upgrade). The STRATEGY row `gameFrameClean composite` (erase 3
|
||||||
|
sprite windows from a pristine clean surface + 3 draws + present; NO
|
||||||
|
per-frame background repaint, NO save-under -- the static-background
|
||||||
|
pattern, not an op-for-op gameFrame speedup) measures, vs gameFrame:
|
||||||
|
**ST 48 vs 22 = 2.18x (acceptance bar was 1.4x), DOS 195 vs 19 =
|
||||||
|
10.3x, IIgs 18 vs 10 = 1.8x, Amiga 34 vs 25 = 1.36x** (Amiga gains
|
||||||
|
least, exactly as the adversarial recon predicted: its fillRect was
|
||||||
|
already blitter-fast, and clean->stage is a chip-to-chip CPU copy
|
||||||
|
against display DMA -- the blitter rect variant is its lever).
|
||||||
|
GOLDEN CONTRACT now **51 lines**: regression gate exactly 50 match +
|
||||||
|
1 extra per port, 4-port cross-identity of the new row (hash
|
||||||
|
C51FE171 x4), then freeze. Found along the way: `make all` does NOT
|
||||||
|
regenerate joey.2mg, so bench-iigs.sh silently benched the previous
|
||||||
|
UBER (clean 50/50, new row absent) until `make iigs-disk` -- recorded
|
||||||
|
in the stale-binary memory.
|
||||||
|
|
||||||
|
**2026-07-07, NATIVE-PERF PHASE 4 (ST arm) LANDED -- movep.l map
|
||||||
|
engine:** the ST jlTileMapPaste arm now dispatches to
|
||||||
|
stTileMapPasteMovep (src/atarist/tileMap.s): per tile row, one
|
||||||
|
`move.l (a0)+` + `movep.l d0,d16(a1)` writes all 4 interleaved plane
|
||||||
|
bytes in 36 cycles vs ~80 for the four compiled move.b -- movep IS
|
||||||
|
the half-group byte-scatter pattern (the classic ST c2p trick), where
|
||||||
|
movem is useless (byte-granular dst) and byte-pair word fusion loses
|
||||||
|
(lsl.w #8 costs 22 cyc on a barrel-shifterless 68000, priced during
|
||||||
|
recon). Cells walk in even/odd pairs sharing one group pointer
|
||||||
|
(+8/pair, all row offsets compile-time d16); odd bx = head cell, odd
|
||||||
|
width = tail cell. The C pair walker (stTileMapPaste in stTile.h,
|
||||||
|
+17% alone -- adversarial recon correctly predicted the pure-C cap at
|
||||||
|
+15-23% because gcc already hoists the mulu and the 632-cyc copy body
|
||||||
|
dominates) stays as the odd-tileset-pointer fallback: `move.l (a0)+`
|
||||||
|
needs an even source, jlTileT is align-1, and the guard is one AND
|
||||||
|
per call. RESULT: ST batch row 163 -> 346 ops/sec = **17,300
|
||||||
|
tiles/s** (2.12x, 462 cyc/tile) -- ABOVE the Phase-0 12,700-15,500
|
||||||
|
"hand-coded ceiling", which under-counted by assuming byte-move
|
||||||
|
bodies. Gate: ST 50/50 bit-identical (hash 52B9646B held through
|
||||||
|
both engines) + a 12-case on-emulator proof (TILETEST, session
|
||||||
|
scratchpad) covering odd-bx head, odd-width tail, w=1 columns, h=1,
|
||||||
|
checkerboard/end/all skips, and both odd-pointer fallback paths,
|
||||||
|
each with a border-ring containment check. Batch row across ports:
|
||||||
|
DOS 22,500 / ST 17,300 / Amiga 7,650 / IIgs 7,000 tiles/s. IIgs
|
||||||
|
PEI-slam map variant was EVALUATED and REJECTED: models ~+28%
|
||||||
|
(~312 cyc/tile) but requires SEI for the whole map (~5.6 ms
|
||||||
|
interrupts-off at 10x5, audio ISR jitter) AND the VBL-driven bench
|
||||||
|
clock cannot honestly measure SEI ops (PERF artifact: SEI inflation)
|
||||||
|
-- the walker already beats the 480-cyc per-tile ceiling without it.
|
||||||
|
|
||||||
|
**2026-07-07, NATIVE-PERF PHASE 3 LANDED -- jlTileMapPaste batch API:**
|
||||||
|
one public entry pastes a wTiles x hTiles rectangle from a tileset +
|
||||||
|
row-major uint8_t index map (TILE_MAP_SKIP = 0xFF leaves a cell
|
||||||
|
untouched): one validation, one dirty union (68k/DOS; the IIgs
|
||||||
|
stage-fused entries mark per pasted tile, and its loop hoists the
|
||||||
|
stage test + does tileset indexing as base + (idx << 5) -- the
|
||||||
|
natural tiles[idx] spelling cost a 32-bit indexed multiply per cell
|
||||||
|
on the 65816 and erased the batch win until specialized). GOLDEN
|
||||||
|
CONTRACT now **50 lines**: refreeze followed the Phase-0 procedure
|
||||||
|
(regression exactly 49 match + 1 extra per port, then 4-port
|
||||||
|
cross-identity of the new `jlTileMapPaste 10x5` row -- all four hash
|
||||||
|
52B9646B -- then freeze). RESULTS, tiles/sec batch vs per-call loop:
|
||||||
|
ST 8,150 vs 4,000 (+106%, gap-to-ceiling 1.6-1.9x); Amiga 7,650 vs
|
||||||
|
3,750 (+104%, gap 1.40x -- INSIDE the ~1.5x program target); DOS
|
||||||
|
22,500 vs 12,900 (+74%, 2.5x); IIgs 3,000 vs 2,950 (+2% -- its
|
||||||
|
Phase-2 fused entries already deleted the per-tile wrapper, so the
|
||||||
|
remaining cost is ~93% inner JSL; a whole-map IIgs asm walker is the
|
||||||
|
Phase-4 lever). Sprite batch APIs were DESCOPED from Phase 3: the
|
||||||
|
phase gate measures only the tilemap row, and post-R5 sprite dispatch
|
||||||
|
overhead is ~3-6% -- not worth new public surface without a measured
|
||||||
|
need.
|
||||||
|
|
||||||
**2026-07-07, NATIVE-PERF PHASE 2 COMPLETE -- Steps D3 (R6 + R10)
|
**2026-07-07, NATIVE-PERF PHASE 2 COMPLETE -- Steps D3 (R6 + R10)
|
||||||
close the phase:** (R6) the IIgs masked-copy inner's 4 x 8 jsr/rts
|
close the phase:** (R6) the IIgs masked-copy inner's 4 x 8 jsr/rts
|
||||||
round-trips to tmaskByte were inlined via the TMASK_BYTE assembler
|
round-trips to tmaskByte were inlined via the TMASK_BYTE assembler
|
||||||
|
|
@ -244,49 +348,53 @@ meet or beat it -- project directive). Non-IIgs cells show absolute
|
||||||
ops/sec plus percentage of the IIgs number; sub-100% cells are bolded
|
ops/sec plus percentage of the IIgs number; sub-100% cells are bolded
|
||||||
as below-floor. `tools/uber-perf-table` emits this table directly;
|
as below-floor. `tools/uber-perf-table` emits this table directly;
|
||||||
regenerate after any re-capture (Amiga needs TIMEOUT=600 -- the
|
regenerate after any re-capture (Amiga needs TIMEOUT=600 -- the
|
||||||
default cuts off the final checks). Capture provenance: ST 2026-07-07
|
default cuts off the final checks). Capture provenance: all four
|
||||||
(post-audio-fix); IIgs/Amiga/DOS 2026-07-06 (recovery-gate rerun,
|
ports 2026-07-07, the 51-line golden freeze gate (jlSurfaceCopyRect +
|
||||||
unaffected by the ST fix).
|
gameFrameClean experiment; ST column includes the movep.l map engine;
|
||||||
|
Amiga run with TIMEOUT=600; IIgs from a fresh `make iigs-disk` image
|
||||||
|
-- `make all` alone leaves joey.2mg stale).
|
||||||
|
|
||||||
| Op | IIgs (ops/sec) | Amiga (ops/sec, % of IIGS) | Atari ST (ops/sec, % of IIGS) | DOS (ops/sec, % of IIGS) |
|
| Op | IIgs (ops/sec) | Amiga (ops/sec, % of IIGS) | Atari ST (ops/sec, % of IIGS) | DOS (ops/sec, % of IIGS) |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| jlSurfaceClear | 33 | 75 (227%) | 88 (267%) | 66 (200%) |
|
| jlSurfaceClear | 33 | 75 (227%) | 88 (267%) | 66 (200%) |
|
||||||
| jlPaletteSet | 1561 | 11403 (730%) | 10388 (665%) | 29809 (1910%) |
|
| jlPaletteSet | 1561 | 11503 (737%) | 10388 (665%) | 29809 (1910%) |
|
||||||
| jlScbSetRange | 163 | 3953 (2425%) | 4833 (2965%) | 8495 (5212%) |
|
| jlScbSetRange | 163 | 4003 (2456%) | 4833 (2965%) | 8495 (5212%) |
|
||||||
| jlDrawPixel | 5576 | 5853 (105%) | 6522 (117%) | 23687 (425%) |
|
| jlDrawPixel | 5576 | 5853 (105%) | 6522 (117%) | 23687 (425%) |
|
||||||
| jlDrawLine H | 1142 | 1753 (154%) | 2670 (234%) | 5708 (500%) |
|
| jlDrawLine H | 1142 | 1753 (154%) | 2670 (234%) | 5708 (500%) |
|
||||||
| jlDrawLine V | 99 | 303 (306%) | 261 (264%) | 468 (473%) |
|
| jlDrawLine V | 99 | 303 (306%) | 261 (264%) | 468 (473%) |
|
||||||
| jlDrawLine diag | 42 | **40 (95%)** | 88 (210%) | 109 (260%) |
|
| jlDrawLine diag | 42 | **40 (95%)** | 88 (210%) | 109 (260%) |
|
||||||
| jlDrawRect 100x100 | 73 | 285 (390%) | 279 (382%) | 454 (622%) |
|
| jlDrawRect 100x100 | 73 | 285 (390%) | 279 (382%) | 470 (644%) |
|
||||||
| jlDrawCircle r=16 | 304 | **285 (94%)** | 366 (120%) | 447 (147%) |
|
| jlDrawCircle r=16 | 304 | **285 (94%)** | 359 (118%) | 447 (147%) |
|
||||||
| jlDrawCircle r=80 | 70 | **63 (90%)** | 83 (119%) | 98 (140%) |
|
| jlDrawCircle r=80 | 70 | **63 (90%)** | 83 (119%) | 98 (140%) |
|
||||||
| jlFillRect 16x16 | 602 | 1153 (192%) | 1203 (200%) | 3013 (500%) |
|
| jlFillRect 16x16 | 602 | 1153 (192%) | 1225 (203%) | 3027 (503%) |
|
||||||
| jlFillRect 80x80 | 110 | 285 (259%) | 240 (218%) | 414 (376%) |
|
| jlFillRect 80x80 | 110 | 285 (259%) | 236 (215%) | 414 (376%) |
|
||||||
| jlFillRect 320x200 | 21 | 68 (324%) | 77 (367%) | 58 (276%) |
|
| jlFillRect 320x200 | 21 | 68 (324%) | 77 (367%) | 58 (276%) |
|
||||||
| jlFillCircle r=40 | 42 | **31 (74%)** | 155 (369%) | 156 (371%) |
|
| jlFillCircle r=40 | 42 | **31 (74%)** | 155 (369%) | 156 (371%) |
|
||||||
| jlSamplePixel | 4797 | 7253 (151%) | 9603 (200%) | 46397 (967%) |
|
| jlSamplePixel | 4797 | 7253 (151%) | 9603 (200%) | 46251 (964%) |
|
||||||
| jlTileFill | 3251 | 3753 (115%) | 3855 (119%) | 11840 (364%) |
|
| jlTileFill | 3251 | 3703 (114%) | 3855 (119%) | 11840 (364%) |
|
||||||
| jlTileCopy | 1988 | 2903 (146%) | 3445 (173%) | 10913 (549%) |
|
| jlTileCopy | 1988 | 2953 (149%) | 3445 (173%) | 10913 (549%) |
|
||||||
| jlTileCopyMasked | 681 | 1003 (147%) | 1632 (240%) | 3391 (498%) |
|
| jlTileCopyMasked | 681 | 1003 (147%) | 1632 (240%) | 3391 (498%) |
|
||||||
| jlTilePaste | 2710 | 3803 (140%) | 4033 (149%) | 12545 (463%) |
|
| jlTilePaste | 2710 | 3803 (140%) | 4033 (149%) | 12601 (465%) |
|
||||||
| jlTileSnap | 2530 | 6003 (237%) | 6403 (253%) | 19247 (761%) |
|
| jlTileSnap | 2530 | 6003 (237%) | 6403 (253%) | 19247 (761%) |
|
||||||
| jlSpriteSaveUnder | 602 | 2103 (349%) | 2611 (434%) | 10743 (1785%) |
|
| jlSpriteSaveUnder | 602 | 2103 (349%) | 2611 (434%) | 10743 (1785%) |
|
||||||
| jlSpriteDraw | 966 | 1103 (114%) | 1283 (133%) | 6390 (661%) |
|
| jlSpriteDraw | 966 | 1103 (114%) | 1283 (133%) | 6404 (663%) |
|
||||||
| jlSpriteRestoreUnder | 545 | 1453 (267%) | 1781 (327%) | 6246 (1146%) |
|
| jlSpriteRestoreUnder | 545 | 1453 (267%) | 1781 (327%) | 6218 (1141%) |
|
||||||
| jlSpriteSaveAndDraw | 423 | 753 (178%) | 892 (211%) | 4656 (1101%) |
|
| jlSpriteSaveAndDraw | 423 | 803 (190%) | 892 (211%) | 4668 (1104%) |
|
||||||
| jlStagePresent full | 67 | 540 (806%) | 467 (697%) | 1132 (1690%) |
|
| jlStagePresent full | 67 | 534 (797%) | 467 (697%) | 1137 (1697%) |
|
||||||
| jlInputPoll | 228 | 4003 (1756%) | 2841 (1246%) | 3148 (1381%) |
|
| jlInputPoll | 228 | 4053 (1778%) | 2788 (1223%) | 3148 (1381%) |
|
||||||
| jlKeyDown | 10191 | 39753 (390%) | 44448 (436%) | 77738 (763%) |
|
| jlKeyDown | 10191 | 39353 (386%) | 45286 (444%) | 77526 (761%) |
|
||||||
| jlKeyPressed | 11389 | 38003 (334%) | 44448 (390%) | 74907 (658%) |
|
| jlKeyPressed | 11389 | 38353 (337%) | 45286 (398%) | 75119 (660%) |
|
||||||
| jlMouseX | 20214 | 97703 (483%) | 117920 (583%) | 155685 (770%) |
|
| jlMouseX | 20214 | 96753 (479%) | 117981 (584%) | 155281 (768%) |
|
||||||
| joeyJoyConnected | 10191 | 38903 (382%) | 44260 (434%) | 77748 (763%) |
|
| joeyJoyConnected | 10191 | 38903 (382%) | 44260 (434%) | 77537 (761%) |
|
||||||
| jlAudioFrameTick | 27509 | 57003 (207%) | 39885 (145%) | 59372 (216%) |
|
| jlAudioFrameTick | 27509 | 56353 (205%) | 40637 (148%) | 59211 (215%) |
|
||||||
| jlAudioIsPlayingMod | 11071 | 56403 (509%) | 60441 (546%) | 128214 (1158%) |
|
| jlAudioIsPlayingMod | 11071 | 56453 (510%) | 60441 (546%) | 128004 (1156%) |
|
||||||
| surfaceMarkDirtyRect (via jlFillRect 32x32) | 323 | 853 (264%) | 788 (244%) | 1476 (457%) |
|
| surfaceMarkDirtyRect (via jlFillRect 32x32) | 323 | 853 (264%) | 774 (240%) | 1476 (457%) |
|
||||||
| jlSpriteDraw unaligned | 906 | **903 (100%)** | 1070 (118%) | 3837 (424%) |
|
| jlSpriteDraw unaligned | 906 | **903 (100%)** | 1070 (118%) | 5215 (576%) |
|
||||||
| jlSpriteDraw sweep16 | 60 | **56 (93%)** | 68 (113%) | 362 (603%) |
|
| jlSpriteDraw sweep16 | 60 | **56 (93%)** | 68 (113%) | 362 (603%) |
|
||||||
| jlTilePaste map 10x5 | 59 | 78 (132%) | 80 (136%) | 259 (439%) |
|
| jlTilePaste map 10x5 | 59 | 78 (132%) | 80 (136%) | 258 (437%) |
|
||||||
| gameFrame composite | 10 | 25 (250%) | 21 (210%) | 19 (190%) |
|
| gameFrame composite | 10 | 25 (250%) | 22 (220%) | 19 (190%) |
|
||||||
|
| jlTileMapPaste 10x5 | 140 | 153 (109%) | 346 (247%) | 450 (321%) |
|
||||||
|
| gameFrameClean composite | 18 | 34 (189%) | 48 (267%) | 195 (1083%) |
|
||||||
|
|
||||||
> **IIgs full-screen ceiling (~29 ops/sec).** A full-screen op moves
|
> **IIgs full-screen ceiling (~29 ops/sec).** A full-screen op moves
|
||||||
> 32000 bytes = 16000 word-stores x 6 cyc (STA long,X / PEI, both
|
> 32000 bytes = 16000 word-stores x 6 cyc (STA long,X / PEI, both
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,19 @@ static void op_spriteDrawSweep(void) {
|
||||||
#define UBER_TILEMAP_W 10
|
#define UBER_TILEMAP_W 10
|
||||||
#define UBER_TILEMAP_H 5
|
#define UBER_TILEMAP_H 5
|
||||||
|
|
||||||
|
// Batch twin of op_tileMapPaste: the SAME 10x5 alternating map in ONE
|
||||||
|
// jlTileMapPaste call (NATIVE-PERF Phase 3) at a disjoint spot two
|
||||||
|
// block-rows below, so both rows keep independent pixel signatures.
|
||||||
|
// gTileSet[0/1] are struct copies of gTileMapA/B made at setup.
|
||||||
|
#define UBER_TILEMAP_B_Y0 (UBER_TILEMAP_Y0 + UBER_TILEMAP_H + 1)
|
||||||
|
static jlTileT gTileSet[2];
|
||||||
|
static uint8_t gTileMapIndices[UBER_TILEMAP_W * UBER_TILEMAP_H];
|
||||||
|
|
||||||
|
static void op_tileMapPasteBatch(void) {
|
||||||
|
jlTileMapPaste(gStage, UBER_TILEMAP_X0, UBER_TILEMAP_B_Y0, UBER_TILEMAP_W, UBER_TILEMAP_H, gTileSet, gTileMapIndices);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void op_tileMapPaste(void) {
|
static void op_tileMapPaste(void) {
|
||||||
uint8_t tx;
|
uint8_t tx;
|
||||||
uint8_t ty;
|
uint8_t ty;
|
||||||
|
|
@ -392,8 +405,10 @@ static void op_tileMapPaste(void) {
|
||||||
static const int16_t gFrameSpriteX[UBER_FRAME_SPRITES] = { 48, 144, 240 };
|
static const int16_t gFrameSpriteX[UBER_FRAME_SPRITES] = { 48, 144, 240 };
|
||||||
static const int16_t gFrameSpriteY[UBER_FRAME_SPRITES] = { 80, 96, 112 };
|
static const int16_t gFrameSpriteY[UBER_FRAME_SPRITES] = { 80, 96, 112 };
|
||||||
|
|
||||||
static void op_gameFrame(void) {
|
// The frame's static content (play-region fill + HUD tile row) --
|
||||||
int16_t i;
|
// shared between op_gameFrame's per-call repaint and the one-time
|
||||||
|
// clean-surface paint of op_gameFrameClean.
|
||||||
|
static void paintFrameStatic(void) {
|
||||||
uint8_t t;
|
uint8_t t;
|
||||||
|
|
||||||
jlFillRect(gStage, UBER_FRAME_RX, UBER_FRAME_RY, UBER_FRAME_RW, UBER_FRAME_RH, UBER_FRAME_BG);
|
jlFillRect(gStage, UBER_FRAME_RX, UBER_FRAME_RY, UBER_FRAME_RW, UBER_FRAME_RH, UBER_FRAME_BG);
|
||||||
|
|
@ -404,6 +419,13 @@ static void op_gameFrame(void) {
|
||||||
jlTilePaste(gStage, (uint8_t)(UBER_FRAME_TILE_TX + t), UBER_FRAME_TILE_TY, &gTileMapB);
|
jlTilePaste(gStage, (uint8_t)(UBER_FRAME_TILE_TX + t), UBER_FRAME_TILE_TY, &gTileMapB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void op_gameFrame(void) {
|
||||||
|
int16_t i;
|
||||||
|
|
||||||
|
paintFrameStatic();
|
||||||
for (i = 0; i < UBER_FRAME_SPRITES; i++) {
|
for (i = 0; i < UBER_FRAME_SPRITES; i++) {
|
||||||
jlSpriteSaveAndDraw(gStage, gSprite, gFrameSpriteX[i], gFrameSpriteY[i], &gFrameBackups[i]);
|
jlSpriteSaveAndDraw(gStage, gSprite, gFrameSpriteX[i], gFrameSpriteY[i], &gFrameBackups[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -414,6 +436,31 @@ static void op_gameFrame(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Clean-buffer twin of op_gameFrame (NATIVE-PERF Phase 4 STRATEGY
|
||||||
|
// row -- it measures the clean-buffer pattern for static-background
|
||||||
|
// games, not an op-for-op speedup of gameFrame). The background +
|
||||||
|
// HUD are painted ONCE into gCleanSurface before the timed window,
|
||||||
|
// so the per-frame body is 3 window erases from the clean copy + 3
|
||||||
|
// sprite draws + present: the per-frame background repaint and the
|
||||||
|
// per-sprite save-under disappear. Erase-all-then-draw-all per the
|
||||||
|
// jlSurfaceCopyRect contract. End state every call = sprites drawn
|
||||||
|
// over clean background, stable from call 1, so the post-window
|
||||||
|
// hash is iteration-count independent.
|
||||||
|
static jlSurfaceT *gCleanSurface = NULL;
|
||||||
|
|
||||||
|
static void op_gameFrameClean(void) {
|
||||||
|
int16_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < UBER_FRAME_SPRITES; i++) {
|
||||||
|
jlSurfaceCopyRect(gStage, gCleanSurface, gFrameSpriteX[i], gFrameSpriteY[i], 16, 16);
|
||||||
|
}
|
||||||
|
for (i = 0; i < UBER_FRAME_SPRITES; i++) {
|
||||||
|
jlSpriteDraw(gStage, gSprite, gFrameSpriteX[i], gFrameSpriteY[i]);
|
||||||
|
}
|
||||||
|
jlStagePresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----- Build the ball sprite procedurally -----
|
// ----- Build the ball sprite procedurally -----
|
||||||
|
|
||||||
#define BALL_TILES_X 2
|
#define BALL_TILES_X 2
|
||||||
|
|
@ -1026,12 +1073,40 @@ static void __attribute__((noinline)) runAllTests(void) {
|
||||||
jlFillRect(gStage, 8, 72, 8, 8, 9);
|
jlFillRect(gStage, 8, 72, 8, 8, 9);
|
||||||
jlDrawLine(gStage, 8, 72, 15, 79, 1);
|
jlDrawLine(gStage, 8, 72, 15, 79, 1);
|
||||||
jlTileSnap(gStage, 1, 9, &gTileMapB);
|
jlTileSnap(gStage, 1, 9, &gTileMapB);
|
||||||
|
gTileSet[0] = gTileMapA;
|
||||||
|
gTileSet[1] = gTileMapB;
|
||||||
|
{
|
||||||
|
uint8_t tx;
|
||||||
|
uint8_t ty;
|
||||||
|
for (ty = 0; ty < UBER_TILEMAP_H; ty++) {
|
||||||
|
for (tx = 0; tx < UBER_TILEMAP_W; tx++) {
|
||||||
|
gTileMapIndices[ty * UBER_TILEMAP_W + tx] = (uint8_t)((tx + ty) & 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
gFrameBackups[0].bytes = gFrameBackupBytes[0];
|
gFrameBackups[0].bytes = gFrameBackupBytes[0];
|
||||||
gFrameBackups[1].bytes = gFrameBackupBytes[1];
|
gFrameBackups[1].bytes = gFrameBackupBytes[1];
|
||||||
gFrameBackups[2].bytes = gFrameBackupBytes[2];
|
gFrameBackups[2].bytes = gFrameBackupBytes[2];
|
||||||
|
|
||||||
timeOp("jlTilePaste map 10x5", op_tileMapPaste);
|
timeOp("jlTilePaste map 10x5", op_tileMapPaste);
|
||||||
timeOp("gameFrame composite", op_gameFrame);
|
timeOp("gameFrame composite", op_gameFrame);
|
||||||
|
timeOp("jlTileMapPaste 10x5", op_tileMapPasteBatch);
|
||||||
|
|
||||||
|
// Clean-buffer strategy row (appended last per the cumulative-
|
||||||
|
// hash safety rule): paint the frame's static content, snapshot
|
||||||
|
// the WHOLE stage into the clean surface (clean == stage
|
||||||
|
// everywhere makes the snapped erase windows hash-safe by
|
||||||
|
// construction), then time erase+draw+present.
|
||||||
|
gCleanSurface = jlSurfaceCreate();
|
||||||
|
if (gCleanSurface != NULL) {
|
||||||
|
paintFrameStatic();
|
||||||
|
jlSurfaceCopy(gCleanSurface, gStage);
|
||||||
|
timeOp("gameFrameClean composite", op_gameFrameClean);
|
||||||
|
jlSurfaceDestroy(gCleanSurface);
|
||||||
|
gCleanSurface = NULL;
|
||||||
|
} else {
|
||||||
|
jlLogF("UBER: gameFrameClean composite: SKIPPED (surface alloc failed)\n");
|
||||||
|
}
|
||||||
|
|
||||||
jlLogF("UBER: ----- end -----\n");
|
jlLogF("UBER: ----- end -----\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@
|
||||||
#define JL_HAS_FLOOD_WALK_PLANES // amiga planar flood walk-out, function
|
#define JL_HAS_FLOOD_WALK_PLANES // amiga planar flood walk-out, function
|
||||||
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // amiga planar flood scan-row, function
|
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // amiga planar flood scan-row, function
|
||||||
#define JL_HAS_SURFACE_COPY_PLANES // amiga blitter plane copy, function
|
#define JL_HAS_SURFACE_COPY_PLANES // amiga blitter plane copy, function
|
||||||
|
#define JL_HAS_SURFACE_COPY_RECT // amiga per-plane rect row copy, function
|
||||||
#define JL_HAS_SAMPLE_PIXEL // amiga planar nibble decode, function
|
#define JL_HAS_SAMPLE_PIXEL // amiga planar nibble decode, function
|
||||||
#define JL_HAS_SURFACE_HASH // amiga planar hash, function
|
#define JL_HAS_SURFACE_HASH // amiga planar hash, function
|
||||||
#define JL_HAS_SURFACE_LOAD_FILE // amiga planar load, function
|
#define JL_HAS_SURFACE_LOAD_FILE // amiga planar load, function
|
||||||
|
|
@ -206,6 +207,7 @@
|
||||||
#define JL_HAS_FLOOD_WALK_PLANES // st planar flood walk-out, function
|
#define JL_HAS_FLOOD_WALK_PLANES // st planar flood walk-out, function
|
||||||
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // st planar flood scan-row, function
|
#define JL_HAS_FLOOD_SCAN_ROW_PLANES // st planar flood scan-row, function
|
||||||
#define JL_HAS_SURFACE_COPY_PLANES // st planar buffer memcpy, function
|
#define JL_HAS_SURFACE_COPY_PLANES // st planar buffer memcpy, function
|
||||||
|
#define JL_HAS_SURFACE_COPY_RECT // st interleaved rect row memcpy, function
|
||||||
#define JL_HAS_SAMPLE_PIXEL // st planar nibble decode, function
|
#define JL_HAS_SAMPLE_PIXEL // st planar nibble decode, function
|
||||||
#define JL_HAS_SURFACE_HASH // st planar hash, function
|
#define JL_HAS_SURFACE_HASH // st planar hash, function
|
||||||
#define JL_HAS_SURFACE_LOAD_FILE // st planar load, function
|
#define JL_HAS_SURFACE_LOAD_FILE // st planar load, function
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,23 @@ jlSurfaceT *jlStageGet(void);
|
||||||
// surfaces.
|
// surfaces.
|
||||||
void jlSurfaceCopy(jlSurfaceT *dst, const jlSurfaceT *src);
|
void jlSurfaceCopy(jlSurfaceT *dst, const jlSurfaceT *src);
|
||||||
|
|
||||||
|
// Copy a pixel rectangle from src into dst at the SAME position (no
|
||||||
|
// repositioning, so planar ports never shift bits). The rect is
|
||||||
|
// clipped to the surface, then widened OUTWARD to the 16-pixel group
|
||||||
|
// grid ON EVERY PORT -- the copied window is x rounded down and
|
||||||
|
// x + w rounded up to multiples of 16 (same window rule as
|
||||||
|
// jlSpriteSaveUnder), so up to 15 extra pixels on each side are
|
||||||
|
// copied. Two consequences the caller owns: a snapped erase can
|
||||||
|
// clobber on-screen content up to 15 px outside the requested rect,
|
||||||
|
// and it silently invalidates any live jlSpriteBackupT whose
|
||||||
|
// recorded window it touches. Copies pixels only: SCBs and palettes
|
||||||
|
// are untouched. The intended use is restoring background
|
||||||
|
// rectangles from a pristine copy of the scene (the clean-buffer
|
||||||
|
// pattern) instead of per-sprite save/restore -- erase ALL windows,
|
||||||
|
// then redraw ALL sprites; do not mix with backup-restore in the
|
||||||
|
// same region.
|
||||||
|
void jlSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, int16_t x, int16_t y, uint16_t w, uint16_t h);
|
||||||
|
|
||||||
// Persist a surface to disk in target-native form: 32000 bytes of
|
// Persist a surface to disk in target-native form: 32000 bytes of
|
||||||
// pixels followed by 200 bytes of SCB followed by 512 bytes of $0RGB
|
// pixels followed by 200 bytes of SCB followed by 512 bytes of $0RGB
|
||||||
// palette table (16 palettes x 16 colors, native uint16_t). Returns
|
// palette table (16 palettes x 16 colors, native uint16_t). Returns
|
||||||
|
|
|
||||||
|
|
@ -132,4 +132,23 @@ void jlDrawText(jlSurfaceT *dst, uint8_t bx, uint8_t by,
|
||||||
const jlSurfaceT *fontSurface, const uint16_t *asciiMap,
|
const jlSurfaceT *fontSurface, const uint16_t *asciiMap,
|
||||||
const char *str);
|
const char *str);
|
||||||
|
|
||||||
|
// Map entry value that leaves a cell untouched (nothing pasted, the
|
||||||
|
// destination shows through). Mirrors TILE_NO_GLYPH's role in
|
||||||
|
// jlDrawText.
|
||||||
|
#define TILE_MAP_SKIP ((uint8_t)0xFFu)
|
||||||
|
|
||||||
|
// Paste a wTiles x hTiles rectangle of tiles in ONE call: the
|
||||||
|
// validation, dispatch, and dirty union are paid once for the whole
|
||||||
|
// map instead of per tile (the jlDrawText batching pattern
|
||||||
|
// generalized, NATIVE-PERF Phase 3). `tiles` is the tileset; `map`
|
||||||
|
// holds wTiles * hTiles tileset indices in row-major order
|
||||||
|
// (map[row * wTiles + col]). An index of TILE_MAP_SKIP leaves that
|
||||||
|
// cell untouched; any other out-of-tileset index is the CALLER'S
|
||||||
|
// contract to avoid (the batch does not know the tileset length and
|
||||||
|
// does not range-check it). The whole rectangle must fit on the
|
||||||
|
// surface -- a map whose edge crosses TILE_BLOCKS_PER_ROW/COL is
|
||||||
|
// rejected as a no-op, matching the per-tile ops' silent-reject
|
||||||
|
// convention. wTiles or hTiles of 0 is a no-op.
|
||||||
|
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1454,6 +1454,45 @@ void jlpSurfaceCopyPlanes(jlSurfaceT *dst, const jlSurfaceT *src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Same-position group-snapped rect copy (NATIVE-PERF Phase 4
|
||||||
|
// clean-buffer op). The core wrapper clipped and 16-px-snapped the
|
||||||
|
// rect; per plane each row is a short contiguous span at the same
|
||||||
|
// byte offset in both buffers. CPU row copies, not the blitter:
|
||||||
|
// amigaBlitterCopyPlanes hardcodes modulo 0 (whole contiguous rows),
|
||||||
|
// a 16-px snap gives only word alignment (CopyMemQuick needs longs),
|
||||||
|
// and typical spans (a 16x16 sprite window = 2 bytes/row/plane) are
|
||||||
|
// below any setup-cost crossover. A modulo-driven blitter variant is
|
||||||
|
// the production upgrade if this op ever moves big rects.
|
||||||
|
void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||||
|
AmigaPlanarT *dstPd;
|
||||||
|
AmigaPlanarT *srcPd;
|
||||||
|
uint8_t *d;
|
||||||
|
const uint8_t *s;
|
||||||
|
uint16_t off;
|
||||||
|
uint16_t rowBytes;
|
||||||
|
uint16_t row;
|
||||||
|
uint8_t plane;
|
||||||
|
|
||||||
|
dstPd = (AmigaPlanarT *)dst->portData;
|
||||||
|
srcPd = (AmigaPlanarT *)src->portData;
|
||||||
|
if (dstPd == NULL || srcPd == NULL) {
|
||||||
|
jlpGenericSurfaceCopyRect(dst, src, x, y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
off = (uint16_t)(y * AMIGA_BYTES_PER_ROW + (x >> 3));
|
||||||
|
rowBytes = (uint16_t)(w >> 3);
|
||||||
|
for (plane = 0; plane < 4u; plane++) {
|
||||||
|
d = dstPd->planes[plane] + off;
|
||||||
|
s = srcPd->planes[plane] + off;
|
||||||
|
for (row = 0; row < h; row++) {
|
||||||
|
memcpy(d, s, rowBytes);
|
||||||
|
d += AMIGA_BYTES_PER_ROW;
|
||||||
|
s += AMIGA_BYTES_PER_ROW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Phase 4 planar dual-write helper: set one pixel's bit in each of
|
// Phase 4 planar dual-write helper: set one pixel's bit in each of
|
||||||
// the four shadow planes. Caller (the per-primitive walker below) has
|
// the four shadow planes. Caller (the per-primitive walker below) has
|
||||||
// already validated (x, y) is on-surface. byteOff is computed once
|
// already validated (x, y) is on-surface. byteOff is computed once
|
||||||
|
|
|
||||||
|
|
@ -1311,6 +1311,39 @@ void jlpSurfaceCopyPlanes(jlSurfaceT *dst, const jlSurfaceT *src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Same-position group-snapped rect copy (NATIVE-PERF Phase 4
|
||||||
|
// clean-buffer op). The core wrapper clipped and 16-px-snapped the
|
||||||
|
// rect, so each row's span is contiguous interleaved bytes at the
|
||||||
|
// same offset in both buffers -- the raw-span sprite-restore recipe
|
||||||
|
// with a strided source. Offsets are multiples of 8 from long-aligned
|
||||||
|
// bases, so memcpy's long path is alignment-safe.
|
||||||
|
void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||||
|
StPlanarT *dstPd;
|
||||||
|
StPlanarT *srcPd;
|
||||||
|
uint8_t *d;
|
||||||
|
const uint8_t *s;
|
||||||
|
uint16_t off;
|
||||||
|
uint16_t rowBytes;
|
||||||
|
uint16_t row;
|
||||||
|
|
||||||
|
dstPd = (StPlanarT *)dst->portData;
|
||||||
|
srcPd = (StPlanarT *)src->portData;
|
||||||
|
if (dstPd == NULL || srcPd == NULL) {
|
||||||
|
jlpGenericSurfaceCopyRect(dst, src, x, y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
off = (uint16_t)(y * ST_BYTES_PER_ROW + (x >> 4) * ST_BYTES_PER_GROUP);
|
||||||
|
d = dstPd->base + off;
|
||||||
|
s = srcPd->base + off;
|
||||||
|
rowBytes = (uint16_t)((w >> 4) * ST_BYTES_PER_GROUP);
|
||||||
|
for (row = 0; row < h; row++) {
|
||||||
|
memcpy(d, s, rowBytes);
|
||||||
|
d += ST_BYTES_PER_ROW;
|
||||||
|
s += ST_BYTES_PER_ROW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----- Phases 4-7: per-pixel / tile / sprite / blit planar primitives -----
|
// ----- Phases 4-7: per-pixel / tile / sprite / blit planar primitives -----
|
||||||
//
|
//
|
||||||
// These implementations dual-write the planar shadow alongside the
|
// These implementations dual-write the planar shadow alongside the
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,13 @@
|
||||||
// all 4 planes. Declared here because stTileFill is its only caller.
|
// all 4 planes. Declared here because stTileFill is its only caller.
|
||||||
extern void surface68kStTileFill8x8(uint8_t *firstGroupPtr, uint16_t mask, uint8_t color);
|
extern void surface68kStTileFill8x8(uint8_t *firstGroupPtr, uint16_t mask, uint8_t color);
|
||||||
|
|
||||||
|
// 68k asm entry (tileMap.s): whole-map paste with a movep.l row body
|
||||||
|
// (36 cyc/tile-row vs ~80 for compiled move.b). Requires an EVEN
|
||||||
|
// tileset pointer (move.l source alignment); stTileMapPaste guards
|
||||||
|
// and keeps the C pair walker as the odd-pointer fallback. Declared
|
||||||
|
// here because stTileMapPaste is its only caller.
|
||||||
|
extern void stTileMapPasteMovep(uint8_t *rowBase, const uint8_t *tiles, const uint8_t *map, uint16_t wTiles, uint16_t hTiles, uint16_t headOdd);
|
||||||
|
|
||||||
// Phase 5 tile ops. 8x8 tiles at byte position (bx, by) start at
|
// Phase 5 tile ops. 8x8 tiles at byte position (bx, by) start at
|
||||||
// pixel (bx*8, by*8). 8 pixels wide always covers exactly half a
|
// pixel (bx*8, by*8). 8 pixels wide always covers exactly half a
|
||||||
// 16-pixel group: high half (bits 15..8) when bx is even, low half
|
// 16-pixel group: high half (bits 15..8) when bx is even, low half
|
||||||
|
|
@ -93,6 +100,104 @@ static inline __attribute__((always_inline)) void stTilePaste(jlSurfaceT *dst, u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Whole-map paste engine (NATIVE-PERF Phase 4). jlTileMapPaste's ST
|
||||||
|
// arm calls this ONCE per map: the portData check, the row base
|
||||||
|
// derivation, and the group walk are hoisted out of the per-cell
|
||||||
|
// loop -- the generic loop re-derives base + by*1280 + (bx>>1)*8 +
|
||||||
|
// (bx&1) and reloads portData per cell, ~250 of its ~980 cyc/tile.
|
||||||
|
// Cells advance in even/odd PAIRS sharing one group pointer (+8 per
|
||||||
|
// pair, constant displacements select the half-group), so an odd bx
|
||||||
|
// contributes a head cell and an odd width a tail cell. Pixel output
|
||||||
|
// is byte-identical to per-cell stTilePaste.
|
||||||
|
#define ST_MAP_CELL_ROW(_o, _r) \
|
||||||
|
do { \
|
||||||
|
gp[(_o) + (_r) * ST_BYTES_PER_ROW + 0] = src[(_r) * TILE_BYTES_PER_ROW + 0]; \
|
||||||
|
gp[(_o) + (_r) * ST_BYTES_PER_ROW + 2] = src[(_r) * TILE_BYTES_PER_ROW + 1]; \
|
||||||
|
gp[(_o) + (_r) * ST_BYTES_PER_ROW + 4] = src[(_r) * TILE_BYTES_PER_ROW + 2]; \
|
||||||
|
gp[(_o) + (_r) * ST_BYTES_PER_ROW + 6] = src[(_r) * TILE_BYTES_PER_ROW + 3]; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ST_MAP_CELL(_o) \
|
||||||
|
do { \
|
||||||
|
src = tileBytes + ((uint16_t)idx << 5); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 0); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 1); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 2); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 3); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 4); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 5); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 6); \
|
||||||
|
ST_MAP_CELL_ROW(_o, 7); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static inline __attribute__((always_inline)) void stTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map) {
|
||||||
|
StPlanarT *pd;
|
||||||
|
const uint8_t *tileBytes;
|
||||||
|
const uint8_t *src;
|
||||||
|
uint8_t *rowBase;
|
||||||
|
uint8_t *gp;
|
||||||
|
uint8_t idx;
|
||||||
|
uint8_t ty;
|
||||||
|
uint8_t tx;
|
||||||
|
uint8_t pairs;
|
||||||
|
uint8_t headOdd;
|
||||||
|
uint8_t tail;
|
||||||
|
|
||||||
|
tileBytes = (const uint8_t *)tiles;
|
||||||
|
pd = (StPlanarT *)dst->portData;
|
||||||
|
if (pd == NULL) {
|
||||||
|
for (ty = 0; ty < hTiles; ty++) {
|
||||||
|
for (tx = 0; tx < wTiles; tx++) {
|
||||||
|
idx = map[tx];
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
jlpGenericTilePaste(dst, (uint8_t)(bx + tx), (uint8_t)(by + ty), tileBytes + ((uint16_t)idx << 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map += wTiles;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rowBase = pd->base + (uint16_t)by * 8u * ST_BYTES_PER_ROW + ((uint16_t)bx >> 1) * ST_BYTES_PER_GROUP;
|
||||||
|
headOdd = (uint8_t)(bx & 1u);
|
||||||
|
if (((uintptr_t)tileBytes & 1u) == 0u) {
|
||||||
|
stTileMapPasteMovep(rowBase, tileBytes, map, wTiles, hTiles, headOdd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pairs = (uint8_t)((uint8_t)(wTiles - headOdd) >> 1);
|
||||||
|
tail = (uint8_t)((uint8_t)(wTiles - headOdd) & 1u);
|
||||||
|
for (ty = 0; ty < hTiles; ty++) {
|
||||||
|
gp = rowBase;
|
||||||
|
if (headOdd != 0u) {
|
||||||
|
idx = *map++;
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
ST_MAP_CELL(1);
|
||||||
|
}
|
||||||
|
gp += ST_BYTES_PER_GROUP;
|
||||||
|
}
|
||||||
|
for (tx = pairs; tx != 0u; tx--) {
|
||||||
|
idx = *map++;
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
ST_MAP_CELL(0);
|
||||||
|
}
|
||||||
|
idx = *map++;
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
ST_MAP_CELL(1);
|
||||||
|
}
|
||||||
|
gp += ST_BYTES_PER_GROUP;
|
||||||
|
}
|
||||||
|
if (tail != 0u) {
|
||||||
|
idx = *map++;
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
ST_MAP_CELL(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rowBase += 8u * ST_BYTES_PER_ROW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef ST_MAP_CELL
|
||||||
|
#undef ST_MAP_CELL_ROW
|
||||||
|
|
||||||
|
|
||||||
// Planar monochrome paste. monoTile follows the cross-port mono
|
// Planar monochrome paste. monoTile follows the cross-port mono
|
||||||
// contract (include/joey/tile.h + jlpGenericTilePasteMono): 32 chunky
|
// contract (include/joey/tile.h + jlpGenericTilePasteMono): 32 chunky
|
||||||
// nibble-pair bytes, row-major, TILE_BYTES_PER_ROW bytes per row, two
|
// nibble-pair bytes, row-major, TILE_BYTES_PER_ROW bytes per row, two
|
||||||
|
|
|
||||||
127
src/atarist/tileMap.s
Normal file
127
src/atarist/tileMap.s
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
| ST whole-map tile-paste engine, movep.l row body (NATIVE-PERF
|
||||||
|
| Phase 4).
|
||||||
|
|
|
||||||
|
| One call pastes a wTiles x hTiles map of 8x8 tiles. The copy body
|
||||||
|
| uses the 68000's byte-lane scatter instruction:
|
||||||
|
|
|
||||||
|
| move.l (%a0)+,%d0 | one tile row: 4 plane bytes, 12 cyc
|
||||||
|
| movep.l %d0,d16(%a1) | writes d16, d16+2, d16+4, d16+6, 24 cyc
|
||||||
|
|
|
||||||
|
| = 36 cyc per tile row vs ~80 for the four move.b the compiler emits
|
||||||
|
| (movep writes MSB first, matching the plane-major tile layout and
|
||||||
|
| the interleaved d16 0/2/4/6 dst pattern of stTilePaste). movem is
|
||||||
|
| useless here -- the dst is a byte scatter -- but movep IS this
|
||||||
|
| access pattern. Cells advance in even/odd pairs sharing one group
|
||||||
|
| pointer (+8/pair); an odd bx adds a head cell (displacements +1),
|
||||||
|
| an odd width a tail cell. Row displacements are compile-time
|
||||||
|
| constants (row r at r*160), so the group pointer only moves once
|
||||||
|
| per pair.
|
||||||
|
|
|
||||||
|
| void stTileMapPasteMovep(uint8_t *rowBase, const uint8_t *tiles,
|
||||||
|
| const uint8_t *map, uint16_t wTiles,
|
||||||
|
| uint16_t hTiles, uint16_t headOdd);
|
||||||
|
|
|
||||||
|
| rowBase = planar base + by*1280 + (bx>>1)*8 (group-aligned; the
|
||||||
|
| caller keeps the bx&1 head flag). CONTRACT: tiles must be EVEN
|
||||||
|
| (move.l source alignment on 68000); the C caller guards and falls
|
||||||
|
| back to the byte-copy engine for odd tileset pointers. Map bytes of
|
||||||
|
| 0xFF (TILE_MAP_SKIP) leave the cell untouched but still advance the
|
||||||
|
| walk. wTiles/hTiles >= 1 (jlTileMapPaste validated).
|
||||||
|
|
|
||||||
|
| cdecl args land in 4-byte slots (uint16_t value at slot+2), same
|
||||||
|
| convention as surface68kStTileFill8x8 (lineSpan.s). d2-d7/a2-a6 are
|
||||||
|
| callee-save; register lists (not hand masks) keep the predec movem
|
||||||
|
| mask correct.
|
||||||
|
|
|
||||||
|
| Register map: a0 tile src (scratch), a1 group walker, a2 map
|
||||||
|
| cursor, a3 tileset base, a4 rowBase (+1280/map row); d0 movep data,
|
||||||
|
| d1 map byte, d2 pairs/row, d3 tail flag, d4 rows left, d5 head
|
||||||
|
| flag, d6 pair countdown.
|
||||||
|
|
|
||||||
|
| GAS m68k syntax, assembled by m68k-atari-mint-as. ST-only: this
|
||||||
|
| file must stay in src/atarist/ (src/m68k/*.s is wildcarded into the
|
||||||
|
| Amiga build too).
|
||||||
|
|
||||||
|
.equ MP_SAVED, 32 | d2-d6/a2-a4 = 8 longs
|
||||||
|
.equ MP_OFF, (MP_SAVED + 4)
|
||||||
|
.equ MP_BASE, MP_OFF + 0
|
||||||
|
.equ MP_TILES, MP_OFF + 4
|
||||||
|
.equ MP_MAP, MP_OFF + 8
|
||||||
|
.equ MP_WTILES, MP_OFF + 12 + 2
|
||||||
|
.equ MP_HTILES, MP_OFF + 16 + 2
|
||||||
|
.equ MP_HEAD, MP_OFF + 20 + 2
|
||||||
|
|
||||||
|
| One tile cell: derive src from the map byte in d1, then 8 rows of
|
||||||
|
| move.l + movep.l at half-group displacement \off (0 = even cell,
|
||||||
|
| 1 = odd cell). Falls straight through on TILE_MAP_SKIP.
|
||||||
|
.macro MP_CELL off, skip
|
||||||
|
moveq #0,%d1
|
||||||
|
move.b (%a2)+,%d1 | map byte
|
||||||
|
cmp.b #-1,%d1 | TILE_MAP_SKIP?
|
||||||
|
beq.s \skip
|
||||||
|
lsl.w #5,%d1 | idx * sizeof(jlTileT)
|
||||||
|
lea (%a3,%d1.w),%a0 | tile src (even by contract)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+0(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+160(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+320(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+480(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+640(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+800(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+960(%a1)
|
||||||
|
move.l (%a0)+,%d0
|
||||||
|
movep.l %d0,\off+1120(%a1)
|
||||||
|
\skip:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
.globl _stTileMapPasteMovep
|
||||||
|
|
||||||
|
_stTileMapPasteMovep:
|
||||||
|
movem.l %d2-%d6/%a2-%a4,-(%sp)
|
||||||
|
|
||||||
|
movea.l MP_BASE(%sp),%a4 | a4 = rowBase
|
||||||
|
movea.l MP_TILES(%sp),%a3 | a3 = tileset base
|
||||||
|
movea.l MP_MAP(%sp),%a2 | a2 = map cursor
|
||||||
|
move.w MP_WTILES(%sp),%d3
|
||||||
|
move.w MP_HTILES(%sp),%d4 | d4 = rows left
|
||||||
|
move.w MP_HEAD(%sp),%d5 | d5 = 1 if bx odd
|
||||||
|
sub.w %d5,%d3
|
||||||
|
move.w %d3,%d2
|
||||||
|
lsr.w #1,%d2 | d2 = pairs per row
|
||||||
|
and.w #1,%d3 | d3 = tail cell flag
|
||||||
|
|
||||||
|
.LmpRow:
|
||||||
|
movea.l %a4,%a1 | a1 = this row's group ptr
|
||||||
|
|
||||||
|
tst.w %d5
|
||||||
|
beq.s .LmpNoHead
|
||||||
|
MP_CELL 1, .LmpHeadSkip
|
||||||
|
addq.l #8,%a1
|
||||||
|
.LmpNoHead:
|
||||||
|
move.w %d2,%d6 | pair countdown
|
||||||
|
beq.w .LmpNoPairs
|
||||||
|
subq.w #1,%d6
|
||||||
|
.LmpPair:
|
||||||
|
MP_CELL 0, .LmpEvenSkip
|
||||||
|
MP_CELL 1, .LmpOddSkip
|
||||||
|
addq.l #8,%a1
|
||||||
|
dbra %d6,.LmpPair
|
||||||
|
.LmpNoPairs:
|
||||||
|
tst.w %d3
|
||||||
|
beq.s .LmpNoTail
|
||||||
|
MP_CELL 0, .LmpTailSkip
|
||||||
|
.LmpNoTail:
|
||||||
|
lea 1280(%a4),%a4 | next tile row of the map
|
||||||
|
subq.w #1,%d4
|
||||||
|
bne.w .LmpRow
|
||||||
|
|
||||||
|
movem.l (%sp)+,%d2-%d6/%a2-%a4
|
||||||
|
rts
|
||||||
|
|
@ -67,6 +67,7 @@ extern void iigsTileCopyMark (const uint8_t *srcRow0, uint16_t dstBx, uint16
|
||||||
extern void iigsTileCopyMaskedInner(uint8_t *dstRow0, const uint8_t *srcRow0, uint16_t transparent);
|
extern void iigsTileCopyMaskedInner(uint8_t *dstRow0, const uint8_t *srcRow0, uint16_t transparent);
|
||||||
extern void iigsTilePasteInner (uint8_t *dstRow0, const uint8_t *srcTilePixels);
|
extern void iigsTilePasteInner (uint8_t *dstRow0, const uint8_t *srcTilePixels);
|
||||||
extern void iigsTilePasteMark (const uint8_t *srcTilePixels, uint16_t bx, uint16_t by);
|
extern void iigsTilePasteMark (const uint8_t *srcTilePixels, uint16_t bx, uint16_t by);
|
||||||
|
extern void iigsTileMapPasteMark (uint16_t bx, uint16_t by, uint16_t wTiles, uint16_t hTiles, const uint8_t *tiles, const uint8_t *map);
|
||||||
extern void iigsTilePasteMonoInner(uint8_t *dstRow0, const uint8_t *monoTile, uint16_t fgColor, uint16_t bgColor);
|
extern void iigsTilePasteMonoInner(uint8_t *dstRow0, const uint8_t *monoTile, uint16_t fgColor, uint16_t bgColor);
|
||||||
extern void iigsTileSnapInner (uint8_t *dstTilePixels, const uint8_t *srcRow0);
|
extern void iigsTileSnapInner (uint8_t *dstTilePixels, const uint8_t *srcRow0);
|
||||||
extern void iigsFillRectInner (uint8_t *pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t nibble);
|
extern void iigsFillRectInner (uint8_t *pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t nibble);
|
||||||
|
|
@ -542,6 +543,19 @@ void jlpGenericSurfaceCopyChunky(jlSurfaceT *dst, const jlSurfaceT *src);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Same-position group-snapped rect copy (jlSurfaceCopyRect payload).
|
||||||
|
// The generic body serves the chunky ports (incl. the IIgs stage --
|
||||||
|
// its pixels pointer reaches bank $01 through ordinary large-model
|
||||||
|
// stores); the planar ports override with their plane-layout walks.
|
||||||
|
void jlpGenericSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||||
|
#if !defined(jlpSurfaceCopyRect)
|
||||||
|
#if defined(JL_HAS_SURFACE_COPY_RECT)
|
||||||
|
void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||||
|
#else
|
||||||
|
#define jlpSurfaceCopyRect(_d, _s, _x, _y, _w, _h) jlpGenericSurfaceCopyRect((_d), (_s), (_x), (_y), (_w), (_h))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
void jlpGenericSurfaceFreePixels(uint8_t *pixels);
|
void jlpGenericSurfaceFreePixels(uint8_t *pixels);
|
||||||
#if !defined(jlpSurfaceFreePixels)
|
#if !defined(jlpSurfaceFreePixels)
|
||||||
#if defined(JL_HAS_SURFACE_FREE_PIXELS)
|
#if defined(JL_HAS_SURFACE_FREE_PIXELS)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,49 @@ void jlSurfaceCopy(jlSurfaceT *dst, const jlSurfaceT *src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, int16_t x, int16_t y, uint16_t w, uint16_t h) {
|
||||||
|
int32_t left;
|
||||||
|
int32_t top;
|
||||||
|
int32_t right;
|
||||||
|
int32_t bottom;
|
||||||
|
|
||||||
|
if (dst == NULL || src == NULL || dst == src) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (w == 0u || h == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Clip in int32 (jlFillRect precedent), then widen OUTWARD to the
|
||||||
|
// 16-px group grid per the header contract. SURFACE_WIDTH is a
|
||||||
|
// multiple of 16, so the widened right edge never re-crosses it.
|
||||||
|
left = x;
|
||||||
|
top = y;
|
||||||
|
right = (int32_t)x + w;
|
||||||
|
bottom = (int32_t)y + h;
|
||||||
|
if (left < 0) {
|
||||||
|
left = 0;
|
||||||
|
}
|
||||||
|
if (top < 0) {
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
|
if (right > SURFACE_WIDTH) {
|
||||||
|
right = SURFACE_WIDTH;
|
||||||
|
}
|
||||||
|
if (bottom > SURFACE_HEIGHT) {
|
||||||
|
bottom = SURFACE_HEIGHT;
|
||||||
|
}
|
||||||
|
if (left >= right || top >= bottom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
left = left & ~(int32_t)15;
|
||||||
|
right = (right + 15) & ~(int32_t)15;
|
||||||
|
jlpSurfaceCopyRect(dst, src, (uint16_t)left, (uint16_t)top, (uint16_t)(right - left), (uint16_t)(bottom - top));
|
||||||
|
// Pixels-only op: the SCB/palette dirty flags are jlSurfaceCopy's
|
||||||
|
// concern, not this one's.
|
||||||
|
surfaceMarkDirtyRect(dst, (int16_t)left, (int16_t)top, (int16_t)(right - left), (int16_t)(bottom - top));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
jlSurfaceT *jlSurfaceCreate(void) {
|
jlSurfaceT *jlSurfaceCreate(void) {
|
||||||
jlSurfaceT *s;
|
jlSurfaceT *s;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@
|
||||||
// the tile side or the 4-px-per-word grain ever changes):
|
// the tile side or the 4-px-per-word grain ever changes):
|
||||||
typedef char tileMarkBandProof[(SURFACE_WORD_INDEX(TILE_PIXELS_PER_SIDE) == 2) ? 1 : -1];
|
typedef char tileMarkBandProof[(SURFACE_WORD_INDEX(TILE_PIXELS_PER_SIDE) == 2) ? 1 : -1];
|
||||||
|
|
||||||
|
// jlTileMapPaste indexes its tileset as base + (idx << 5); pin the
|
||||||
|
// struct size so that shift can never drift from sizeof(jlTileT).
|
||||||
|
typedef char tileMapStrideProof[(sizeof(jlTileT) == 32) ? 1 : -1];
|
||||||
|
|
||||||
#ifdef JOEYLIB_PLATFORM_IIGS
|
#ifdef JOEYLIB_PLATFORM_IIGS
|
||||||
#define tileMarkDirty(_pixelY, _bx) iigsMarkDirtyRowsInner((uint16_t)(_pixelY), (uint16_t)((_pixelY) + TILE_PIXELS_PER_SIDE), (uint16_t)((uint16_t)(_bx) << 1), (uint16_t)(((uint16_t)(_bx) << 1) + 1u))
|
#define tileMarkDirty(_pixelY, _bx) iigsMarkDirtyRowsInner((uint16_t)(_pixelY), (uint16_t)((_pixelY) + TILE_PIXELS_PER_SIDE), (uint16_t)((uint16_t)(_bx) << 1), (uint16_t)(((uint16_t)(_bx) << 1) + 1u))
|
||||||
// Fill/copy/paste FUSE the mark into their stage-arm asm entries on
|
// Fill/copy/paste FUSE the mark into their stage-arm asm entries on
|
||||||
|
|
@ -242,6 +246,87 @@ void jlTileFill(jlSurfaceT *s, uint8_t bx, uint8_t by, uint8_t colorIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map) {
|
||||||
|
uint8_t tx;
|
||||||
|
uint8_t ty;
|
||||||
|
uint8_t idx;
|
||||||
|
const uint8_t *row;
|
||||||
|
|
||||||
|
if (dst == NULL || tiles == NULL || map == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (wTiles == 0u || hTiles == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((uint16_t)bx + wTiles > TILE_BLOCKS_PER_ROW || (uint16_t)by + hTiles > TILE_BLOCKS_PER_COL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// One validation above, one dirty union below: the per-cell loop
|
||||||
|
// pays only the jlp dispatch (inline on the 68k ports since Phase
|
||||||
|
// 2 R4a; the fused stage entry on IIgs). Tileset indexing is a
|
||||||
|
// 16-bit shift folded into the far base ONCE per cell -- the
|
||||||
|
// natural tiles[idx] spelling made the 65816 do a 32-bit indexed
|
||||||
|
// multiply per cell, which alone erased the batch win there.
|
||||||
|
row = map;
|
||||||
|
#ifdef JOEYLIB_PLATFORM_IIGS
|
||||||
|
// The stage test is hoisted OUT of the loop on the IIgs: the
|
||||||
|
// jlpTilePaste macro's per-cell (dst == gStage) ternary
|
||||||
|
// materializes a boolean (~65 cycles/cell) that a 10x5 map pays
|
||||||
|
// 50 times. The fused stage entry marks each pasted tile itself,
|
||||||
|
// so the stage arm needs no union; non-stage surfaces carry no
|
||||||
|
// dirty state.
|
||||||
|
if (dst == gStage) {
|
||||||
|
// ONE JSL for the whole map (NATIVE-PERF Phase 4): the asm
|
||||||
|
// walker amortizes the per-tile JSL + arg staging + derive +
|
||||||
|
// 8-row mark across the rectangle and issues one band union.
|
||||||
|
iigsTileMapPasteMark(bx, by, wTiles, hTiles, (const uint8_t *)tiles, map);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ty = 0; ty < hTiles; ty++) {
|
||||||
|
for (tx = 0; tx < wTiles; tx++) {
|
||||||
|
idx = row[tx];
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
iigsTilePasteInner(&dst->pixels[SURFACE_ROW_OFFSET((uint16_t)(by + ty) << 3) + ((uint16_t)(bx + tx) << 2)], (const uint8_t *)tiles + ((uint16_t)idx << 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row += wTiles;
|
||||||
|
}
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_ATARIST)
|
||||||
|
// Whole-map engine (NATIVE-PERF Phase 4, stTile.h): one portData
|
||||||
|
// check, one row-base derive, and a paired group-pointer walk for
|
||||||
|
// the whole rectangle instead of the per-cell re-derivation the
|
||||||
|
// generic loop pays.
|
||||||
|
(void)tx;
|
||||||
|
(void)ty;
|
||||||
|
(void)idx;
|
||||||
|
(void)row;
|
||||||
|
stTileMapPaste(dst, bx, by, wTiles, hTiles, tiles, map);
|
||||||
|
// One rectangle-union mark for the whole map. Over-marks
|
||||||
|
// TILE_MAP_SKIP cells (safe: present copies extra CLEAN content,
|
||||||
|
// pixels unchanged).
|
||||||
|
if (dst == gStage) {
|
||||||
|
surfaceMarkDirtyRows((uint16_t)((uint16_t)by << 3), (uint16_t)(((uint16_t)by + hTiles) << 3), (uint8_t)((uint16_t)bx << 1), (uint8_t)((((uint16_t)bx + wTiles) << 1) - 1u));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (ty = 0; ty < hTiles; ty++) {
|
||||||
|
for (tx = 0; tx < wTiles; tx++) {
|
||||||
|
idx = row[tx];
|
||||||
|
if (idx != TILE_MAP_SKIP) {
|
||||||
|
jlpTilePaste(dst, (uint8_t)(bx + tx), (uint8_t)(by + ty), (const uint8_t *)tiles + ((uint16_t)idx << 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row += wTiles;
|
||||||
|
}
|
||||||
|
// One rectangle-union mark for the whole map. Over-marks
|
||||||
|
// TILE_MAP_SKIP cells (safe: present copies extra CLEAN content,
|
||||||
|
// pixels unchanged).
|
||||||
|
if (dst == gStage) {
|
||||||
|
surfaceMarkDirtyRows((uint16_t)((uint16_t)by << 3), (uint16_t)(((uint16_t)by + hTiles) << 3), (uint8_t)((uint16_t)bx << 1), (uint8_t)((((uint16_t)bx + wTiles) << 1) - 1u));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const jlTileT *in) {
|
void jlTilePaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, const jlTileT *in) {
|
||||||
if (dst == NULL || in == NULL) {
|
if (dst == NULL || in == NULL) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,33 @@ void jlpGenericSurfaceCopyChunky(jlSurfaceT *dst, const jlSurfaceT *src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// jlSurfaceCopyRect's chunky payload: same-position row segments. The
|
||||||
|
// core wrapper already clipped and group-snapped the rect, so x and w
|
||||||
|
// are multiples of 16 and the rect is on-surface. Guarded to a no-op
|
||||||
|
// on planar ports (pixels == NULL), whose JL_HAS_SURFACE_COPY_RECT
|
||||||
|
// override owns the plane work. The row offset is seeded once and
|
||||||
|
// walked by stride: SURFACE_ROW_OFFSET is a LUT read on the IIgs and
|
||||||
|
// a multiply elsewhere, and neither belongs in a row loop.
|
||||||
|
void jlpGenericSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||||
|
uint8_t *d;
|
||||||
|
const uint8_t *s;
|
||||||
|
uint16_t rowBytes;
|
||||||
|
uint16_t row;
|
||||||
|
|
||||||
|
if (dst->pixels == NULL || src->pixels == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d = dst->pixels + SURFACE_ROW_OFFSET(y) + (x >> 1);
|
||||||
|
s = src->pixels + SURFACE_ROW_OFFSET(y) + (x >> 1);
|
||||||
|
rowBytes = (uint16_t)(w >> 1);
|
||||||
|
for (row = 0; row < h; row++) {
|
||||||
|
memcpy(d, s, rowBytes);
|
||||||
|
d += SURFACE_BYTES_PER_ROW;
|
||||||
|
s += SURFACE_BYTES_PER_ROW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Release a per-surface chunky pixel buffer. Must mirror
|
// Release a per-surface chunky pixel buffer. Must mirror
|
||||||
// jlpGenericSurfaceAllocPixels' allocator exactly: handing a
|
// jlpGenericSurfaceAllocPixels' allocator exactly: handing a
|
||||||
// jlpBigAlloc pointer to free() corrupts the IIgs heap
|
// jlpBigAlloc pointer to free() corrupts the IIgs heap
|
||||||
|
|
|
||||||
|
|
@ -1731,6 +1731,250 @@ iigsTileCopyMark:
|
||||||
plp
|
plp
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
|
; ====================================================================
|
||||||
|
; iigsTileMapPasteMark(uint16_t bx, uint16_t by, uint16_t wTiles,
|
||||||
|
; uint16_t hTiles, const jlTileT *tiles,
|
||||||
|
; const uint8_t *map)
|
||||||
|
;
|
||||||
|
; NATIVE-PERF Phase 4: whole-map tile walker. ONE JSL walks the
|
||||||
|
; wTiles x hTiles row-major uint8_t index map and pastes every
|
||||||
|
; non-0xFF tile to the stage, then widens the dirty band once for the
|
||||||
|
; whole rectangle -- amortizing the per-tile JSL, arg staging, derive,
|
||||||
|
; and 8-row mark that iigsTilePasteMark pays per tile. The dst offset
|
||||||
|
; advances incrementally (+4 per tile column, +1280 per tile row =
|
||||||
|
; 8 SHR rows) from ONE gRowOffsetLut read at entry.
|
||||||
|
;
|
||||||
|
; ABI: bx in A; by @4,s, wTiles @6,s, hTiles @8,s, tiles @10,s/12,s,
|
||||||
|
; map @14,s/16,s on JSL entry. Frame: php+phb+phd (+4), parked
|
||||||
|
; bx (+2), then a 22-byte reserved D-frame (+22) = +28 total; D
|
||||||
|
; = SP+1 so frame slots are D-relative and args sit at D+31..43.
|
||||||
|
; X survives the 16-pair copy body (sta long,x never writes X),
|
||||||
|
; so it carries the walking dst offset; Y is clobbered per cell
|
||||||
|
; and the map index lives in the frame instead. The idx << 5
|
||||||
|
; tileset add carries into the bank byte (tmwSrc+2), so a
|
||||||
|
; tileset crossing a bank boundary still resolves.
|
||||||
|
;
|
||||||
|
; No internal jsr (the band union is inline), so the section is
|
||||||
|
; self-contained -- no cross-section 16-bit-jsr hazard. The three
|
||||||
|
; wrapping branches (skip-cell forward, column back, row back) span
|
||||||
|
; the ~150-byte copy body and use the bne-over-brl idiom.
|
||||||
|
; ====================================================================
|
||||||
|
.section .text.iigsTileMapPasteMark,"ax"
|
||||||
|
.globl iigsTileMapPasteMark
|
||||||
|
iigsTileMapPasteMark:
|
||||||
|
tmwSrc = 0 ; current tile src far ptr ([0],y)
|
||||||
|
tmwMap = 4 ; map far ptr ([4],y)
|
||||||
|
tmwRowBase = 8 ; dst offset of current tile row
|
||||||
|
tmwTilesLo = 10 ; tileset base low16
|
||||||
|
tmwTilesHi = 12 ; tileset base high16 (bank)
|
||||||
|
tmwMapIdx = 14 ; running map index (reused: yEnd for the mark)
|
||||||
|
tmwTx = 16 ; columns left this row (reused: minWord)
|
||||||
|
tmwTy = 18 ; rows left (reused: maxWord)
|
||||||
|
tmwWt = 20 ; wTiles reload value
|
||||||
|
tmwBx = 22 ; parked bx
|
||||||
|
tmwByArg = 31 ; by (orig 4,s + 28 pushed, D-relative)
|
||||||
|
tmwWtArg = 33 ; wTiles (orig 6,s)
|
||||||
|
tmwHtArg = 35 ; hTiles (orig 8,s)
|
||||||
|
tmwTLoArg = 37 ; tiles low16 (orig 10,s)
|
||||||
|
tmwTHiArg = 39 ; tiles high16 (orig 12,s)
|
||||||
|
tmwMLoArg = 41 ; map low16 (orig 14,s)
|
||||||
|
tmwMHiArg = 43 ; map high16 (orig 16,s)
|
||||||
|
|
||||||
|
php
|
||||||
|
phb
|
||||||
|
phd
|
||||||
|
rep #0x30
|
||||||
|
.a16
|
||||||
|
.i16
|
||||||
|
pha ; park bx -> D+22
|
||||||
|
tsc
|
||||||
|
sec
|
||||||
|
sbc #22
|
||||||
|
tcs ; reserve the 22-byte frame
|
||||||
|
inc a
|
||||||
|
tcd ; D = frame base (bank-0 stack)
|
||||||
|
|
||||||
|
lda tmwMLoArg
|
||||||
|
sta tmwMap+0
|
||||||
|
lda tmwMHiArg
|
||||||
|
sta tmwMap+2
|
||||||
|
lda tmwTLoArg
|
||||||
|
sta tmwTilesLo
|
||||||
|
lda tmwTHiArg
|
||||||
|
sta tmwTilesHi
|
||||||
|
lda tmwWtArg
|
||||||
|
sta tmwWt
|
||||||
|
lda tmwHtArg
|
||||||
|
sta tmwTy
|
||||||
|
stz tmwMapIdx
|
||||||
|
; row-0 dst offset = gRowOffsetLut[by*8] + bx*4 (ONCE)
|
||||||
|
lda tmwByArg
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
lda tmwBx
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc gRowOffsetLut,x
|
||||||
|
sta tmwRowBase
|
||||||
|
|
||||||
|
tmwRowTop:
|
||||||
|
lda tmwWt
|
||||||
|
sta tmwTx
|
||||||
|
lda tmwRowBase
|
||||||
|
tax ; X = dst offset, walks +4/column
|
||||||
|
tmwColTop:
|
||||||
|
ldy tmwMapIdx
|
||||||
|
sep #0x20
|
||||||
|
.a8
|
||||||
|
lda [tmwMap], y ; map byte
|
||||||
|
cmp #0xFF
|
||||||
|
bne tmwDoTile
|
||||||
|
rep #0x20
|
||||||
|
.a16
|
||||||
|
brl tmwCellNext ; skip: past the copy body's range
|
||||||
|
tmwDoTile:
|
||||||
|
rep #0x20
|
||||||
|
.a16
|
||||||
|
and #0x00FF ; idx (high byte was stale)
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a ; idx * 32 (sizeof proof in tile.c)
|
||||||
|
clc
|
||||||
|
adc tmwTilesLo
|
||||||
|
sta tmwSrc+0
|
||||||
|
lda tmwTilesHi
|
||||||
|
adc #0 ; bank carry
|
||||||
|
sta tmwSrc+2
|
||||||
|
; 8 rows: src stride 4 via [0],y; dst stride 160 folded
|
||||||
|
; into the long,x literals (the iigsTilePasteMark body).
|
||||||
|
ldy #0
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+0,x
|
||||||
|
ldy #2
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+2,x
|
||||||
|
ldy #4
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+160,x
|
||||||
|
ldy #6
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+162,x
|
||||||
|
ldy #8
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+320,x
|
||||||
|
ldy #10
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+322,x
|
||||||
|
ldy #12
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+480,x
|
||||||
|
ldy #14
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+482,x
|
||||||
|
ldy #16
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+640,x
|
||||||
|
ldy #18
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+642,x
|
||||||
|
ldy #20
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+800,x
|
||||||
|
ldy #22
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+802,x
|
||||||
|
ldy #24
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+960,x
|
||||||
|
ldy #26
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+962,x
|
||||||
|
ldy #28
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+1120,x
|
||||||
|
ldy #30
|
||||||
|
lda [tmwSrc], y
|
||||||
|
sta 0x012000+1122,x
|
||||||
|
tmwCellNext:
|
||||||
|
inc tmwMapIdx ; 16-bit (M=16 on both arrival paths)
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
inx ; dst offset += 4 (one tile column)
|
||||||
|
dec tmwTx
|
||||||
|
beq tmwRowDone
|
||||||
|
brl tmwColTop
|
||||||
|
tmwRowDone:
|
||||||
|
lda tmwRowBase
|
||||||
|
clc
|
||||||
|
adc #1280 ; 8 SHR rows x 160 = one tile row
|
||||||
|
sta tmwRowBase
|
||||||
|
dec tmwTy
|
||||||
|
beq tmwMark
|
||||||
|
brl tmwRowTop
|
||||||
|
|
||||||
|
tmwMark:
|
||||||
|
; One band union for the whole rectangle (over-marks
|
||||||
|
; TILE_MAP_SKIP cells -- safe, present just re-uploads
|
||||||
|
; clean pixels). Frame slots are dead now; reuse them:
|
||||||
|
; tmwMapIdx = yEnd, tmwTx = minWord, tmwTy = maxWord.
|
||||||
|
lda tmwByArg
|
||||||
|
clc
|
||||||
|
adc tmwHtArg
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
sta tmwMapIdx ; yEnd = (by + hTiles) * 8
|
||||||
|
lda tmwBx
|
||||||
|
asl a
|
||||||
|
sta tmwTx ; minWord = bx * 2
|
||||||
|
lda tmwBx
|
||||||
|
clc
|
||||||
|
adc tmwWtArg
|
||||||
|
asl a
|
||||||
|
dec a
|
||||||
|
sta tmwTy ; maxWord = (bx + wTiles) * 2 - 1
|
||||||
|
lda tmwByArg
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
tax ; X = first row = by * 8
|
||||||
|
sep #0x20
|
||||||
|
.a8
|
||||||
|
tmwMarkLoop:
|
||||||
|
lda tmwTx
|
||||||
|
cmp gStageMinWord,x
|
||||||
|
bcs tmwNoMin
|
||||||
|
sta gStageMinWord,x ; widen down
|
||||||
|
tmwNoMin:
|
||||||
|
lda tmwTy
|
||||||
|
cmp gStageMaxWord,x
|
||||||
|
bcc tmwNoMax
|
||||||
|
beq tmwNoMax
|
||||||
|
sta gStageMaxWord,x ; widen up
|
||||||
|
tmwNoMax:
|
||||||
|
inx
|
||||||
|
cpx tmwMapIdx
|
||||||
|
bcc tmwMarkLoop
|
||||||
|
rep #0x30
|
||||||
|
.a16
|
||||||
|
.i16
|
||||||
|
tsc
|
||||||
|
clc
|
||||||
|
adc #24 ; drop frame (22) + parked bx (2)
|
||||||
|
tcs
|
||||||
|
pld
|
||||||
|
plb
|
||||||
|
plp
|
||||||
|
rtl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; --------------------------------------------------------------------
|
; --------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -202,3 +202,5 @@ UBER: total wall time: 165280 ms (8264 frames @ 50 Hz)
|
||||||
|
|
||||||
UBER: press any key to exit
|
UBER: press any key to exit
|
||||||
|
|
||||||
|
UBER: jlTileMapPaste 10x5: 49 iters / 16 frames = 153 ops/sec | hash=52B9646B
|
||||||
|
UBER: gameFrameClean composite: 11 iters / 16 frames = 34 ops/sec | hash=C51FE171
|
||||||
|
|
|
||||||
|
|
@ -198,3 +198,5 @@ UBER: total wall time: 289833 ms (17390 frames @ 60 Hz)
|
||||||
|
|
||||||
UBER: press any key to exit
|
UBER: press any key to exit
|
||||||
|
|
||||||
|
UBER: jlTileMapPaste 10x5: 49 iters / 18 frames = 163 ops/sec | hash=52B9646B
|
||||||
|
UBER: gameFrameClean composite: 13 iters / 16 frames = 48 ops/sec | hash=C51FE171
|
||||||
|
|
|
||||||
|
|
@ -198,3 +198,5 @@ UBER: total wall time: 23142 ms (1620 frames @ 70 Hz)
|
||||||
|
|
||||||
UBER: press any key to exit
|
UBER: press any key to exit
|
||||||
|
|
||||||
|
UBER: jlTileMapPaste 10x5: 113 iters / 18 frames = 450 ops/sec | hash=52B9646B
|
||||||
|
UBER: gameFrameClean composite: 43 iters / 16 frames = 195 ops/sec | hash=C51FE171
|
||||||
|
|
|
||||||
|
|
@ -98,3 +98,5 @@ UBER-CHK: floodFillBounded: hash=EF6B6F51
|
||||||
UBER-CHK: ----- end -----
|
UBER-CHK: ----- end -----
|
||||||
UBER: total wall time: 419016 ms (25141 frames @ 60 Hz)
|
UBER: total wall time: 419016 ms (25141 frames @ 60 Hz)
|
||||||
UBER: press any key to exit
|
UBER: press any key to exit
|
||||||
|
UBER: jlTileMapPaste 10x5: 15 iters / 16 frames = 56 ops/sec | hash=52B9646B
|
||||||
|
UBER: gameFrameClean composite: 5 iters / 16 frames = 18 ops/sec | hash=C51FE171
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue