Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions LICENSE-ROXAS2712.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Roxas2712 — Fork Contributions

Version 1.0 · Effective 25 October 2026

Copyright © 2026 Roxas2712, to the extent of rights held.
All rights reserved except as expressly granted below or already granted.

## 1. Scope and ownership

This notice applies only to original contributions made by Roxas2712
(GitHub: Roxas2712) to `Roxas2712/Gen2Recomped`, including original additions
within modified files, insofar as he owns the relevant rights. Git history,
pull-request diffs and source provenance identify contributions; a commit
author or a whole-file change alone does not establish ownership of every
part of that file.

It does not claim ownership of Cedric's work, Gen1Recomp / BOIS CLUB GAMES
work, other contributors' work, third-party dependencies, or ROM-derived
content. Their applicable licences and notices remain in force. It does not
grant permission to distribute the complete combined fork where another
rights holder's permission is required.

## 2. General permission and reserved rights

For contributions covered by this notice and not already licensed more
broadly, you may read and study the published source and make private,
non-commercial modifications and builds for your own use. This grants no
general permission to redistribute, sublicense, sell, or offer those
contributions as a hosted service. Other uses require Roxas2712's
written permission, except for the specific permission below and rights
available independently under applicable law.

Keep the relevant copyright, licence and attribution notices. Do not
represent Roxas2712's contributions as exclusively authored by someone
else. Public source availability alone does not put them in the public
domain or grant an open-source licence.

## 3. Specific permission for Cedric / UNDERdecoded

Roxas2712 expressly grants Cedric (Ced; GitHub: UNDERdecoded /
UNDERdecodedHD) a non-exclusive, worldwide, perpetual, royalty-free
permission to use, reproduce, integrate, modify and distribute the covered
contributions that Roxas2712 submits or expressly offers through pull
requests to `UNDERdecoded/Gen2Recomped`, for Cedric's Gen2Recomped /
RetroRecompiled project. This includes its renamed continuation maintained
by Cedric, its official builds, and its commercial operation.

Cedric may license the incorporated contributions to recipients as part of
that project, subject to preserving this permission's applicable credit and
notice requirements. This permission is not a copyright assignment, is not
exclusive, and does not cover unpublished work, separate repositories,
ModCompass service operation, credentials, or content not offered in those
pull requests. Roxas2712 retains his own rights to his contributions.

For uses relying on this permission:

- retain the relevant source copyright and licence notices;
- give visible credit in the project's README or equivalent public project
page, and in the application's existing About / Credits view, if present;
- use at least the following credit, or a substantially equivalent one:

> Contributions by Roxas2712 —
> https://github.com/Roxas2712

A maintained consolidated contributor credit is sufficient; a separate
credit on every screen or for every change is not required. Adapted
contributions must not be attributed exclusively to another author. Other
contributors' credits must also remain intact.

## 4. Earlier licences and permissions remain effective

This notice does not revoke, narrow, or retrospectively add conditions to
any permission already validly granted, including MIT / GPL grants,
separate agreements, or permissions arising from previously submitted
pull requests. Where an earlier grant permits a use independently of this
notice, its existing terms govern that use. For such earlier uses, the
visible credit above is requested to the extent it is not already required.

For subsequent contributions, this is the default statement of Roxas2712's own reserved rights and the specific PR permission, subject to any
applicable existing licence obligations or separately agreed terms. It
does not change another rights holder's licence or guarantee compatibility
of a particular combination of code.

## 5. No warranty

To the extent permitted by applicable law, these contributions are provided
as is, without warranty. No support obligation, endorsement, or rights to
third-party code, game assets, names, or trademarks are granted.
70 changes: 70 additions & 0 deletions docs/native-atlas-compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Native atlas resources for unchanged GBA mods

`src.core.game3.tileset_native` projects imported native tileset banks into the
atlas resources expected by existing GBA mods. Mod manifests and assets do not
need rewriting. The projection owns its images, image data and quads; it never
lends a native renderer sheet to a consumer that can recolor or release it.

