Как именно работает XNA SpriteBatch?

38

Если быть более точным, если бы мне нужно было воссоздать эту функциональность с нуля в другом API (например, в OpenGL), что бы он мог сделать?

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

Однако я не слишком знаком с самим процессом дозирования. Все ли квады хранятся в одном буфере вершин? Нужен ли для этого индексный буфер? Как обрабатываются разные текстуры?

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

Дэвид Гувея
источник
Посмотрите , как это реализовано в MonoGame через OpenGL: github.com/mono/MonoGame/blob/master/MonoGame.Framework/...
Ден
Вы пытались использовать DotPeek или Reflector на Microsoft.Xna.Graphics (хотя я не предлагаю делать что-то незаконное :)?
День
Спасибо за ссылку. И эта мысль перешла меня (у меня даже есть dotPeek здесь под рукой), но я подумал, что было бы лучше попросить общее описание у кого-то, кто, возможно, уже сделал это раньше и знает процедуру по памяти. Таким образом, ответ будет сохранен здесь и доступен для других людей. В случае, если никто не дает такого описания, я сам проведу анализ и отправлю ответ на свой вопрос, но сейчас я немного подожду.
Дэвид Гувея
Да, вот почему я использовал комментарий. Я думаю, что Эндрю Рассел может быть хорошим человеком, чтобы ответить на этот вопрос. Он активен в этом сообществе и работает над ExEn, который является альтернативой MonoGame: andrewrussell.net/exen .
День
Да, это тот вопрос, который Эндрю Рассел (или Шон Харгривз назад на форуме XNA) обычно мог бы дать много понимания.
Дэвид Гувея

Ответы:

42

Я как бы повторил поведение SpriteBatch в отложенном режиме для кроссплатформенного движка, над которым я работаю, поэтому вот шаги, которые я до сих пор проектировал в обратном порядке:

  1. SpriteBatch конструктор: создает DynamicIndexBuffer, DynamicVertexBufferи массив VertexPositionColorTextureфиксированного размера (в данном случае, максимальный размер пакета - 2048 для спрайтов и 8192 для вершин).

    • Буфер индекса заполняется индексами вершин квадратов, которые будут нарисованы (0-1-2, 0-2-3, 4-5-6, 4-6-7 и т. Д.).
    • Также SpriteInfoсоздается внутренний массив структур. Это сохранит временные настройки спрайта, которые будут использоваться при дозировании.
  2. SpriteBatch.Begin: внутренне сохраняет значения BlendState, SamplerStateи т.д. , указанные и проверки , если она была вызвана дважды без SpriteBatch.Endмежду ними.

  3. SpriteBatch.Draw: берет всю информацию о спрайте (текстуру, положение, цвет) и копирует ее в SpriteInfo. Если максимальный размер партии достигнут, вся партия рисуется, чтобы освободить место для новых спрайтов.

    • SpriteBatch.DrawStringпросто выдает Drawдля каждого символа строки, принимая во внимание кернинг и интервалы.
  4. SpriteBatch.End: выполняет следующие операции:

    • Устанавливает состояния рендеринга, указанные в Begin.
    • Создает матрицу ортографической проекции.
    • Применяет SpriteBatchшейдер.
    • Связывает DynamicVertexBufferи DynamicIndexBuffer.
    • Выполняет следующую операцию дозирования:

      startingOffset = 0;
      currentTexture, oldTexture = null;
      
      // Iterate through all sprites
      foreach SpriteInfo in SpriteBuffer
      {
          // Store sprite index and texture
          spriteIndex = SpriteBuffer.IndexOf(SpriteInfo);
          currentTexture = SpriteInfo.Texture;
      
          // Issue draw call if batch count > 0 and there is a texture change
          if (currentTexture != oldTexture)
          {
              if (spriteIndex > startingOffset)
              {
                  RenderBatch(currentTexture, SpriteBuffer, startingOffset,
                              spriteIndex - startingOffset);
              }
              startingOffset = spriteIndex;
              oldTexture = currentTexture;
          }
      }
      
      // Draw remaining batch and clear the sprite data
      RenderBatch(currentTexture, SpriteBuffer, startingOffset,
                  SpriteBuffer.Count - startingOffset);
      SpriteBuffer.Clear();
  5. SpriteBatch.RenderBatch: выполняет следующие операции для каждого из SpriteInfoпакета:

    • Занимает позицию спрайта и вычисляет конечную позицию четырех вершин в соответствии с началом и размером. Применяет существующее вращение.
    • Вычисляет координаты УФ и применяет указанные SpriteEffectsк ним.
    • Копирует цвет спрайта.
    • Эти значения затем сохраняются в массиве VertexPositionColorTextureранее созданных элементов. Когда все спрайты были рассчитаны, SetDataвызывается DynamicVertexBufferи DrawIndexedPrimitivesвызывается.
    • Вершинный шейдер выполняет только операцию преобразования, а пиксельный шейдер применяет тонирование к цвету, выбранному из текстуры.
r2d2rigo
источник
Это именно то, что мне было нужно, большое спасибо! Мне потребовалось немного, чтобы поглотить все это, но я, наконец, понял все детали. Одна из тех, что привлекла мое внимание, и я не совсем осознавал это раньше, это то, что даже в отложенном режиме, если я могу каким-то образом сортировать свои вызовы SpriteBatch.Draw по текстуре (то есть, если мне не важен порядок этих вызовов), это будет уменьшить количество операций RenderBatch и, возможно, ускорить рендеринг.
Дэвид Гувея
Между прочим, я не могу найти в документации - какой предел вызовов Draw вы можете сделать до того, как буфер заполнится? И вызывает ли DrawText этот буфер на все количество символов в строке?
Дэвид Гувейя
Впечатляет! Как ваш движок сравнится с ExEn и MonoGame? Для этого потребуется MonoTouch и MonoAndroid? Спасибо.
Ден
@DavidGouveia: 2048 спрайтов - это максимальный размер пакета - отредактировал ответ и добавил его для удобства. Да, сортировка по текстуре и разрешение z-буфера позаботиться о порядке следования спрайтов будет использовать минимальные вызовы отрисовки. Если вам это нужно, попробуйте реализовать SpriteSortMode.Textureрисование в любом порядке и позвольте SpriteBatchсортировке.
r2d2rigo
1
@Den: ExEn и MonoGame - это API-интерфейсы более низкого уровня, которые позволяют запускать код XNA на платформе, отличной от Windows. Проект, над которым я работаю, - это компонентный движок, написанный исключительно на C #, но нам нужен очень тонкий слой для обеспечения доступа к системным API (вот где я работаю). Мы просто решили переопределить SpriteBatchдля удобства. И да, это требует MonoTouch / MonoDroid, так как это все C #!
r2d2rigo
13

Просто хочу отметить, что код XNA SpriteBatch был перенесен в DirectX и выпущен как OSS предыдущим членом команды XNA. Итак, если вы хотите увидеть, как именно это работает в XNA, то вы идете.

ClassicThunder
источник
+1 за «код, который я никогда не буду использовать, но интересно пересмотреть»
Кайл Баран
Последнюю версию XNA SpriteBatch в виде нативного C ++ можно найти на GitHub h / cpp . В качестве бонуса доступна также версия DirectX12 h / cpp
Чак Уолбурн