WIP: feat(vk): map NVN's RGB16, RGB32 integer, BGRA8, BGR5 and RGB DXT1 formats #451

Draft
mooki wants to merge 2 commits from mooki/Longshot:feat/nvn-format-map into main
Member

Type: feat · Risk: medium · Tested on: macOS 26.7.1, Apple M4 Max (game-free checks, no game run) · Needs: Windows or Linux, any GPU (a game run)

Why

The Vulkan renderer has no Vulkan format for 14 of NVN's texture formats. A texture in one of them is left out on every
platform, with one log line, vk: NVN format %#x has no Vulkan format: its textures are left out. Whether the game
uses any of them is not known; the owner chose to map them all now rather than wait for that line in a log.

What changed

The renderer now maps the 14 NVN texture formats it had no Vulkan format for (RGB16 in five kinds, RGB32 integer, BGRA8,
the three BGR5 packings and RGB DXT1), so their textures are drawn instead of left out. Three-component formats are
widened to four on upload with alpha one in the image's own encoding, and a copy between a buffer and such a texture
goes through the CPU, or is skipped with a log line, instead of covering a third more of the buffer.

  • Read first: vk_format's new cases with alpha_reads_one, alpha_one and widen_rows in vk_texture.cpp, then
    copy_b2t.

  • vk: gpu/vk/vk_formats.cpp maps each of them, after NVN's component order (packed formats name their components from
    the lowest bit, as RGB565 already is):

    NVN Vulkan Note
    RGB16F, RGB16, RGB16SN, RGB16UI, RGB16I R16G16B16_SFLOAT / _UNORM / _SNORM / _UINT / _SINT 3 components: widened to RGBA16 on upload, as RGB8 and RGB32F already are
    RGB32UI, RGB32I R32G32B32_UINT / _SINT widened to RGBA32
    BGRA8, BGRA8_SRGB B8G8R8A8_UNORM / _SRGB as BGRX8 / BGRX8_SRGB
    BGR5A1 A1R5G5B5_UNORM_PACK16 blue in bits 0-4, alpha in bit 15
    BGR5 A1R5G5B5_UNORM_PACK16 BGR5A1 with its top bit unused: alpha reads one
    A1BGR5 R5G5B5A1_UNORM_PACK16 alpha in bit 0, then blue, green, red
    RGB_DXT1, RGB_DXT1_SRGB BC1_RGBA_UNORM_BLOCK / _SRGB RGBA_DXT1's format with alpha read as one, so both share one image (Vulkan's BC1_RGB is another view class)

    The two RGB_DXT1 formats were not in the first list of this gap. They come from the macOS port's texel-format survey
    (its §1, "NVN formats that never get a view on any platform"), and are mapped here because the owner chose to map every
    unmapped NVN format.

  • vk: gpu/vk/vk_texture.cpp.

    • widen_rows writes the alpha a widened format lacks as one in the image's own encoding (alpha_one). It wrote half
      1.0 (0x3C00) for every 16-bit format, which an RGBA16 UNORM or SNORM image reads as 0.23 or 0.47, and float 1.0 for
      every 32-bit one, which an RGBA32 integer image reads as 1065353216. Only RGBA16F and RGBA32F were right before, and
      only RGB32F was mapped of the 16- and 32-bit ones.
    • alpha_reads_one is the X formats' list that texture_view had inline, plus BGR5, the two RGB_DXT1 and the
      3-component formats: a sampled view of them reads alpha as one, whatever the image holds there.
    • nvn_format_widened (declared in vk_texture.h): whether a texture's image holds 4 components of its storage's 3.
    • texel_buffer_view makes no view of a 3-component format where the device has no texel-buffer support for that use
      (bufferFeatures). A buffer cannot be widened; such a view would break the Vulkan spec's rules (VUID-08778/08779).
  • vk: gpu/vk/vk_xfer.cpp copy_b2t. A copy between a buffer and a widened texture on the GPU used the image's texel
    size, so it read or wrote a third more of the buffer than the copy covers. This was already the case for RGB8 and RGB32F.

    • Into such a texture: through its storage on the CPU, as ASTC already is, so the upload widens it. When the CPU cannot
      reach the buffer, the copy is skipped with a log line.
    • Out of such a texture: skipped with a log line.
  • vk: gpu/vk/vk_shader.cpp handle_is_integer counts the 3-component integer formats as integer (their image is
    RGBA integer).

  • nx2: nx2/pipeline_states.py V1_NVN lists the 8 new Vulkan formats vk_format makes, as
    tests/test_pipeline_states.py requires.

  • tests: tests/test_nvn_formats.py (new). It fails on main.

    • Every format gpu/render/nvn_api.h names is mapped, or is a vertex attribute format only (*_UI2F, *_I2F,
      RGB10A2SN, RGB10A2I, RGB10A2_UI2F, RGB10A2_I2F, which vk_draw.cpp vertex_format maps).
    • It cuts vk_format, widened, alpha_one, widen_rows, alpha_reads_one, image_format_of, view_format_of,
      the upload's texel size and host/nvn_layout.c's fmt_of out of the sources, and compiles them with stubs for a
      made-up GPU.
    • Every mapped format's bytes per texel (its Vulkan format's, or the 3 components a widened one is uploaded from) are
      the texture layout's.
    • 64 synthetic texels of each newly mapped uncompressed format (all-zero bits, all-one bits, then made-up bytes) go
      through the upload's steps, and are read back by the sampled view's Vulkan format, from the spec's layout, with the
      view's swizzle. Each reads what NVN's definition says, with alpha one where the format has none. A widened image
      also holds alpha one itself, because storage views and copies read it without the swizzle.
    • RGB_DXT1 shares RGBA_DXT1's format and reads alpha as one.
  • tests: tests/test_texel_buffer_ownership.py gets a three-component case: no view without the format's
    texel-buffer support for that use. tests/test_image_view_retry.py cuts alpha_reads_one along with texture_view.

  • Design: the references for NVN's definitions are gpu/render/nvn_api.h's names, the WebGPU renderer's own decoding of
    the packed formats (gpu/render/rx_texture.cpp convert_packed16), and deko3d's format table
    (source/maxwell/format_traits.inc, zlib, read for reference only: nothing copied).

  • Left out: a copy out of a widened texture into a buffer is still skipped. Doing it exactly needs a narrowing pass on
    the GPU.

