Управление памятью С++ для потоковой передачи текстур в видеоиграх
это "жесткий" вопрос. Я не нашел ничего интересного в Интернете.
Я разрабатываю модуль управления памятью для своей компании. Мы разрабатываем игры для консолей следующего поколения (Xbox 360, PS3 и ПК... мы рассматриваем ПК консоль!).
В будущих играх нам понадобится обработать потоки текстур для больших игровых миров, которые не могут быть загружены в основную консольную память (не говоря уже о ПК на данный момент).
Мы собираемся передать в начале hi-res мипмапы текстур (что составляет около 70% от объема мировых данных). Может быть, в будущем нам придется потопить также геометрию, меньшие мип-карты, аудио и т.д.
Я разрабатываю диспетчер памяти для этой проблемы, ориентированный на X360 (потому что на PS3 мы можем использовать память хоста и связанный с ним автодефракционный GMM-распределитель).
Проблема, с которой я сталкиваюсь, заключается в следующем: мы решили зарезервировать определенную область памяти для потоковой передачи текстур (например, 64 мегабайта), и мы хотим обрабатывать все распределения и освобождения от необходимости в этой области. Мы выделили область в начале приложения, и область физически гарантирована быть смежной (не только практически, потому что нам нужно хранить текстуры там).
Я реализовал автоматический дефрагментационный распределитель, используя дескрипторы вместо указателей. Время не проблема, проблема в фрагментации памяти. В игре мы постоянно загружаем и выгружаем потоковые цели, поэтому мы хотели бы использовать максимальный объем нашего буфера (64 мегабайта).
С помощью этого распределителя мы можем использовать все выделенное пространство, но подпрограмма дефрагментации работает в неаккуратное время (иногда 60 миллисекунд, больше, чем кадры!), в то время как алгоритм не так уж плох... есть слишком чересчур непредсказуемые тетср!
Я ищу решение для решения этой проблемы. Я бы хотел найти хотя бы хорошую бумагу или посмертное слово или кого-то, у кого была та же проблема.
Теперь я выбираю между двумя стратегиями:
1) переместите подпрограмму дефрагментации на выделенный поток (хорошо для X360 с потоками 6 hw, плохо для PS3 с потоком hw... и не говорите мне использовать SPU!) Со всеми проблемами многопоточности областей блокировки, доступ к региону, который перемещается,...
2) найти "инкрементное" решение проблемы дефрагментации: мы можем предоставить каждому кадру временный бюджет (например, до 1 миллисекунды) для дефрагментации, а диспетчер памяти будет делать то, что он может сделать в бюджете каждого кадра.
Может ли кто-нибудь рассказать мне о своем опыте?
Ответы
Ответ 1
Недавно я много изучил управление памятью, и это самая информативная и полезная статья, которую я нашел в сети.
http://www.ibm.com/developerworks/linux/library/l-memory/
Основываясь на этой статье, лучший и быстрый результат, который вы получите, состоит в том, чтобы разделить 64 МБ на куски равного размера. Размер кусков будет зависеть от размера вашего объекта. И выделять или освобождать полный кусок за раз. Это
- Быстрее, чем инкрементная сборка мусора.
- Simpler.
- И решает эту проблему "слишком много фрагментации" на какую-то сумму.
Прочтите, вы найдете отличную информацию о каждом возможном решении, которое есть, и достоинствах и недостатках для каждого.
Ответ 2
Почему бы не использовать несколько областей памяти для потоковых текстур и пула по размеру текстуры?
Insomniac имеет документ об их реализации потоковой передачи текстур на PS3. Я полагаю, это может быть полезно: ссылка.
Для общих стратегий распределения, чтобы минимизировать фрагментацию, может помочь Doug Lea.
Но из моего чтения вашего вопроса, похоже, вы переусердствовали, и я настоятельно рекомендую использовать объединенный подход. (Кроме того, запуск дефрагментационного прохода в памяти с записью не звучит особенно безопасно или забавно.)
Ответ 3
Поскольку вы используете ручки, у вас есть много свободы для перемещения памяти. Я думаю, что использование отдельного потока, вероятно, не самый лучший (самый безопасный или быстрый) способ - думаю, вам лучше использовать тип инкрементного распределителя копирования, где на каждом malloc()
или free()
вы компактны ( копировать вперед или назад в памяти) некоторое количество выделенных блоков с количеством байтов, которое вы копируете, истощая "бюджет", который периодически reset, до его начального значения (например, при каждом обновлении экрана). (Конечно, копируются только целые блоки.)
Идея состоит в том, что копирование заданного количества байтов занимает довольно предсказуемое количество времени, поэтому вы можете оценить, сколько байтов копирования вы можете безопасно выполнять на обновление экрана, и ограничиться этим. Если в бюджете достаточно времени, вызов malloc()
или free()
будет полностью дефрагментировать память, в противном случае он будет дефрагментировать его как можно больше в заданных временных ограничениях.
Есть некоторые вопросы, которые я оставляю здесь нерешенными - например. точно как сжать память. Стандартный неинкрементный копировальный распределитель может просто начать выделять с фронта, а затем копировать все в спину (освобождая память спереди), когда память заканчивается, но у вас нет этой свободы здесь. Возможно, вам понадобится эвристика, чтобы решить, нужно ли перемещать блоки вперед или назад. Важно избегать колебаний (один и тот же блок перемещается вперед, а затем назад при последовательных вызовах на malloc()
или free()
).
Ответ 4
Мы почти точно описали систему, за исключением того, что мы выделяем в слоты фиксированного размера - 256x256, 512x512, 1024x1024 и 2048x2048 текстуры в двух форматах каждый (DXT1 и DXT5) - чтобы избежать управления памятью.
Ответ 5
Я бы рекомендовал постепенный подход. Каждый кадр находит непрерывный блок памяти inuse, который имеет свободное пространство с обеих сторон и перемещает его в зависимости от того, какое из направлений позволит ему поместиться. или вы можете просто переместить все блоки в одном направлении, найти зазор и использовать блок, который лучше всего подходит для него и переместить его. На 360 вам, вероятно, следует использовать поток для перемещения, в то время как на PS3 лучше всего использовать графический процессор для перемещения данных вокруг вас.