xash3d-fwgs icon indicating copy to clipboard operation
xash3d-fwgs copied to clipboard

Textures Manager

Open vklachkov opened this issue 2 years ago • 52 comments

Менеджер текстур

Цель

Данный PR нацелен на создание менеджера текстур для упрощения рендеров и избавления от дублирующего кода

Мотивация

Впервые с необходимостью в отдельном менеджере я столкнулся при переключении рендера на лету. Перезагрузка ресурсов, в частности, текстур -- краеугольный камень для смены рендера без перезапуска движка. В своём черновике для принудительного обновления текстур были применены грязные хаки с флагами и отключением важных проверок. Желая избавится от них была начата работа в этом направлении

Перспектива

Менеджер преимущественно разрабатывается на перспективу. В недалёком будущем планируется:

  • Вынос системы патчей материалов из ref_vk в менеджер ресурсов
  • Реализовать переключение рендеров. Meta issue с обобщением опыта будет в скором времени
  • Доработать менеджер текстур до полноценного менеджера ресурсов

План

Работу планирую разделить на два глобальных этапа

Этап 1

Этап нацелен на создание дублирующего слоя: менеджера текстур, работающего полностью идентично текущему

  • [x] Создать прокси между движком и рендером, для последующей реализации менеджера текстур
  • [x] Повторить загрузку, хранение текстур
  • [x] Повторить генерацию текстур-заглушек из рендера
  • [x] Восстановить недостающий код флагов
  • [x] Починить сломанные спрайты
  • [x] Починить сломанные модели и декали
  • [x] Починить сломанные лайтстили
  • [x] Реализовать очистку памяти
  • [ ] ~~Реализовать релокацию, если текстур больше, чем 4096~~ (пока нет необходимости)
  • [x] Прокидывание методов менеджера ресурсов в ref_api
  • [x] Прокидывание методов менеджера ресурсов в render_api

Этап 2

Этап нацелен на фиксы и облегчение ref_gl. Этот рендер стабилен, реализует все возможности движка и идеально подходит для экспериментов

  • [x] Удалить хранение текстур
  • [x] Удалить генерацию текстур-заглушек

Второй этап может выполняться параллельно с первым. Но потребует чужой помощи. Я не разбираюсь в рендерах. И патчинг ref_gl займёт у меня времени больше, чем у знающего человека

vklachkov avatar Jan 14 '22 00:01 vklachkov

Это как-то повлияет на рендеры, реализованные внутри клиентской игрокой библиотеки через RenderAPI?

SNMetamorph avatar Jan 14 '22 00:01 SNMetamorph

Это как-то повлияет на рендеры, реализованные внутри клиентской игрокой библиотеки через RenderAPI?

~~Возможно, повлияет. Не думал, не тестировал. Если требуется, протестирую и поддержу~~

Нет, не повлияет. Никаких изменений в render api я не вношу принципиально. Сейчас он будет работать некорректно: проксирование методов мной не реализовано. Это сделаю позднее, как дойду до работ в рендере

vklachkov avatar Jan 14 '22 00:01 vklachkov

@SNMetamorph рендеры наверное зовут что-то типа ref.dllFuncs.GL_LoadTexture? тогда менеджер текстур по идее это перехватывает.

0x4E69676874466F78 avatar Jan 14 '22 01:01 0x4E69676874466F78

Эксперименты в mainui попали в данный PR случайно

vklachkov avatar Jan 14 '22 03:01 vklachkov

Сейчас глитчи с текстурами стен происходят из-за расхождения texnum в рендере и моём менеджере текстур. Расхождение возникает из-за функции Mod_ProcessRenderData, которая подгружает текстуры спрайтов. Если её захачить, то глитчи уходят, но и весь движок падает через несколько секунд

image

Я вижу несколько сценариев, как поступать с функцией Mod_ProcessRenderData.

Первый. Закрыть глаза на глитчи, мёрж сделать в отдельную ветку resman. И при дальнейшей реализации вынести загрузку спрайтов в двиг, за одно решив эту проблему

Второй. Вытащить функцию и всё ей сопутствующее в двиг, превратив PR в Resource Manager. Я попытался на скорую руку, но там очень много кода. Неизвестно, что ещё придётся распутать

Третий. Пока что захачить texnum, чтобы он всегда был идентичен рендеровскому до тех пор, пока не будет удалён менеджер текстур в рендере

