Как передать переменное количество MTLTexture во фрагментный шейдер?

Каков правильный синтаксис для передачи переменного числа MTLTexture в виде массива фрагментному шейдеру?

В этом вопросе StackOverflow: «Как использовать массив texture2d_array в металле?» упоминается использование:

array<texture2d<half>, 5>

Однако для этого необходимо указать размер массива. В Metal Shading Language Specification.pdf (раздел 2.11) они также демонстрируют этот тип. Тем не менее, они также ссылаются на array_ref, но мне не ясно, как его использовать, и разрешено ли оно даже в качестве типа параметра для общего фрагмента с учетом этого утверждения:

«Тип array_ref не может быть передан в качестве аргумента функциям графики и ядра».

Сейчас я просто объявляю параметр как:

fragment float4 fragmentShader(RasterizerData in [[ stage_in ]],
                               sampler s [[ sampler(0) ]],
                               const array<texture2d<half>, 128> textures [[ texture(0) ]]) {

  const half4 c = textures[in.textureIndex].sample(s, in.coords);
}

Поскольку ограничение составляет 128 текстур фрагментов. На любом этапе рендеринга я могу использовать от 1 до n текстур, при этом я гарантирую, что n не превышает 128. Похоже, это работает для меня, но я делаю это неправильно?


Мой вариант использования - это рисование двухмерной плоскости, которая разделена на несколько плиток. Содержимое каждой плитки выбирается из указанной текстуры в массиве на основе предварительно вычисленного индекса текстуры. Текстуры устанавливаются с помощью setFragmentTexture:atIndex в правильном порядке в начале прохода рендеринга. Индекс текстуры передается из вершинного шейдера во фрагментный шейдер.


person kennyc    schedule 16.11.2018    source источник


Ответы (1)


Вы должны рассматривать текстуру массива вместо массива текстур. То есть текстура с типом MTLTextureType2DArray. Вы используете свойство arrayLength дескриптора текстуры, чтобы указать, сколько двумерных срезов содержит текстура массива.

Чтобы заполнить текстуру, вы указываете, в какой фрагмент вы пишете, с помощью таких методов, как -replaceRegion:... или -copyFrom{Buffer,Texture}:...toTexture:....

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

person Ken Thomases    schedule 16.11.2018
comment
Я думал об этом, но тогда все исходные текстуры должны быть одинакового размера, что не относится ко мне. Например, у меня может быть изображение размером 21000 x 5000 пикселей, которое я представляю с четырьмя текстурами размером 5000 x 1000 и одной текстурой размером 1000 x 1000. Использование одной текстуры невозможно, потому что максимальный размер составляет 16384. Текстуры меньшего размера дают мне больше детализации при определении того, какие текстуры грязные и какие mip-карты необходимо регенерировать. Эти текстуры заполняются службой XPC, отображающей произвольные плитки. - person kennyc; 17.11.2018
comment
Дальнейшие действия: переработав мой механизм тайлов и допустив переполнение тайлов, чтобы я мог получать тайлы одинакового размера, использование 2D-массива значительно улучшает производительность и немного очищает код. Документы Metal, видеоролики WWDC и инструменты отладки Xcode довольно хороши, но еще так много предстоит узнать, если вы еще не знакомы с низкоуровневой графикой. - person kennyc; 18.11.2018
comment
@kennyc Я работаю над чем-то вроде этого, пытаюсь визуализировать действительно большие изображения в виде плиток. Не могли бы вы указать мне на какие-нибудь ресурсы? - person Akshay; 18.01.2021
comment
@akshay Трудно дать много рекомендаций, так как это очень обширная тема с множеством различных способов ее реализации. Если вам абсолютно не нужен Metal, я бы начал с того, чтобы заставить все работать с помощью AppKit / UIKit и ImageIO. Вид прокрутки может иметь произвольно большой размер. Когда пользователь прокручивает, нарисуйте соответствующую часть вашего изображения на основе границ scrollView. Вы также можете использовать CALayer в режиме прокрутки и разбивать изображение на более мелкие изображения, которые вы устанавливаете в качестве содержимого слоя. Я использую Metal в своем конвейере и сопоставляю плитки изображений с текстурами. - person kennyc; 19.01.2021
comment
@kennyc Я также использую металл для сопоставления изображений с текстурами. Но меня беспокоит воссоздание всех видимых текстур в разных масштабах всякий раз, когда пользователь увеличивает или уменьшает масштаб. - person Akshay; 19.01.2021
comment
Вам следует посмотреть, как CATiledLayer реализует панорамирование, масштабирование и масштабирование содержимого. Это даст вам представление об одном способе реализации этого. В Metal обычно используется mipmapping для уменьшения разрешения текстур. Для этого вы можете использовать собственные API-интерфейсы Metal или встроить их в свой конвейер обработки изображений. См. metalbyexample.com/mipmapping. - person kennyc; 19.01.2021