WIP: feat(vk): map NVN's RGB16, RGB32 integer, BGRA8, BGR5 and RGB DXT1 formats #451
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!451
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:feat/nvn-format-map"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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 gameuses 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 withalpha_reads_one,alpha_oneandwiden_rowsinvk_texture.cpp, thencopy_b2t.vk:
gpu/vk/vk_formats.cppmaps each of them, after NVN's component order (packed formats name their components fromthe lowest bit, as
RGB565already is):RGB16F,RGB16,RGB16SN,RGB16UI,RGB16IR16G16B16_SFLOAT/_UNORM/_SNORM/_UINT/_SINTRGB32UI,RGB32IR32G32B32_UINT/_SINTBGRA8,BGRA8_SRGBB8G8R8A8_UNORM/_SRGBBGRX8/BGRX8_SRGBBGR5A1A1R5G5B5_UNORM_PACK16BGR5A1R5G5B5_UNORM_PACK16BGR5A1with its top bit unused: alpha reads oneA1BGR5R5G5B5A1_UNORM_PACK16RGB_DXT1,RGB_DXT1_SRGBBC1_RGBA_UNORM_BLOCK/_SRGBRGBA_DXT1's format with alpha read as one, so both share one image (Vulkan'sBC1_RGBis another view class)The two
RGB_DXT1formats 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_rowswrites the alpha a widened format lacks as one in the image's own encoding (alpha_one). It wrote half1.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 forevery 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_oneis the X formats' list thattexture_viewhad inline, plusBGR5, the twoRGB_DXT1and the3-component formats: a sampled view of them reads alpha as one, whatever the image holds there.
nvn_format_widened(declared invk_texture.h): whether a texture's image holds 4 components of its storage's 3.texel_buffer_viewmakes 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.cppcopy_b2t. A copy between a buffer and a widened texture on the GPU used the image's texelsize, so it read or wrote a third more of the buffer than the copy covers. This was already the case for RGB8 and RGB32F.
reach the buffer, the copy is skipped with a log line.
vk:
gpu/vk/vk_shader.cpphandle_is_integercounts the 3-component integer formats as integer (their image isRGBA integer).
nx2:
nx2/pipeline_states.pyV1_NVNlists the 8 new Vulkan formatsvk_formatmakes, astests/test_pipeline_states.pyrequires.tests:
tests/test_nvn_formats.py(new). It fails on main.gpu/render/nvn_api.hnames is mapped, or is a vertex attribute format only (*_UI2F,*_I2F,RGB10A2SN,RGB10A2I,RGB10A2_UI2F,RGB10A2_I2F, whichvk_draw.cppvertex_formatmaps).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'sfmt_ofout of the sources, and compiles them with stubs for amade-up GPU.
the texture layout's.
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_DXT1sharesRGBA_DXT1's format and reads alpha as one.tests:
tests/test_texel_buffer_ownership.pygets athree-componentcase: no view without the format'stexel-buffer support for that use.
tests/test_image_view_retry.pycutsalpha_reads_onealong withtexture_view.Design: the references for NVN's definitions are
gpu/render/nvn_api.h's names, the WebGPU renderer's own decoding ofthe packed formats (
gpu/render/rx_texture.cppconvert_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
featis 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.pyagainst main's sources (6c9edffe) and against this PR; the fullgame-free suite on both
How to reproduce:
python -I -B tests/test_nvn_formats.py --cxx clang++ --headers <Vulkan headers>6c9edffe2b651d73tests/test_nvn_formats.pytests/run_all.py --jobs 6)Screenshots: none yet: no in-game run.
Flags and settings
None.
Testing
Tested:
tests/test_nvn_formats.pyfails on main and passes here. Before the rebase, three deliberate breaks in a scratch copyeach made it fail:
A1BGR5asA1R5G5B5, RGBA16 UNORM's alpha as half 1.0 (main'swiden_rows), andBGR5leftout 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.pyand the other checks that cutvk_texture.cppandvk_xfer.cpppass.2b651d73.test_nvn_formats, absent onmain, passes here.
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.recorder_sync_test.exe-2026-11-14-180744.ips).
gpu/vk/vk_formats.cpp,vk_texture.cpp,vk_xfer.cppandvk_shader.cppwith the renderer'sflags (
nx2/build.py), Apple clang 21, arm64 and x86_64: all 16 build, 8 on main and 8 here.environinvk_shader.cpp,posix_fallocateinpipeline_cache_file.h). These compiles include a compile-only prelude that declares those two names; it is part ofno tree.
aa95d35f, at687661e7(2b651d73before the rebases ontoe090cc48andaa95d35f):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.pyon wave S1's merge11a5b4e7, which carriesthis 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:
vk: NVN format %#x has no Vulkan formatline for them, and main's log names the ones it meets.vk_formatalready mapped keep their Vulkan format. Of the widened ones, RGB8 and RGB32F get the same alphabytes as before. RGB8SN, RGB8UI and RGB8I change: main wrote
0xFF, which they read as -0.008, 255 and -1; they nowhold and read one. Those were not checked in a game run either.
source/maxwell/format_traits.inc) have no RGB8 or RGB16 entry, so NVN may only use those asvertex or texel-buffer formats [INFERENCE]. Mapping them costs nothing when no texture uses them.
Must be tested:
687661e7) and mainaa95d35fthe same way, start each (thisPR'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 novk: NVN format ... has no Vulkan formatline where main'snames the formats it meets, any new
vk: copy of ... 3-component texture ...: skippedline to be posted, and notexture 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) andBGR5A1(new) both useA1R5G5B5_UNORM_PACK16until the RGB5 bit-order PR(#452) lands. Main's
RGB5A1mapping looks red/blue swapped against NVN's order, andRGB5's is shifted by a bit.That fix changes colour on every platform, so it is its own PR (
fix/nvn-rgb5-bit-order).cannot be done exactly. Before, it read or wrote a third more of the buffer than the copy covered.
6c9edffe(2026-11-14) without a conflict; main had changedvk_texture.cpp(#404) andnx2/pipeline_states.py(#563) away from this PR's lines. It conflicts with #452 (RGB5 bit order) invk_texture.cpp(texture_view's alpha list) andnx2/pipeline_states.py(V1_NVN);vk_formats.cppmerges(
git merge-treeof 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.pysince 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
type(scope): description) of the status line's type.main: onaa95d35f(#761, 2026-11-15), head687661e7, without a conflict; this PR's owncommits, old → new:
4e3224ba→b566516e,2b651d73→687661e7; the commit names elsewhere in this descriptionare from before the rebase.
python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). On macOS: 532 passed, 17skipped, and 32 failures that also fail on main
6c9edffeon this Mac, named under Testing. After the rebase onto mainaa95d35f(head687661e7): wave S1's merge11a5b4e7, 568/22/17 against main aa95d35f's 543/33/17, no new failure.third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.efe4cd82d52b651d733a2b651d733ad5436d9916d5436d9916687661e7bf687661e7bf5b8099757eView command line instructions
Checkout
From your project repository, check out a new branch and test the changes.