Measurements

Verdict: game-free only: the new test fails on main (14 formats with no Vulkan format) and passes here (156 formats
mapped or vertex only; 768 synthetic texels of 12 formats read as NVN defines them), and the suite adds no failure. The
in-game run a feat is measured by is still missing (Must be tested).

Commits: main 6c9edffe5d (2026-11-14); this PR feat/nvn-format-map @
2b651d733a.
Hardware: Apple M4 Max (16 CPUs), macOS 26.7.1, Apple clang 21. Game-free: no GPU, no game
Scene: none. The game was not run, so a feat's in-game measurement is still missing, and this PR stays WIP until then
(see Testing).
Method: tests/test_nvn_formats.py against main's sources (6c9edffe) and against this PR; the full
game-free suite on both
How to reproduce: python -I -B tests/test_nvn_formats.py --cxx clang++ --headers <Vulkan headers>

Before: main @ 6c9edffe After: this PR @ 2b651d73
tests/test_nvn_formats.py FAIL: 14 formats with no Vulkan format PASS: 156 formats mapped or vertex only; 768 synthetic texels of 12 formats read as NVN defines them
full game-free suite on macOS (tests/run_all.py --jobs 6) 531 passed, 32 failed, 17 skipped 532 passed, 32 failed, 17 skipped
main @ 6c9edffe:
NVN formats with no Vulkan format (their textures are left out): ['0x1d NVN_FORMAT_RGB16F', '0x1e NVN_FORMAT_RGB16', '0x1f NVN_FORMAT_RGB16SN', '0x20 NVN_FORMAT_RGB16UI', '0x21 NVN_FORMAT_RGB16I', '0x23 NVN_FORMAT_RGB32UI', '0x24 NVN_FORMAT_RGB32I', '0x41 NVN_FORMAT_RGB_DXT1', '0x45 NVN_FORMAT_RGB_DXT1_SRGB', '0x96 NVN_FORMAT_BGR5', '0x97 NVN_FORMAT_BGR5A1', '0x98 NVN_FORMAT_A1BGR5', '0x9a NVN_FORMAT_BGRA8', '0x9c NVN_FORMAT_BGRA8_SRGB']; left out but mapped: none; vertex_format maps the left-out ones: True