Четвёртый. Добавить методы менеджера ресурсов в gEngfuncs, пропатчив рендер так, чтобы он подгружал текстуры через двиг, а не напрямую в себя. Тупо, но практично

Как поступить лучше всего, @a1batross? Мне по душе четвёртый сценарий. Ибо он проще всего и не создаст много задач одномоментно

vklachkov avatar Jan 14 '22 04:01 vklachkov

В каком состоянии находится функция Mod_ProcessRenderData? Часть веток закомментированна без комментариев. Саму функцию дёргают непонятно кто и непонятно откуда в больших количествах. Я не могу распутать клубок вызовов(

image

При смене рендера я всё это хачил. Много, муторно, методично. Сейчас же хочу разобраться и сделать более-менее красиво

vklachkov avatar Jan 14 '22 04:01 vklachkov

Дополнительно отмечу, что где-то утекает ref_api.h. Если его подключить несколько раз подряд, случается такое на несколько экранов:

image

vklachkov avatar Jan 14 '22 04:01 vklachkov

Лучше за основу как раз брать ref_gl, а ref_vk уже подпиливать. Я в целом пока рекомендую забить на существование ref_vk, неизвестно насколько корректно он реализован.

Mod_ProcessRenderData исключительно рендероспецифичная. То что она наполовину закомментирована ничего страшного -- значит ничего делать. Вызывается при загрузке и выгрузке модели.

a1batross avatar Jan 14 '22 10:01 a1batross

Лучше за основу как раз брать ref_gl, а ref_vk уже подпиливать. Я в целом пока рекомендую забить на существование ref_vk, неизвестно насколько корректно он реализован

За основу взят общий код для обоих рендеров

Mod_ProcessRenderData исключительно рендероспецифичная. То что она наполовину закомментирована ничего страшного -- значит ничего делать. Вызывается при загрузке и выгрузке модели.

Понял. Как мне лучше поступить с ней? Четыре сценария изложил выше

vklachkov avatar Jan 14 '22 14:01 vklachkov

Ничего не делать. Это загрузка моделей, она не связана с текстурами.

Возможно, лучше завести новый коллбэк, который будет оповещать рендер что пора загрузить текстуру в видеопамять и присвоить ей рендероспецифичный handle.

a1batross avatar Jan 15 '22 00:01 a1batross

Ничего не делать. Это загрузка моделей, она не связана с текстурами.

Вы не правы. При загрузке моделей происходит загрузка и текстур через функцию R_SpriteLoadFrame (см. gl_sprite.c:50). Происходит это в обход движка. И именно это я пытаюсь решить. То ли хачить пока не поправлю рендер, то ли прокидывать менеджер ресурсов в рендер

Возможно, лучше завести новый коллбэк, который будет оповещать рендер что пора загрузить текстуру в видеопамять и присвоить ей рендероспецифичный handle.

Да, так и будет сделано, как буду вырезать управление текстурами из рендера. Сейчас я этого не делаю для упрощения отладки

vklachkov avatar Jan 15 '22 00:01 vklachkov

Ничего там в обход не происходит. У тебя уже есть LoadTexture в движке, пусть он движковый и вызывает.

a1batross avatar Jan 15 '22 00:01 a1batross

После коммита d50edac ушли глитчи из меню в рендере ref_soft. Сами уровни не запускал, ибо ref_soft не работает на x64 и пока не удалось его пропатчить

image

vklachkov avatar Jan 16 '22 01:01 vklachkov

После коммита 54fc8a0 починились лайтмапы на GL

image

На карте test_brush всё ещё наблюдаются глитчи. Надо гонять статические анализаторы и санитайзеры. Похоже на утечку памяти, коих на этой карте стреляет очень много на ref_vk

vklachkov avatar Jan 16 '22 01:01 vklachkov

Возможно, всё же, где то поплыли индексы, судя по редким глитчам в меню. По логам не вижу, буду исследовать позднее, в процессе патчей ref api

vklachkov avatar Jan 16 '22 01:01 vklachkov

Удалось зафиксить ref_soft на x64, успешно работает с менеджером текстур Больше тестов красивых и разных для менеджера

image

vklachkov avatar Jan 16 '22 01:01 vklachkov

Как полностью удалишь менеджер текстур из ref_gl, то и индексы начнут совпадать.

Дело все в том, что загрузка текстур в нём почему-то игнорирует вызов glCreateTextures и в качестве texnum берёт порядковый номер загруженной текстуры. Про это поведение в спеках OpenGL я находил целое ничего, так что удивительно что оно вообще работает.

a1batross avatar Jan 16 '22 07:01 a1batross

Как полностью удалишь менеджер текстур из ref_gl, то и индексы начнут совпадать

Они и сейчас должны совпадать. Но я согласен, что после удаления проблема уйдёт точно. Надеюсь, в ближайшие пару дней у меня получится придти к этому. Сначала нужно перенести текстуры-заглушки, флаги и поправить работу с хэш мапой

Дело все в том, что загрузка текстур в нём почему-то игнорирует вызов glCreateTextures и в качестве texnum берёт порядковый номер загруженной текстуры. Про это поведение в спеках OpenGL я находил целое ничего, так что удивительно что оно вообще работает

Повеселил! Хоть не я один не понимаю, что вообще происходит в рендере и как оно работает

А ведь я хочу перед сменой рендера ещё попытаться в ref_common...

vklachkov avatar Jan 16 '22 08:01 vklachkov

Дело все в том, что загрузка текстур в нём почему-то игнорирует вызов glCreateTextures и в качестве texnum берёт порядковый номер загруженной текстуры. Про это поведение в спеках OpenGL я находил целое ничего, так что удивительно что оно вообще работает

Иван @w23 ответил следующее:

https://www.khronos.org/registry/OpenGL/specs/gl/glspec21.pdf раздел 3.8.12, страница 182 (196 в пдф) The name space for texture objects is the unsigned integers, with zero reserved by the GL. A texture object is created by binding an unused name to TEXTURE 1D, TEXTURE 2D, TEXTURE 3D, or TEXTURE CUBE MAP.

glCreateTextures издревле был не то, чтобы очень нужен. это просто соломка, если тебе влом самому за интами следить

vklachkov avatar Jan 16 '22 08:01 vklachkov

Спустя продолжительный перерыв вернулся к работе над PR.

В коммите 8b2e6f3 я объединил сразу несколько важных изменений:

  • Удаление менеджмента текстур из ref_gl Теперь API упрощено до методов загрузки и удаления текстуры по индексу, всем прочим занят менеджер Пока пришлось выключить ref_soft, ибо он не адаптирован под изменения.
  • Переработка хэш таблицы в менеджере текстур Изначально реализация была перенесена из рендера, но с ней были странные проблемы. Проще было написать свою несложную, чем выяснять-чинить
  • Реализация удаления текстур в менеджере

Всё в совокупности дало результат, идентичный натуральному. Ничего не падает, явным образом не глитчует.

Остаются проблемы:

  • Не реализованы текстуры-заглушки
  • Не перенесены флаги
  • Не вынесены хитрые манипуляции в менеджер текстур

Теперь нужно решить, как тестировать реализацию. @a1batross, подскажи, пожалуйста, как выявить регрессии? Временно использую тестовые карты от @0x4E69676874466F78, но и они не абсолют

vklachkov avatar Apr 23 '22 18:04 vklachkov

@zgdump тестирование на регрессии у нас нет.

Сейчас ручная работа. Берёшь порт, берёшь мод и тестируешь. Впрочем, если в Half-Life, PrimeXT, Paranoia 2 всё в порядке, то высока вероятность что заработает скорее всего везде.

a1batross avatar Apr 24 '22 12:04 a1batross

Навскидку можно пробежать моды под GS и под ксашем из этого списка: https://pastebin.com/tJseQ7PG

nekonomicon avatar Apr 24 '22 13:04 nekonomicon

После реализации стандартных текстур, внезапно, стало очень хорошо. Ушли ошибки, пропали глитчи с лайтмап. Во текущем виде, менеджер текстур достаточно стабилен, основные задачи выполнены

image

Далее нужно пройти несколько игр на этой реализации, исправляя все найденные проблемы. Отрефакторить код, пройтись анализатором, починить утечки. И восстановить недостающие методы

vklachkov avatar May 04 '22 00:05 vklachkov

Да, встроенные текстуры важны. Важно ещё соблюдать им имена, но я их все вынес в RefAPI.

Надо пожалуй перейти на твою ветку и дорабатывать её до мержабельного (от слова морж походу) состояния.

a1batross avatar May 04 '22 22:05 a1batross

Да, встроенные текстуры важны. Важно ещё соблюдать им имена, но я их все вынес в RefAPI.

В ref_api.h добавил ещё *cintexture и *dlight, почему-то они не были задефанены и в ref_gl использовались строками.

Из всех стандартных текстур не перенёс solid_sky и alpha_sky. Код для генерации в gl_warp.c:652. Испужался, оставил на когда-нибудь)

