mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
A thread samples every two seconds the video memory budget against this process's use (vk_mem_vram) and the system's commit headroom and free physical memory (plat_commit_info, plat_mem_info), and turns each side into a level: roomy, normal, tight or critical, with five points of hysteresis upward and a 256 MiB floor. Changes are logged, and the memory report's system line ends with both levels. MEM_BUDGET_TEST_VRAM_MB and MEM_BUDGET_TEST_RAM_MB pretend a side is that small, so that what gives memory back can be tried on a large machine; the texture cache's rules are unchanged. tests/mem_budget_check.py checks the levels and the hysteresis. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
4.0 KiB
C
67 lines
4.0 KiB
C
// How much room the card and the system have, as one level per side (mem_budget.c): roomy, normal, tight or critical.
|
|
// Owners of memory that can be given back read the level and give back earlier when room is short; what they keep
|
|
// while there is room is theirs to decide. Header-only part: the arithmetic, which tests/mem_budget_check.c runs.
|
|
//
|
|
// A side's room is its free share in per mille: for the video memory the driver's budget less this process's use of
|
|
// the device-local heaps (vk_mem_vram); for the system the lower of the commit's headroom (the commit limit less the
|
|
// system's commit in use: an allocation fails at the limit) and the physical memory free (plat_commit_info,
|
|
// plat_mem_info). The levels begin at fixed shares going down, and a side climbs back to a better level only once its
|
|
// share is MEM_HYST_PM above the line it fell through, so a share that hovers at a line does not flip the level at
|
|
// every sample. Below MEM_FLOOR_MB free a side is critical whatever its share.
|
|
#pragma once
|
|
#include <stdint.h>
|
|
|
|
enum { MEM_ROOMY, MEM_NORMAL, MEM_TIGHT, MEM_CRITICAL, MEM_LEVELS };
|
|
enum { MEM_VRAM, MEM_RAM, MEM_SIDES };
|
|
|
|
// The free share (per mille) above which a level holds, from the best down: roomy above 400 (40% free), normal above
|
|
// 200, tight above 80; critical at 80 or less. The 20% line is where vk_texture.c's VRAM pressure rule
|
|
// (tex_vram_pressure, 80% of the budget in use) starts to act on released textures.
|
|
static const uint32_t mem_level_line[MEM_LEVELS - 1] = {400, 200, 80};
|
|
#define MEM_HYST_PM 50 // a level is left for a better one 5 points above the line it fell through
|
|
#define MEM_FLOOR_MB 256 // less free than this is critical at any share
|
|
|
|
static const char *const mem_level_name[MEM_LEVELS] = {"roomy", "normal", "tight", "critical"};
|
|
|
|
typedef struct {
|
|
uint32_t free_pm; // free share of the limit, per mille
|
|
uint64_t free_mb; // free MiB
|
|
} MemRoom;
|
|
|
|
// The room of a side with used of limit in use (any one unit, here MiB); none when used reaches the limit.
|
|
static inline MemRoom mem_room(uint64_t used, uint64_t limit) {
|
|
MemRoom r = {0, 0};
|
|
if (!limit || used >= limit) return r;
|
|
r.free_mb = limit - used;
|
|
r.free_pm = (uint32_t)(r.free_mb * 1000 / limit);
|
|
return r;
|
|
}
|
|
|
|
// The tighter of two measures of one side: the lower share, and the lower free amount.
|
|
static inline MemRoom mem_room_min(MemRoom a, MemRoom b) {
|
|
MemRoom r = {a.free_pm < b.free_pm ? a.free_pm : b.free_pm, a.free_mb < b.free_mb ? a.free_mb : b.free_mb};
|
|
return r;
|
|
}
|
|
|
|
// The level a side moves to from `level` at room r: down to a worse level at once, up only past the hysteresis.
|
|
static inline int mem_level_next(int level, MemRoom r) {
|
|
int raw = 0;
|
|
while (raw < MEM_LEVELS - 1 && r.free_pm <= mem_level_line[raw]) raw++;
|
|
if (r.free_mb < MEM_FLOOR_MB) raw = MEM_CRITICAL;
|
|
if (raw >= level) return raw;
|
|
int n = level; // climb one level at a time while the share clears the line above with room to spare
|
|
while (n > raw && r.free_pm > mem_level_line[n - 1] + MEM_HYST_PM) n--;
|
|
return n;
|
|
}
|
|
|
|
// ---- mem_budget.c: the levels, sampled every MEM_BUDGET_PERIOD_MS on a thread of their own, off the drawing threads.
|
|
// Each change of a side's level is logged with its figures. MEM_BUDGET_TEST_VRAM_MB and MEM_BUDGET_TEST_RAM_MB (0:
|
|
// off) pretend a side is that small, for testing what gives memory back on a large machine: the video memory budget
|
|
// becomes at most that many MiB against this process's use, and the system's room becomes that many MiB less this
|
|
// process's commit. They change the levels only, never the texture cache's own rules.
|
|
#define MEM_BUDGET_PERIOD_MS 2000
|
|
void mem_budget_vram(int on); // vk_device.c: 1 once the device is up (the first call starts the thread), 0 before
|
|
// it goes (waits for a sample under way)
|
|
int mem_budget_level(int side); // MEM_ROOMY .. MEM_CRITICAL of MEM_VRAM or MEM_RAM; MEM_NORMAL before its first sample
|
|
void mem_budget_text(char *out, unsigned cap); // "video memory normal (31.2% free), system memory roomy (55.0% free)"
|