this PR @ 2b651d73:
PASS: 156 NVN formats mapped or vertex only, texel sizes as the layout's; 768 synthetic texels of 12 formats read as NVN defines them; RGB_DXT1 as RGBA_DXT1 with alpha one

Screenshots: none yet: no in-game run.

Flags and settings

None.

Testing

Tested:

  • On an Apple M4 Max with macOS 26.7.1, game-free only.
  • tests/test_nvn_formats.py fails on main and passes here. Before the rebase, three deliberate breaks in a scratch copy
    each made it fail: A1BGR5 as A1R5G5B5, RGBA16 UNORM's alpha as half 1.0 (main's widen_rows), and BGR5 left
    out of alpha_reads_one.
  • tests/test_texel_buffer_ownership.py, tests/test_image_view_retry.py, tests/test_pipeline_states.py,
    tests/test_pipeline_state_portable.py and the other checks that cut vk_texture.cpp and vk_xfer.cpp pass.
  • The full suite on both sides, compared by test name, at 2b651d73.
    • By test name, against main 6c9edffe's run on the same Mac: only one status changed: test_nvn_formats, absent on
      main, passes here.
    • These 32 checks fail here and on main on this Mac, by name (not triaged here: some need the macOS port's other pull
      requests, some are checks main added or changed since 5e072fc8): test_appimage_package_retry,
      test_appimage_player_build, test_collect_wide, test_control, test_cpm_discovery, test_crash_dialog,
      test_crash_record, test_env_on, test_game_layout, test_lift_cleanup_error, test_linux_preflight,
      test_longshot_ctl, test_low_memory_notice, test_make_game_cache, test_mem_gate, test_mem_gate_children,
      test_mirror_trim, test_oot_state_tap, test_pcache_header_read, test_pipeline_cache_file,
      test_pipeline_cache_save, test_pipeline_play_cache, test_pipeline_tour, test_prof_symbols,
      test_program_cache_cleanup, test_program_cache_one_variant, test_readback_late, test_report_run,
      test_split_giants, test_vk_pgo, test_vram_budget_row, test_walk_key.
    • The same 17 checks skipped as on main.
    • Crash reports during the run: none (main's run: fault-arm64-2026-11-14-180704.ips,
      recorder_sync_test.exe-2026-11-14-180744.ips).
  • Object compiles of gpu/vk/vk_formats.cpp, vk_texture.cpp, vk_xfer.cpp and vk_shader.cpp with the renderer's
    flags (nx2/build.py), Apple clang 21, arm64 and x86_64: all 16 build, 8 on main and 8 here.
    • Main's renderer does not build on macOS as it is (environ in vk_shader.cpp, posix_fallocate in
      pipeline_cache_file.h). These compiles include a compile-only prelude that declares those two names; it is part of
      no tree.
  • After the rebase onto main aa95d35f, at 687661e7 (2b651d73 before the rebases onto e090cc48 and aa95d35f):
    Sweep3's checks of this head (the C/C++ it changes compiled, and its own tests run, on arm64 and x86_64 where they
    apply) gave the same results as before; python -I -B tests/run_all.py on wave S1's merge 11a5b4e7, which carries
    this head: 568/22/17 (passed/failed/skipped) against main aa95d35f's 543/33/17 on this Mac, no new failure
    (reports/2026-11-15-rebase-sweep-3.md).

Not tested:

  • The game. Nobody knows yet whether it uses any of these formats: a run with this PR logs no vk: NVN format %#x has no Vulkan format line for them, and main's log names the ones it meets.
  • A GPU. The texel layouts are checked against the Vulkan spec, not sampled on a device.
  • Formats vk_format already mapped keep their Vulkan format. Of the widened ones, RGB8 and RGB32F get the same alpha
    bytes as before. RGB8SN, RGB8UI and RGB8I change: main wrote 0xFF, which they read as -0.008, 255 and -1; they now
    hold and read one. Those were not checked in a game run either.
  • deko3d's image formats (source/maxwell/format_traits.inc) have no RGB8 or RGB16 entry, so NVN may only use those as
    vertex or texel-buffer formats [INFERENCE]. Mapping them costs nothing when no texture uses them.
  • Not built on Windows or Linux. The change uses the same types and calls as the code around it.
  • An in-game measurement, so the box for numbers from the same PC is not in the checklist.

