На прошлой неделе я разбирал место на диске и заметил папку, которая жрала почти 100 ГБ на SSD. Не игра. Не видеопроект. Не образы Docker. Папка с кешем, которую я в жизни не открывал:
C:\Users\<Username>\AppData\Local\NVIDIA\DXCache
Девяносто с лишним гигабайт файлов с расширением .nvph и бессмысленными именами, которые тихо лежали на диске неизвестно сколько времени.
Что там на самом деле лежит
Когда запускается игра, драйвер видеокарты компилирует шейдеры — небольшие программы, которые говорят GPU, как рендерить освещение, материалы, эффекты и так далее. Компиляция «на лету» вызывает подтормаживания, поэтому драйверы кешируют результат компиляции на диск. При следующем запуске той же игры загружается уже готовый скомпилированный шейдер вместо повторной компиляции — и всё работает плавнее.
Сама по себе эта часть — не проблема. Проблема в том, что происходит дальше — а ещё в том, почему кешировать стало намного больше, чем раньше.
Почему это стало намного серьёзнее с современными движками
Если тебе кажется, что подтормаживания из-за компиляции шейдеров стали куда заметнее в последние пару лет — это не только ощущение. У игр на Unreal Engine 4 эта проблема тоже была, просто она была куда тише, и на то есть конкретные причины:
- DX11 против DX12/Vulkan. Большинство игр на UE4 выходили на DX11, где компиляцию шейдеров берёт на себя драйвер и во многом скрывает её стоимость. UE5 гораздо сильнее толкает разработчиков в сторону DX12 и Vulkan, а на этих API компиляция Pipeline State Object (PSO) — это задача движка, а не драйвера. Epic сама открыто говорила, что подтормаживания из-за создания PSO — неотъемлемое свойство именно этих API, в отличие от DX11.
- Гораздо больше комбинаций PSO. Nanite и Lumen добавляют динамическое освещение и материалы, которые порождают намного больше уникальных комбинаций шейдеров/состояний конвейера, чем более статичные пайплайны рендеринга в UE4. Больше уникальных PSO — значит больше первых компиляций «на лету», прямо во время игры, а это и есть тот самый видимый фриз.
- Старое решение было ручным и неполным. В UE4 разработчики вручную записывали и зашивали PSO в билд заранее. Работало, но было утомительно для крупных проектов и всё равно оставляло пробелы — сама Epic признавала, что процесс «could be burdensome for large projects, and still leave gaps in the cache leading to hitches» («может быть обременительным для крупных проектов и всё равно оставлять пробелы в кеше, приводящие к фризам»).
- Epic активно чинит это от версии к версии. В UE5.1 появился Automated PSO Gathering, заменивший ручной процесс, а в UE5.2 добавили возможность пропускать отрисовку объекта, если его PSO ещё не готов, вместо того чтобы стопорить кадр. Это фактическое признание того, что проблема архитектурная, а не разовый баг.
Так что дело не в том, что «в UE4 этого не было», а в том, что «UE4 в основном мог прятать это за DX11, а UE5 своим сдвигом в сторону DX12/Vulkan плюс огромным числом вариаций шейдеров от Nanite/Lumen сделал это невозможным скрыть». А это, в свою очередь, значит: современные игры компилируют и кешируют больше вариантов шейдеров, чем когда-либо — и именно это подпитывает тот самый неуправляемый, вечно растущий кеш, о котором весь этот текст.
О чём никто не говорит
Как только шейдер скомпилирован и закеширован, ничто и никогда его не удаляет — ни когда ты заканчиваешь играть, ни когда удаляешь игру, ни когда обновление драйвера делает старую запись кеша бесполезной. DXCache просто растёт. Всегда. В одну сторону.
И по умолчанию настройка NVIDIA Control Panel, которая за это отвечает — Manage 3D Settings → Размер кэша шейдеров — стоит на «По умолчанию для драйвера». Полный список пунктов в выпадающем списке выглядит так:
- По умолчанию для драйвера
- Отключено
- 128 МБ
- 256 МБ
- 512 МБ
- 1 ГБ
- 5 ГБ
- 10 ГБ
- 100 ГБ
- Без ограничений
Вот что меня удивило: официальная документация NVIDIA о том, во что на самом деле превращается «По умолчанию для драйвера», практически отсутствует. Единственное официальное описание, которое мне удалось найти, — это заметка в патчноутах драйвера 495/496.13, вводившая эту настройку одной фразой: «Added Shader Cache Size control to set the maximum amount of disk space to use for storing shader compiles» («Добавлен параметр Shader Cache Size для задания максимального объёма дискового пространства под хранение скомпилированных шейдеров»). И всё. Ни страницы с объяснением, какому числу соответствует «По умолчанию для драйвера», ни документа про политику вытеснения при достижении лимита, ни слова о том, как это взаимодействует с настройками для конкретных игр. Показательно, что даже разработчики на официальном форуме NVIDIA публично спрашивали, существует ли вообще подробная документация по политике кеширования — и не получили конкретного ответа. Так что «По умолчанию для драйвера» — это не задокументированное значение в духе «это 2 ГБ, вот почему», а чёрный ящик, который на практике ведёт себя так, будто ограничения фактически нет. Что вполне сходится с тем, что у меня накопилось почти 100 ГБ, а этот параметр я вообще никогда не трогал.
Так что математика простая: каждая игра, которую ты когда-либо устанавливал, во что-то поиграл час и удалил, оставила свои «чаевые» в DXCache — и они всё ещё там. Я играл на ПК годами, ни разу не трогая эту настройку, и именно так папка незаметно доходит до 100 ГБ без единого предупреждения, уведомления или подсказки от Windows про очистку диска.
Почему это хуже, чем звучит
Есть несколько моментов, которые делают это особенно коварным, а не просто «ну, кеш есть кеш»:
- Переживает удаление игры. Удаление игры никак не затрагивает её кеш шейдеров. Игры нет — кеш остался навсегда.
- Очистка диска Windows про него не знает. Это не временные файлы в привычном смысле — ими управляет драйвер, и они лежат вне путей, которые Windows обычно предлагает почистить.
- По умолчанию нет лимита. Большинство систем кеширования предполагают, что кто-то рано или поздно поставит им ограничение. Эта — выходит из коробки полностью открытой.
- Невидима, пока сам не пойдёшь искать. Ни в приложении NVIDIA, ни в Windows нет никакого индикатора, который сказал бы, что эта папка существует, не говоря уже о её размере.
Это не баг в смысле «краш». Это архитектурное решение — безлимит по умолчанию плюс отсутствие управления жизненным циклом файлов, — которое тихо ведёт себя как медленная утечка памяти, только на диске, и растягивается на годы вместо часов.
Оговорка про DX12, потому что я сам сначала ошибся
Стоит сразу оговориться: настройка Shader Cache Size в NVIDIA Control Panel в первую очередь управляет путём кеша для DX11 и более старых API. Многие современные игры на DX12 и Vulkan управляют своим кешем шейдеров самостоятельно — иногда в других папках, иногда со своими лимитами размера, а иногда вообще без них. Так что конкретно DXCache в основном забивается играми на DX11, но это не вся картина: если гоняешься за каждым освобождённым байтом, стоит проверить ещё и кеш Vulkan, и папки кеша шейдеров конкретных игр (многие тоже лежат под %LOCALAPPDATA%, просто организованы по-разному в зависимости от движка).
Что я с этим сделал
- Сначала проверил реальный размер. Правой кнопкой → Свойства на папке, или через PowerShell:
Get-ChildItem "C:\Users\<Username>\AppData\Local\NVIDIA\DXCache" -Recurse | Measure-Object -Property Length -Sum
- Удалил содержимое, а не саму папку — NVIDIA пересоздаст её по мере необходимости. Цена очистки — несколько секунд подтормаживания на пересборку шейдеров при следующем запуске каждой игры, что вполне честная плата за 100 ГБ.
- Поставил реальный лимит в NVIDIA Control Panel → Manage 3D Settings → Global Settings → Размер кэша шейдеров. Я выставил 10 ГБ. Достаточно для активных игр, и ограничивает всё, что я с тех пор удалил. Важный момент: как только кеш упирается в этот лимит, драйвер не просто перестаёт добавлять новые записи — он начинает вытеснять старые, чтобы освободить место. Так что если ты одновременно играешь в кучу тяжёлых игр и поставишь лимит слишком маленьким, кеш будет постоянно затираться и пересобираться заново — а это как раз те самые подтормаживания, от которых вся эта система должна была спасать. 10 ГБ хватило для моей библиотеки; если у тебя одновременно установлен десяток крупных игр, может понадобиться запас побольше.
- Сделал себе заметку проверить это ещё раз через полгода, потому что «поставил один раз и забыл» — это именно тот подход, который и довёл меня до 100 ГБ.
Более широкая картина
Это вообще-то не история, специфичная только для NVIDIA, если посмотреть шире. Та же форма проблемы у десятка других тихих пожирателей диска: висячие слои образов Docker, глобальный кеш npm, кеш service worker’ов браузера, кеш wheel-файлов pip. Всё, что придумано для кеширования результата дорогой работы, будет хранить этот результат вечно, если что-то явно не привяжет его срок жизни к тому, что его породило — а «безлимит по умолчанию» — это решение, а не недосмотр, которое кто-то принял и выпустил в продакшен.
Кеш без владельца не падает громко. Он просто лежит и растёт, пока ты не пойдёшь искать пропавшее место на диске и не найдёшь, где оно всё это время пряталось.
Если ты на NVIDIA и ещё не проверял — возможно, стоит заглянуть сюда:
C:\Users\<Username>\AppData\Local\NVIDIA\DXCache
Может удивить, что там найдётся.