Давайте сделаем математику. Ради этого обсуждения мы будем игнорировать размер статической геометрии сетки и других данных, которые не обновляются каждый кадр (например, матрицы обратного связывания, если вы делаете скинг матричной палитры). Насколько велик каждый из этих буферов данных экземпляров скинов?
Instance count * joint count * elements per skinning matrix * bytes per matrix element
50,000 * 50 * 12 * 4 = 120 MB
(я обесцениваю другие вещи, которые могут различаться в каждом кадре, поэтому, если это поможет, замените эти числа порядками величины вашего реальногоданные. Остальная часть анализа все еще применяется.)
Так что да, значительное количество места, но не обязательно невыполнимо. Мы можем выбрать двойную буферизацию вместо тройной буферизации, сокращая общее пространство, требуемое с 360 МБ до 240 МБ.
Мы также предположили, что мы делаем скины матричной палитры, используя матрицы 3х4. Мы могли бы снова сократить необходимое пространство пополам, используя двойную кватернионную скиннинг .
Одна из проблем здесь заключается в том, что передача 60 МБ данных скиннинга на кадр в GPU потребляет большой объем полосы пропускания. если мы работаем на дискретном графическом процессоре. Поэтому мы могли бы рассмотреть возможность переноса наших совместных расчетов в GPU. На iOS с архитектурой с разделяемой памятью это менее важно. В любом случае, мы захотим разбираться в том, как мы вычисляем нашу иерархию преобразований, так как мы собираемся делать многократное умножение, если каждое соединение перемещается в каждом кадре.
Что касается изменяющегося количества экземпляров, тоодна вещь, которую мы определенно не хотим делать, это перераспределять новые буферы в каждом кадре. Вместо этого мы можем перераспределить буфер, увеличив его размер геометрически , когда мы заметим, что число экземпляров превышает текущую максимальную емкость наших буферов.
В качестве альтернативы, мы могли бы использовать «постраничный» подход и разделить экземпляры по нескольким буферам, каждый из которых может содержать несколько экземпляров с большим числом элементов, и которые могут быть переработаны путем однократного добавления их в пул буферов многократного использования. GPU делает рендеринг от них. Это требует разделения рендеринга на несколько вызовов отрисовки (если, возможно, мы не используем буферы аргументов), но накладные расходы на выполнение, скажем, 10 вызовов отрисовки с экземплярами, каждый с 1/10 экземплярами, будут незначительными по сравнению со временемтребуется для фактического рендеринга.
Мы также могли бы рассмотреть возможность использования MTLHeap
для создания временных буферов дешевле, чем выделение буферов непосредственно с нашего устройства, но для этого все же требуется выбрать разумную начальную верхнюю границу и увеличить / перераспределить в качестве максимумаколичество экземпляров увеличивается.
В конце дня теория опережает тесты. Естественно, вы должны начать с анализа ваших данных, как мы пытаемся сделать здесь, но создание тестового приложения и реализация этих идей на практике, замечая, где на самом деле возникают проблемы, - это единственный реальный способ убедиться в том, какая оптимизация вам нужна. сделать, чтобы работать гладко на вашей целевой платформе.