C ++ Управление памятью для потоковой передачи текстур в видеоиграх - PullRequest
20 голосов
/ 19 марта 2009

это "сложный" вопрос. Я не нашел ничего интересного в сети.

Я разрабатываю модуль управления памятью для своей компании. Мы разрабатываем игры для консолей следующего поколения (Xbox 360, PS3 и ПК ... мы считаем ПК консолью!).

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

Мы начнем потоковую передачу в начале изображений в высоком разрешении (это около 70% размера мировых данных). Возможно, в будущем нам также понадобится потоковая передача геометрии, небольших карт, аудио и т. Д.

Я занимаюсь разработкой диспетчера памяти для этой проблемы, ориентированной на X360 (потому что на PS3 мы можем использовать память хоста и связанный с ним, автоматически дефрагментирующий распределитель GMM).

Проблема, с которой я сталкиваюсь, заключается в следующем: мы решили зарезервировать определенную область памяти для потоковой передачи текстур (например, 64 мегабайта), и мы хотим обрабатывать все выделения и освобождения в этой области. Мы выделили область в начале приложения, и физически гарантируется, что эта область является смежной (а не только виртуальной, поскольку нам нужно хранить там текстуры).

Я реализовал автофрагментатор с использованием маркеров вместо указателей. Время не проблема, проблема фрагментации памяти. В игре мы постоянно загружаем и выгружаем потоковые цели, поэтому мы хотели бы использовать максимальный объем нашего буфера (64 мегабайта).

С помощью этого распределителя мы можем использовать все выделенное пространство, но процедура дефрагментации работает в недопустимое время (иногда 60 миллисекунд, больше, чем кадры!), В то время как алгоритм не так уж плох ... слишком много неизбежного неизбежно тетср!

Я ищу решение для решения этой проблемы. Я хотел бы найти хотя бы хорошую газету, или вскрытие, или кого-то, кто сталкивался с той же моей проблемой.

Теперь я выбираю между двумя стратегиями: 1) перенести процедуру дефрагментации на выделенный поток (хорошо для X360 с потоками 6 hw, плохо для PS3 только с потоком hw ... и не говорите мне использовать SPU!) Со всеми проблемами многопоточности блокирующих областей, доступ к региону, который перемещается, ... 2) найти «инкрементное» решение проблемы дефрагментации: мы можем дать каждому кадру бюджет времени (например, до 1 миллисекунды) для дефрагментации, и диспетчер памяти будет делать то, что он может делать в бюджете каждого кадра.

Может кто-нибудь рассказать мне о своем опыте?

Ответы [ 5 ]

14 голосов
/ 19 марта 2009

Недавно я много изучал вопросы управления памятью, и это самая информативная и полезная статья, которую я нашел в сети.

http://www.ibm.com/developerworks/linux/library/l-memory/

Исходя из этой бумаги, лучший и быстрый результат, который вы получите, - это разделить ваши 64 МБ на куски одинакового размера. Размер кусков будет зависеть от размера вашего объекта. И выделять или освобождать полный кусок за раз. Это

  1. Быстрее, чем пошаговая сборка мусора.
  2. Simpler.
  3. И решает эту проблему "слишком большой фрагментации" на некоторое количество.

Прочтите его, вы найдете отличную информацию о всех возможных решениях, а также о достоинствах и недостатках каждого.

5 голосов
/ 20 марта 2009

Почему бы не использовать несколько областей памяти для потоковых текстур и пул по размеру текстуры?

В Insomniac есть статья о реализации потоковой передачи текстур на PS3. Я полагаю, это может быть полезно: ссылка .

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

Но из-за того, что я прочитал ваш вопрос, звучит так, как будто вы его обдумываете, и я настоятельно рекомендую объединенный подход. (Также выполнение прохода дефрагментации в памяти с комбинированной записью не кажется особенно безопасным или забавным.)

2 голосов
/ 12 июля 2009

У нас есть почти точно та система, которую вы описали, за исключением того, что мы размещаем ее в слотах фиксированного размера - текстурах 256x256, 512x512, 1024x1024 и 2048x2048, в двух форматах каждый (DXT1 и DXT5) - именно для того, чтобы избежать управления памятью.

2 голосов
/ 19 марта 2009

Поскольку вы используете дескрипторы, у вас есть много свободы для перемещения памяти. Я думаю, что использование отдельного потока, вероятно, не самый лучший (самый безопасный или самый быстрый) способ - я думаю, что вам лучше использовать тип распределителя инкрементного копирования, где на каждом malloc() или free() вы сокращаете ( копировать вперед или назад в памяти) определенное количество выделенных блоков с количеством копируемых байтов, уменьшая «бюджет», который периодически сбрасывается до исходного значения (например, при каждом обновлении экрана). (Конечно, копируются только целые блоки.)

Идея состоит в том, что копирование заданного количества байтов занимает довольно предсказуемое количество времени, поэтому вы можете оценить, сколько байтов копирования вы можете безопасно выполнить за обновление экрана, и ограничиться этим. Если в бюджете достаточно времени, вызов malloc() или free() полностью дефрагментирует память, в противном случае он будет максимально дефрагментировать ее с учетом временных ограничений.

Есть некоторые вопросы, которые я оставляю здесь нерешенными - например, как именно сжать память. Стандартный распределитель неинкрементного копирования может просто начать выделение с лицевой стороны, а затем скопировать все на заднюю часть (освобождая память на лицевой стороне), когда память заканчивается, но у вас нет этой свободы здесь. Вам может потребоваться некоторая эвристика, чтобы решить, перемещать ли блоки вперед или назад. Важно избегать колебаний (один и тот же блок перемещается вперед, а затем назад при последовательных вызовах на malloc() или free()).

1 голос
/ 20 марта 2009

Я бы рекомендовал поэтапный подход. Каждый кадр находит непрерывный блок памяти, который имеет свободное пространство с обеих сторон, и перемещает его в любом направлении, которое позволит ему поместиться. или вы можете просто переместить все блоки в одном направлении, найти промежуток и блок, который лучше всего подходит для него, и переместить его. На 360 вы, вероятно, должны использовать поток, чтобы сделать перемещение, в то время как на PS3 было бы лучше использовать графический процессор для перемещения данных за вас.

...