The pixel model retains the source atlas's 16-column slot layout, transparent
upper plane, source BGR555 rounding, primary color-zero clearing and reserved
background palette rules. Imported original palette words are retained beside
the existing RGB data. Custom RGB remains exact. Palette changes only revisit
metatiles using the changed slots; tile changes only invalidate affected decodes.
Indexed pixel locations remain stable during recoloring.

Each declared tile animation observes its own period without a combined-cycle
limit. Palette programs use their original words when available. The resource
view observes `TileRenderer.animFrame()` and does not advance another terrain
clock. Uploads maintain independent dirty histories for two or three image
buffers, including switches between those modes. Flush calls remain visible to
installed mod wrappers, and existing field batches can follow image rotation.

`asset_stream` and `warm` provide prioritized cooperative preparation,
cancellation and teardown. Explicit `get` may finish a pending request, as the
consumer contract requires. Atlas leases preserve retired resources until the
last borrower releases them. Queue failures retain diagnostics and clean up
partial allocations; replacing public cache entries also retires unreferenced
owned resources. Loader cleanup runs after mod wrappers unwind.

## Verification

Integration prerequisites: terrain palette decoding/import (#259), semantic
pair identities (#250), early native session resolution (#254), and the map
fixture/read-view foundations from #251 with its dependencies. Five-edition
tests also require the separate cartridge registrations (#183, #186, #187).
Loader cleanup overlaps those integration branches and should retain both
map/player retirement and the resource retirement added here.

From the repository root, with LuaJIT:

```
luajit tools/native-atlas/pixels-check.lua
luajit tools/native-atlas/palette-check.lua
luajit tools/native-atlas/resources-check.lua
luajit tools/native-atlas/warm-check.lua
luajit tests/run_engine.lua
luajit tests/run_modkit.lua
```

The synthetic pixel goldens come from the independent source compositor,
including its palette merge operation. The LÖVE host in
`tools/native-atlas/host` exercises real texture uploads and readbacks; it
requires a fresh QA identity, the requested graphics backend, and explicit
`NATIVE_ATLAS_ENGINE` / `NATIVE_ATLAS_OUT` paths. The cartridge comparison host
hashes normalized planes per metatile, without writing cartridge artwork.
Private cartridge inputs and player data are excluded from the public tests.

## Remaining integration

This resource layer does not establish complete game or renderer compatibility.
The distinct session facade, map writes, full renderer and gameplay services
still need integration. A replacement draw path must retain one native terrain
clock owner. Original tile callback phase semantics and palette profiles across
all five editions still need complete auditing. Old imports without original
palette words retain their RGB; automatic cache upgrading is a separate task.
The cooperative stream does not yet implement generic worker-task protocols.

Final GPU and cartridge receipts must identify the exact tested source snapshot;
successful pixel comparisons alone do not prove unchanged-mod startup or play.
Native QA uses separate identities, preserves player saves and keeps at least
8 GiB free disk reserve.
35 changes: 35 additions & 0 deletions docs/qa/native-foundation/static-palette-projection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Reuse validated static atlas palette projections

Repeated native atlas observations rebuilt the same 256 RGBA entries, BGR
tables and concatenated palette rows. Reuse the projection after comparing
all consumed RGB inputs by value, the original palette-word strings and the
palette-bank boundaries. Keep caller-visible BGR tables detached from the
cache. Same-clock nested RGB writes, changed provenance, modified row metadata,
palette programs, explicit overrides and foreign table observers retain the
full projection path. No terrain clock or upload owner changes.

Qualification on main cbb552a8 plus the existing shader corrections:

- 13 focused CPU groups pass: cache 1360, independent pixel goldens 1748,
palette words/programs 602, atlas resource lifetime 526, static palette
reuse 2448, samples 1018, frame cache 622, renderer 3639, indexed drawing
7771, Gen2 queries 19, drawing 247, byte register 15433 and clock 46.
- An alternating five-trial CPU comparison of 3000 updates per edition
measures median old/new milliseconds: FireRed 118.527/71.382,
LeafGreen 121.363/71.934, Emerald 127.920/71.836,
Ruby 126.816/72.111, Sapphire 132.229/74.963. Final lower/upper RGBA
bytes equal the earlier projection in each edition. These are isolated
CPU costs; they establish no whole-scene FPS improvement.
- The benchmark is reproducible with
`luajit tools/native-atlas/palette-reuse-benchmark.lua /path/to/earlier/AtlasPixels.lua`.
The comparison baseline is 9f8cc0fb, before this projection cache.
- The actual native atlas GPU fixture passes 526 checks with 215 texture
readbacks under each of Metal and OpenGL on the integrated fork source.
- The actually offered atlas component, composed only with its published
palette decoding prerequisite c08f8011 (#259), independently passes the
cache 1360, pixel 1748 and palette-word 602 checks. Its resource/clock
prerequisites remain those listed in the atlas offer (#260).

Receipts: focused CPU 6413d6b825, benchmark b436937911, GPU a980111c01,
offered component e49fd412c0.
Contributions by Roxas2712 under LICENSE-ROXAS2712.md; existing rights remain.
66 changes: 66 additions & 0 deletions src/compat/gen1/AtlasPaletteCache.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-- Copyright (c) 2026 Roxas2712. See LICENSE-ROXAS2712.md.
-- Reuse a static native palette projection only after comparing every RGB
-- input by value. Caller-visible palette tables are never the cache's owner.
local C={};C.__index=C
local function plain(value)return type(value)=='table'and getmetatable(value)==nil end
local function copyRows(rows)
local out={}
for slot=0,15 do local row={};out[slot]=row;for color=0,15 do row[color]=rows[slot][color]end end
return out
end
local function inputs(self,expected,out)
local decoder,layout=self.decoder,self.layout
local state=decoder.paletteState
if not plain(state)or not state.complete or not plain(state.primary)or not plain(state.secondary)then return false end
for _,bank in ipairs{decoder.primary,decoder.secondary}do
if not plain(bank)or not plain(bank.paletteCycles)or next(bank.paletteCycles)~=nil then return false end
end
local at=0
for slot=0,15 do
local reserved=slot>=(layout.palettesTotal or 13)
local list=reserved and decoder.primary.palettes or(slot<decoder.palettesInPrimary and state.primary or state.secondary)
if not plain(list)then return false end
local row=list[reserved and 1 or slot+1];if not plain(row)then return false end
for color=0,15 do
local rgb=row[color+1];if not plain(rgb)then return false end
for channel=1,3 do
local value=not reserved and slot<decoder.palettesInPrimary and color==0 and 0 or rgb[channel]
if type(value)~='number'then return false end
at=at+1
if expected and expected[at]~=value then return false end
if out then out[at]=value end
end
end
end
return true
end
function C.new(decoder,layout)
return setmetatable({decoder=decoder,layout=layout,hits=0,misses=0},C)
end
local function eligible(overrides)return plain(overrides)and next(overrides)==nil end
function C:read(overrides,rows)
local d=self.decoder;local saved=self.saved
if saved and eligible(overrides)and rows
and saved.primaryBytes==d.primary.sourcePaletteBytes and saved.secondaryBytes==d.secondary.sourcePaletteBytes
and saved.split==d.palettesInPrimary and saved.total==(self.layout.palettesTotal or 13)
and inputs(self,saved.rgb)then
local same=true
for slot=0,15 do if rows[slot]~=saved.rows[slot]then same=false;break end end
if same then
self.hits=self.hits+1
return saved.lut,copyRows(saved.bgr),saved.key,{}
end
end
self.misses=self.misses+1
end
function C:remember(overrides,rows,lut,bgr,key)
self.saved=nil
if not eligible(overrides)then return end
local rgb={};if not inputs(self,nil,rgb)then return end
local d=self.decoder;local savedRows,savedLut={},{}
for slot=0,15 do savedRows[slot]=rows[slot]end
for i=0,255 do savedLut[i]=lut[i]end
self.saved={rgb=rgb,primaryBytes=d.primary.sourcePaletteBytes,secondaryBytes=d.secondary.sourcePaletteBytes,
split=d.palettesInPrimary,total=self.layout.palettesTotal or 13,rows=savedRows,lut=savedLut,bgr=copyRows(bgr),key=key}
end
return C
Loading