Надо пожалуй перейти на твою ветку и дорабатывать её до мержабельного (от слова морж походу) состояния.

Про моржа не понял. Если поможешь, буду крайне признателен. Тут нужны и тесты, и почин под linux (f ansient c), и напильник

vklachkov avatar May 05 '22 07:05 vklachkov

С текущей архитектурой менеджера текстур есть одна весомая проблема. Все текстуры хранятся в памяти, если, допустим, мод использует текстур на пару гигабайт, то всё это будет висеть в памяти игры просто ради того, чтобы рендер можно было менять на ходу. Мне кажется, стоит сделать это поведение конфигурируемым, далеко не всегда рационально жертвовать оперативной памятью ради горячей смены рендеров. Я предлагаю сделать возможность со стороны клиента игры отключить такое поведение, либо чтобы это конфигурировалось кваром.

SNMetamorph avatar May 05 '22 10:05 SNMetamorph

Продублирую мысли, сформулированные в Discord.

Хранение оригиналов текстур не моя идея. Так было сделано до меня в ref_gl, это же перекочевало в ref_soft и ref_vk. И это не просто архитектурное решение, но ий инвариант, на который полагается логика.

Для смены рендера оригиналы хранить вовсе не обязательно. Достаточно знать имена текстур. И при смене рендера они будут вновь прогружены из файловой системы