Must be tested:

  • Windows or Linux, any GPU, any driver: build this PR (head 687661e7) and main aa95d35f the same way, start each (this
    PR's first start translates every program again) and play the same few minutes of a save, then read each
    longshot_log.txt, expecting this PR's log to have no vk: NVN format ... has no Vulkan format line where main's
    names the formats it meets, any new vk: copy of ... 3-component texture ...: skipped line to be posted, and no
    texture to look different from main except one main left out (by hand: test request T-JTLG)

Risks

What could break: medium: every platform's texture paths. A texture in one of these formats is now drawn where it
was left out, so a wrong mapping would show as wrong colours rather than a missing texture. The new test checks each
mapping against NVN's and Vulkan's definitions.

  • NVN_FORMAT_RGB5A1 (already mapped) and BGR5A1 (new) both use A1R5G5B5_UNORM_PACK16 until the RGB5 bit-order PR
    (#452) lands. Main's RGB5A1 mapping looks red/blue swapped against NVN's order, and RGB5's is shifted by a bit.
    That fix changes colour on every platform, so it is its own PR (fix/nvn-rgb5-bit-order).
  • A copy between a buffer and a 3-component texture now goes through the CPU, or is skipped with a log line where it
    cannot be done exactly. Before, it read or wrote a third more of the buffer than the copy covered.
  • Landing order: rebased on main 6c9edffe (2026-11-14) without a conflict; main had changed vk_texture.cpp (#404) and
    nx2/pipeline_states.py (#563) away from this PR's lines. It conflicts with #452 (RGB5 bit order) in
    vk_texture.cpp (texture_view's alpha list) and nx2/pipeline_states.py (V1_NVN); vk_formats.cpp merges
    (git merge-tree of the two rebased heads). Whichever lands second needs a rebase.

Turning it off: revert the commit.

Compatibility: vk_shader.cpp (handle_is_integer) is one of the translator stamp's files (nx2/translator_files.py
since main's #370), so the first start after updating translates every program again, as any change to that file
does. Saves, the options file and the vk ABI are unchanged.

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how.
  • Rebased on current main: on aa95d35f (#761, 2026-11-15), head 687661e7, without a conflict; this PR's own
    commits, old → new: 4e3224ba → b566516e, 2b651d73 → 687661e7; the commit names elsewhere in this description
    are from before the rebase.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named). On macOS: 532 passed, 17
    skipped, and 32 failures that also fail on main 6c9edffe on this Mac, named under Testing. After the rebase onto main
    aa95d35f (head 687661e7): wave S1's merge 11a5b4e7, 568/22/17 against main aa95d35f's 543/33/17, no new failure.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
**Type:** feat · **Risk:** medium · **Tested on:** macOS 26.7.1, Apple M4 Max (game-free checks, no game run) · **Needs:** Windows or Linux, any GPU (a game run) ## Why The Vulkan renderer has no Vulkan format for 14 of NVN's texture formats. A texture in one of them is left out on every platform, with one log line, `vk: NVN format %#x has no Vulkan format: its textures are left out`. Whether the game uses any of them is not known; the owner chose to map them all now rather than wait for that line in a log. ## What changed The renderer now maps the 14 NVN texture formats it had no Vulkan format for (RGB16 in five kinds, RGB32 integer, BGRA8, the three BGR5 packings and RGB DXT1), so their textures are drawn instead of left out. Three-component formats are widened to four on upload with alpha one in the image's own encoding, and a copy between a buffer and such a texture goes through the CPU, or is skipped with a log line, instead of covering a third more of the buffer. - Read first: `vk_format`'s new cases with `alpha_reads_one`, `alpha_one` and `widen_rows` in `vk_texture.cpp`, then `copy_b2t`. - vk: `gpu/vk/vk_formats.cpp` maps each of them, after NVN's component order (packed formats name their components from the lowest bit, as `RGB565` already is): | NVN | Vulkan | Note | |---|---|---| | `RGB16F`, `RGB16`, `RGB16SN`, `RGB16UI`, `RGB16I` | `R16G16B16_SFLOAT` / `_UNORM` / `_SNORM` / `_UINT` / `_SINT` | 3 components: widened to RGBA16 on upload, as RGB8 and RGB32F already are | | `RGB32UI`, `RGB32I` | `R32G32B32_UINT` / `_SINT` | widened to RGBA32 | | `BGRA8`, `BGRA8_SRGB` | `B8G8R8A8_UNORM` / `_SRGB` | as `BGRX8` / `BGRX8_SRGB` | | `BGR5A1` | `A1R5G5B5_UNORM_PACK16` | blue in bits 0-4, alpha in bit 15 | | `BGR5` | `A1R5G5B5_UNORM_PACK16` | `BGR5A1` with its top bit unused: alpha reads one | | `A1BGR5` | `R5G5B5A1_UNORM_PACK16` | alpha in bit 0, then blue, green, red | | `RGB_DXT1`, `RGB_DXT1_SRGB` | `BC1_RGBA_UNORM_BLOCK` / `_SRGB` | `RGBA_DXT1`'s format with alpha read as one, so both share one image (Vulkan's `BC1_RGB` is another view class) | The two `RGB_DXT1` formats were not in the first list of this gap. They come from the macOS port's texel-format survey (its §1, "NVN formats that never get a view on any platform"), and are mapped here because the owner chose to map every unmapped NVN format. - vk: `gpu/vk/vk_texture.cpp`. - `widen_rows` writes the alpha a widened format lacks as one in the image's own encoding (`alpha_one`). It wrote half 1.0 (`0x3C00`) for every 16-bit format, which an RGBA16 UNORM or SNORM image reads as 0.23 or 0.47, and float 1.0 for every 32-bit one, which an RGBA32 integer image reads as 1065353216. Only RGBA16F and RGBA32F were right before, and only RGB32F was mapped of the 16- and 32-bit ones. - `alpha_reads_one` is the X formats' list that `texture_view` had inline, plus `BGR5`, the two `RGB_DXT1` and the 3-component formats: a sampled view of them reads alpha as one, whatever the image holds there. - `nvn_format_widened` (declared in `vk_texture.h`): whether a texture's image holds 4 components of its storage's 3. - `texel_buffer_view` makes no view of a 3-component format where the device has no texel-buffer support for that use (`bufferFeatures`). A buffer cannot be widened; such a view would break the Vulkan spec's rules (VUID-08778/08779). - vk: `gpu/vk/vk_xfer.cpp` `copy_b2t`. A copy between a buffer and a widened texture on the GPU used the image's texel size, so it read or wrote a third more of the buffer than the copy covers. This was already the case for RGB8 and RGB32F. - Into such a texture: through its storage on the CPU, as ASTC already is, so the upload widens it. When the CPU cannot reach the buffer, the copy is skipped with a log line. - Out of such a texture: skipped with a log line. - vk: `gpu/vk/vk_shader.cpp` `handle_is_integer` counts the 3-component integer formats as integer (their image is RGBA integer). - nx2: `nx2/pipeline_states.py` `V1_NVN` lists the 8 new Vulkan formats `vk_format` makes, as `tests/test_pipeline_states.py` requires. - tests: `tests/test_nvn_formats.py` (new). It fails on main. - Every format `gpu/render/nvn_api.h` names is mapped, or is a vertex attribute format only (`*_UI2F`, `*_I2F`, `RGB10A2SN`, `RGB10A2I`, `RGB10A2_UI2F`, `RGB10A2_I2F`, which `vk_draw.cpp` `vertex_format` maps). - It cuts `vk_format`, `widened`, `alpha_one`, `widen_rows`, `alpha_reads_one`, `image_format_of`, `view_format_of`, the upload's texel size and `host/nvn_layout.c`'s `fmt_of` out of the sources, and compiles them with stubs for a made-up GPU. - Every mapped format's bytes per texel (its Vulkan format's, or the 3 components a widened one is uploaded from) are the texture layout's. - 64 synthetic texels of each newly mapped uncompressed format (all-zero bits, all-one bits, then made-up bytes) go through the upload's steps, and are read back by the sampled view's Vulkan format, from the spec's layout, with the view's swizzle. Each reads what NVN's definition says, with alpha one where the format has none. A widened image also holds alpha one itself, because storage views and copies read it without the swizzle. - `RGB_DXT1` shares `RGBA_DXT1`'s format and reads alpha as one. - tests: `tests/test_texel_buffer_ownership.py` gets a `three-component` case: no view without the format's texel-buffer support for that use. `tests/test_image_view_retry.py` cuts `alpha_reads_one` along with `texture_view`. - Design: the references for NVN's definitions are `gpu/render/nvn_api.h`'s names, the WebGPU renderer's own decoding of the packed formats (`gpu/render/rx_texture.cpp` `convert_packed16`), and deko3d's format table (`source/maxwell/format_traits.inc`, zlib, read for reference only: nothing copied). - Left out: a copy out of a widened texture into a buffer is still skipped. Doing it exactly needs a narrowing pass on the GPU. ## Measurements **Verdict:** game-free only: the new test fails on main (14 formats with no Vulkan format) and passes here (156 formats mapped or vertex only; 768 synthetic texels of 12 formats read as NVN defines them), and the suite adds no failure. The in-game run a `feat` is measured by is still missing (Must be tested). Commits: main 6c9edffe5da1f5b5383977aeab3f544c0de25d4c (2026-11-14); this PR feat/nvn-format-map @ 2b651d733a727a4a0d77533cc9c07f2d370a295b. Hardware: Apple M4 Max (16 CPUs), macOS 26.7.1, Apple clang 21. Game-free: no GPU, no game Scene: none. The game was not run, so a `feat`'s in-game measurement is still missing, and this PR stays WIP until then (see Testing). Method: `tests/test_nvn_formats.py` against main's sources (6c9edffe) and against this PR; the full game-free suite on both How to reproduce: `python -I -B tests/test_nvn_formats.py --cxx clang++ --headers <Vulkan headers>` | | Before: main @ 6c9edffe | After: this PR @ 2b651d73 | |---|---|---| | `tests/test_nvn_formats.py` | FAIL: 14 formats with no Vulkan format | PASS: 156 formats mapped or vertex only; 768 synthetic texels of 12 formats read as NVN defines them | | full game-free suite on macOS (`tests/run_all.py --jobs 6`) | 531 passed, 32 failed, 17 skipped | 532 passed, 32 failed, 17 skipped | ``` main @ 6c9edffe: NVN formats with no Vulkan format (their textures are left out): ['0x1d NVN_FORMAT_RGB16F', '0x1e NVN_FORMAT_RGB16', '0x1f NVN_FORMAT_RGB16SN', '0x20 NVN_FORMAT_RGB16UI', '0x21 NVN_FORMAT_RGB16I', '0x23 NVN_FORMAT_RGB32UI', '0x24 NVN_FORMAT_RGB32I', '0x41 NVN_FORMAT_RGB_DXT1', '0x45 NVN_FORMAT_RGB_DXT1_SRGB', '0x96 NVN_FORMAT_BGR5', '0x97 NVN_FORMAT_BGR5A1', '0x98 NVN_FORMAT_A1BGR5', '0x9a NVN_FORMAT_BGRA8', '0x9c NVN_FORMAT_BGRA8_SRGB']; left out but mapped: none; vertex_format maps the left-out ones: True this PR @ 2b651d73: PASS: 156 NVN formats mapped or vertex only, texel sizes as the layout's; 768 synthetic texels of 12 formats read as NVN defines them; RGB_DXT1 as RGBA_DXT1 with alpha one ``` Screenshots: none yet: no in-game run. ### Flags and settings None. ## Testing **Tested:** - On an Apple M4 Max with macOS 26.7.1, game-free only. - `tests/test_nvn_formats.py` fails on main and passes here. Before the rebase, three deliberate breaks in a scratch copy each made it fail: `A1BGR5` as `A1R5G5B5`, RGBA16 UNORM's alpha as half 1.0 (main's `widen_rows`), and `BGR5` left out of `alpha_reads_one`. - `tests/test_texel_buffer_ownership.py`, `tests/test_image_view_retry.py`, `tests/test_pipeline_states.py`, `tests/test_pipeline_state_portable.py` and the other checks that cut `vk_texture.cpp` and `vk_xfer.cpp` pass. - The full suite on both sides, compared by test name, at 2b651d73. - By test name, against main 6c9edffe's run on the same Mac: only one status changed: `test_nvn_formats`, absent on main, passes here. - These 32 checks fail here and on main on this Mac, by name (not triaged here: some need the macOS port's other pull requests, some are checks main added or changed since 5e072fc8): `test_appimage_package_retry`, `test_appimage_player_build`, `test_collect_wide`, `test_control`, `test_cpm_discovery`, `test_crash_dialog`, `test_crash_record`, `test_env_on`, `test_game_layout`, `test_lift_cleanup_error`, `test_linux_preflight`, `test_longshot_ctl`, `test_low_memory_notice`, `test_make_game_cache`, `test_mem_gate`, `test_mem_gate_children`, `test_mirror_trim`, `test_oot_state_tap`, `test_pcache_header_read`, `test_pipeline_cache_file`, `test_pipeline_cache_save`, `test_pipeline_play_cache`, `test_pipeline_tour`, `test_prof_symbols`, `test_program_cache_cleanup`, `test_program_cache_one_variant`, `test_readback_late`, `test_report_run`, `test_split_giants`, `test_vk_pgo`, `test_vram_budget_row`, `test_walk_key`. - The same 17 checks skipped as on main. - Crash reports during the run: none (main's run: fault-arm64-2026-11-14-180704.ips, recorder_sync_test.exe-2026-11-14-180744.ips). - Object compiles of `gpu/vk/vk_formats.cpp`, `vk_texture.cpp`, `vk_xfer.cpp` and `vk_shader.cpp` with the renderer's flags (`nx2/build.py`), Apple clang 21, arm64 and x86_64: all 16 build, 8 on main and 8 here. - Main's renderer does not build on macOS as it is (`environ` in `vk_shader.cpp`, `posix_fallocate` in `pipeline_cache_file.h`). These compiles include a compile-only prelude that declares those two names; it is part of no tree. - **After the rebase onto main aa95d35f**, at `687661e7` (`2b651d73` before the rebases onto e090cc48 and aa95d35f): Sweep3's checks of this head (the C/C++ it changes compiled, and its own tests run, on arm64 and x86_64 where they apply) gave the same results as before; `python -I -B tests/run_all.py` on wave S1's merge `11a5b4e7`, which carries this head: 568/22/17 (passed/failed/skipped) against main aa95d35f's 543/33/17 on this Mac, no new failure (`reports/2026-11-15-rebase-sweep-3.md`). **Not tested:** - The game. Nobody knows yet whether it uses any of these formats: a run with this PR logs no `vk: NVN format %#x has no Vulkan format` line for them, and main's log names the ones it meets. - A GPU. The texel layouts are checked against the Vulkan spec, not sampled on a device. - Formats `vk_format` already mapped keep their Vulkan format. Of the widened ones, RGB8 and RGB32F get the same alpha bytes as before. RGB8SN, RGB8UI and RGB8I change: main wrote `0xFF`, which they read as -0.008, 255 and -1; they now hold and read one. Those were not checked in a game run either. - deko3d's image formats (`source/maxwell/format_traits.inc`) have no RGB8 or RGB16 entry, so NVN may only use those as vertex or texel-buffer formats [INFERENCE]. Mapping them costs nothing when no texture uses them. - Not built on Windows or Linux. The change uses the same types and calls as the code around it. - An in-game measurement, so the box for numbers from the same PC is not in the checklist. **Must be tested:** - Windows or Linux, any GPU, any driver: build this PR (head 687661e7) and main aa95d35f the same way, start each (this PR's first start translates every program again) and play the same few minutes of a save, then read each `longshot_log.txt`, expecting this PR's log to have no `vk: NVN format ... has no Vulkan format` line where main's names the formats it meets, any new `vk: copy of ... 3-component texture ...: skipped` line to be posted, and no texture to look different from main except one main left out (by hand: test request T-JTLG) ## Risks **What could break:** medium: every platform's texture paths. A texture in one of these formats is now drawn where it was left out, so a wrong mapping would show as wrong colours rather than a missing texture. The new test checks each mapping against NVN's and Vulkan's definitions. - `NVN_FORMAT_RGB5A1` (already mapped) and `BGR5A1` (new) both use `A1R5G5B5_UNORM_PACK16` until the RGB5 bit-order PR (#452) lands. Main's `RGB5A1` mapping looks red/blue swapped against NVN's order, and `RGB5`'s is shifted by a bit. That fix changes colour on every platform, so it is its own PR (`fix/nvn-rgb5-bit-order`). - A copy between a buffer and a 3-component texture now goes through the CPU, or is skipped with a log line where it cannot be done exactly. Before, it read or wrote a third more of the buffer than the copy covered. - Landing order: rebased on main 6c9edffe (2026-11-14) without a conflict; main had changed `vk_texture.cpp` (#404) and `nx2/pipeline_states.py` (#563) away from this PR's lines. It conflicts with #452 (RGB5 bit order) in `vk_texture.cpp` (`texture_view`'s alpha list) and `nx2/pipeline_states.py` (`V1_NVN`); `vk_formats.cpp` merges (`git merge-tree` of the two rebased heads). Whichever lands second needs a rebase. **Turning it off:** revert the commit. **Compatibility:** `vk_shader.cpp` (`handle_is_integer`) is one of the translator stamp's files (`nx2/translator_files.py` since main's #370), so the first start after updating translates every program again, as any change to that file does. Saves, the options file and the vk ABI are unchanged. ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. - [x] Rebased on current `main`: on aa95d35f (#761, 2026-11-15), head `687661e7`, without a conflict; this PR's own commits, old → new: `4e3224ba` → `b566516e`, `2b651d73` → `687661e7`; the commit names elsewhere in this description are from before the rebase. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). On macOS: 532 passed, 17 skipped, and 32 failures that also fail on main 6c9edffe on this Mac, named under Testing. After the rebase onto main aa95d35f (head `687661e7`): wave S1's merge `11a5b4e7`, 568/22/17 against main aa95d35f's 543/33/17, no new failure. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened.
vk_format left 14 NVN texture formats unmapped, so texture_get logged "has no Vulkan format" and their textures were
left out on every platform: RGB16 F/UNORM/SNORM/UI/I, RGB32UI/I, BGRA8 (+sRGB), BGR5, BGR5A1, A1BGR5 and RGB_DXT1
(+sRGB). Each is now mapped. The 3-component ones are widened on upload as RGB8 and RGB32F already were, with an alpha
of one in the image's own encoding (widen_rows wrote half 1.0 into UNORM16 and float 1.0 into RGB32 integers). BGR5
and RGB_DXT1 share BGR5A1's and RGBA_DXT1's images and read alpha as one, as the X formats do (alpha_reads_one).

A buffer-to-texture copy into a widened texture goes through its storage on the CPU (as ASTC does), and a copy out of
one is skipped with a log line: a copy on the GPU took 4/3 of the buffer's bytes. A 3-component texel buffer view is
made only where the device has the format for that use. 3-component integer textures count as integer.

tests/test_nvn_formats.py (new): every nvn_api.h format is mapped or a vertex format; texel sizes match the texture
layout's; synthetic texels of each newly mapped format, uploaded and widened, read as NVN defines them. Fails on main.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
test(vk): cite deko3d's format table for NVN's packed component order
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Successful in 5s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Third-party parts / As their notes say (pull_request) Successful in 9s
Website / Website on Cloudflare (pull_request_target) Has been skipped
efe4cd82d5
tests/test_nvn_formats.py's docstring and LEFT_OUT comment name deko3d's source/maxwell/format_traits.inc as the
reference for NVN's packed formats and image format list. Comments only.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mooki force-pushed feat/nvn-format-map from efe4cd82d5
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Successful in 5s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Third-party parts / As their notes say (pull_request) Successful in 9s
Website / Website on Cloudflare (pull_request_target) Has been skipped
to 2b651d733a
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 17s
2026-11-15 01:52:02 +00:00
Compare
mooki force-pushed feat/nvn-format-map from 2b651d733a
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 17s
to d5436d9916
All checks were successful
PR description / Description follows the template (pull_request_target) Successful in 5s
2026-11-15 07:01:40 +00:00
Compare
mooki force-pushed feat/nvn-format-map from d5436d9916
All checks were successful
PR description / Description follows the template (pull_request_target) Successful in 5s
to 687661e7bf
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 5s
Third-party parts / As their notes say (pull_request) Successful in 10s
PR description / Description follows the template (pull_request_target) Successful in 4s
2026-11-15 13:02:56 +00:00
Compare
HumanBean force-pushed feat/nvn-format-map from 687661e7bf
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 5s
Third-party parts / As their notes say (pull_request) Successful in 10s
PR description / Description follows the template (pull_request_target) Successful in 4s
to 5b8099757e
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 4s
Third-party parts / As their notes say (pull_request) Successful in 7s
2026-11-15 18:43:17 +00:00
Compare
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 4s
Third-party parts / As their notes say (pull_request) Successful in 7s
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/nvn-format-map:mooki-feat/nvn-format-map'
git switch 'mooki-feat/nvn-format-map'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!451
No description provided.