Skip the menu's draw for a frame when its vertex or index buffer cannot be allocated, instead of copying through a null pointer.

The menu's Vulkan back end went on after a failed buffer allocation: it bound and mapped memory it did not have and copied the frame's vertices to the address the failed mapping left, null. Two reports of 0.8.0 end that way right after the settings menu opens with video memory used up, one on an integrated AMD GPU and one on an RTX 2050: two "got VkResult -2" lines, then an access violation in the C runtime's copy, writing address 0x20, not on a guest thread. The allocation now leaves no buffer and size 0 when it fails, the draw returns before anything is recorded, and the next frame asks for the buffer again. The change is marked in the vendored file and named in THIRD_PARTY_NOTICES.md. Compiled; the failing allocation was not reproduced here.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-12 15:19:59 -05:00
parent 222a025c52
commit 18aa16f2df
2 changed files with 31 additions and 1 deletions

View File

@@ -29,7 +29,7 @@ Code copied or adapted from another project is listed here with its source, the
| SDL 3.4.18, dynamic link, `SDL3.dll` copied next to `nx2navi.exe`. Developer builds use the MSYS2 build (`mingw-w64-ucrt-x86_64-sdl3`, which imports `libiconv-2.dll`, LGPL-2.1, unmodified, copied beside it); the installer release ships the upstream release `SDL3-3.4.18-win32-x64.zip`, which imports no libiconv | https://libsdl.org, Copyright (c) 1997-2026 Sam Lantinga | zlib | `runtime/input_win.c` (gamepad input, rumble, gyro and accelerometer) |
| libopus (MSYS2 `mingw-w64-ucrt-x86_64-opus`), dynamic link, `libopus-0.dll` copied next to `nx2navi.exe` | https://opus-codec.org, Copyright (c) 2001-2023 Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry, CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo, Mozilla, Amazon | BSD-3-Clause | `runtime/ov_media.c` (nn::codec hardware Opus decoder) |
| Monocypher 4.0.2 (`monocypher.c`, `monocypher.h`, unmodified; licence text in `runtime/third_party/monocypher/LICENCE.md`) | https://monocypher.org/download/monocypher-4.0.2.tar.gz (SHA-512 `bf275d4c53ff94af6cdc723a4e002e9f080f4d1436c86c76bb37870b34807f1d7b32331d8ff8a1aeb369e946f3769021e03e63efac25b82efc5abf54dc084714`), Copyright (c) 2017-2023 Loup Vaillant, Michael Savage, Fabio Scotoni | BSD-2-Clause (dual-licensed with CC0-1.0; the BSD licence is the one used here) | `runtime/third_party/monocypher/` (compiled into `nx2navi.exe` for co-op: key derivation with BLAKE2b and the room's messages sealed with XChaCha20-Poly1305, `runtime/coop_proto.c`) |
| Dear ImGui 1.91.9 (core and Vulkan back end, unmodified), as vendored by LAS Recomp | https://github.com/ocornut/imgui, via https://github.com/peiikai/LASRecomp @ `da35b83a9611d046a9a681aae668d7085954d37f` (`third_party/imgui/`), Copyright (c) 2014-2025 Omar Cornut | MIT | `runtime/third_party/imgui/` (compiled into `nx2navi.exe` for the in-game menu, `runtime/ui/`) |
| Dear ImGui 1.91.9 (core unmodified; the Vulkan back end with one change, marked NX2NAVI: a vertex or index buffer that cannot be allocated or mapped skips the frame's draw), as vendored by LAS Recomp | https://github.com/ocornut/imgui, via https://github.com/peiikai/LASRecomp @ `da35b83a9611d046a9a681aae668d7085954d37f` (`third_party/imgui/`), Copyright (c) 2014-2025 Omar Cornut | MIT | `runtime/third_party/imgui/` (compiled into `nx2navi.exe` for the in-game menu, `runtime/ui/`) |
| SavageWindRecomp settings menu: two-pane option rows (row look, left / right stepping, hold speed-up, description pane), the menu style and tab bar | https://github.com/Revivedx/SavageWindRecomp @ `575ad88e02f9a89a9a885d90039d48af33c982f4` (`runtime/ui/option_rows.mm`, `runtime/ui/menu.mm`), Copyright (c) 2026 Revivedx and the Savage Wind Recomp contributors, Copyright (c) 2026 Peiikai | MIT | `runtime/ui/option_rows.cpp`, `runtime/ui/option_rows.h`, `runtime/ui/menu.cpp` |
| SavageWindRecomp performance overlay: the frame ring and its one-second summary, the NVML calls, the sampling loop, the full panel's layout | https://github.com/Revivedx/SavageWindRecomp @ `575ad88e02f9a89a9a885d90039d48af33c982f4` (`runtime/perfmon.c`, `runtime/perfmon.h`, `perf_overlay_draw` in `runtime/ui/menu.mm`), Copyright (c) 2026 Revivedx and the Savage Wind Recomp contributors, Copyright (c) 2026 Peiikai | MIT | `runtime/perfmon.c`, `runtime/perfmon.h`, `runtime/ui/menu.cpp` |
| Roboto Medium font (Dear ImGui's `misc/fonts/Roboto-Medium.ttf`, unmodified) | https://fonts.google.com/specimen/Roboto, via https://github.com/peiikai/LASRecomp @ `da35b83a9611d046a9a681aae668d7085954d37f` (`third_party/imgui/misc/fonts/`), Copyright 2011 Google Inc. | Apache-2.0 | `runtime/ui/fonts/Roboto-Medium.ttf` (licence text in `runtime/ui/fonts/LICENSE-Roboto.txt`), embedded by `runtime/ui/menu.cpp` |

View File

@@ -427,6 +427,13 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &buffer);
check_vk_result(err);
if (err != VK_SUCCESS) // NX2NAVI: as for the memory below
{
buffer = VK_NULL_HANDLE;
buffer_memory = VK_NULL_HANDLE;
buffer_size = 0;
return;
}
VkMemoryRequirements req;
vkGetBufferMemoryRequirements(v->Device, buffer, &req);
@@ -435,8 +442,20 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.allocationSize = req.size;
alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits);
buffer_memory = VK_NULL_HANDLE; // NX2NAVI: the handle freed above is not kept when the allocation fails
err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &buffer_memory);
check_vk_result(err);
// NX2NAVI: out of memory. No buffer, no memory and size 0, so the caller draws nothing this frame and the next
// frame asks again (ImGui_ImplVulkan_RenderDrawData). Unchecked, the mapping below it failed too and the copy of
// the vertices went through a null pointer.
if (err != VK_SUCCESS)
{
vkDestroyBuffer(v->Device, buffer, v->Allocator);
buffer = VK_NULL_HANDLE;
buffer_memory = VK_NULL_HANDLE;
buffer_size = 0;
return;
}
err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0);
check_vk_result(err);
@@ -523,14 +542,25 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size)
CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
// NX2NAVI: a buffer that could not be made (CreateOrResizeBuffer): nothing is drawn this frame
if (rb->VertexBufferMemory == VK_NULL_HANDLE || rb->IndexBufferMemory == VK_NULL_HANDLE)
return;
// Upload vertex/index data into a single contiguous GPU buffer
ImDrawVert* vtx_dst = nullptr;
ImDrawIdx* idx_dst = nullptr;
VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)&vtx_dst);
check_vk_result(err);
// NX2NAVI: a mapping that failed leaves nothing to copy into
if (err != VK_SUCCESS || !vtx_dst)
return;
err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst);
check_vk_result(err);
if (err != VK_SUCCESS || !idx_dst)
{
vkUnmapMemory(v->Device, rb->VertexBufferMemory);
return;
}
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* draw_list = draw_data->CmdLists[n];