vklachkov avatar May 05 '22 10:05 vklachkov

Я попробовал на скорую руку пропатчить ref_gl и убрать хранение оригинальной текстуры. Для этого мне пришлось:

  • Захачить GL_ProcessTexture
  • Захачить GL_ProcessImage
  • Убрать метод GL_GetTextureOriginalBuffer из ref_api

И оно собралось, заработало, но буквально на первом повороте уровня c1a1d взорвалось в кишках:

image

Безусловно, уверен, всё можно изменить, поправить, исправить, закрыть глаза на патчи GAME_EXPORT функций. Вопрос трудозатрат

Как компромисс с @SNMetamorph могу сделать опцию для менедера текстур, чтобы оригинал отчищался сразу после загрузки, а в original хранился NULL

vklachkov avatar May 05 '22 10:05 vklachkov

Хе-хе. Вы что-то не то делаете с хранением оригинальной текстуры или я вас неправильно понимаю.

Во-первых, гигабайты текстур мода и правда не должны храниться в оперативной памяти. Но есть один нюанс, о нём чуть позже.

Во-вторых, запрос хранения оригинала текстур исходит не от пользователя и даже не от разработчика мода, а от того кто пишет рендерер или собственно разрабатывает движок.

Есть у загрузчика флаг TF_KEEP_SOURCE. Где он используется вам расскажет код, но навскидку -- он нужен для ремаппинга палитры, баловства, типа чтобы вписывать номер билда в Quake и самое важное, тот самый нюанс о котором я говорил -- ENGINE_IMPROVED_LINETRACE. Это трейс через дырчатые текстуры. Это может занимать гигабайты, но разработчик мода сам запросил эту фичу движка и знал на что идёт, если хотел чтобы трейс, учитывал дырчатые текстуры, типа сеточек, заборов и прочего.

И только на этот флаг и надо ориентироваться. Задача же горячей смены рендерера -- это то, что нужно выделить за scope этого гигантского патча, а не пытаться решать всё подряд и запутываться в своём же собственном коде.

a1batross avatar May 05 '22 11:05 a1batross

@a1batross

Со сменой рендера на лету будем разбираться и правда потом. Нам бы со текстурнёй разобраться. Патч маленький, но его бы победить для начала

Со хранением, кажется, я сам запутался. Обращение внимания на флаг TF_KEEP_SOURCE меня расшатало, сообразил. Мне не нужно хранить оригинал. Это должен делать сам рендер, если выставлен флаг. Это объясняет мне FS_CopyImage. Спасибо. Простите, если кого запутал

vklachkov avatar May 05 '22 13:05 vklachkov