XNA — проблема с производительностью отбраковки

Этот метод, который рисует мои плитки, кажется довольно медленным, я не уверен, что именно не так, он считает, что мой метод отбраковки не работает и рисует за кадром, но я не совсем уверен. Вот:

        // Calculate the visible range of tiles.
        int left = (int)Math.Floor(cameraPosition.X / 16);
        int right = left + spriteBatch.GraphicsDevice.Viewport.Width / 16;
        right = Math.Min(right, Width) + 1; // Width -1 originally - didn't look good as tiles drawn on screen
        if (right > tiles.GetUpperBound(0))
            right = tiles.GetUpperBound(0) + 1; // adding 1 to get the last right tile drawn

        int top = (int)Math.Floor(cameraPosition.Y / 16);
        int bottom = left + spriteBatch.GraphicsDevice.Viewport.Height/ 16;
        bottom = Math.Min(bottom, Height) + 1; // Height -1 originally - didn't look good as tiles drawn on screen
        if (bottom > tiles.GetUpperBound(1))
            bottom = tiles.GetUpperBound(1) + 1; // adding 1 to get the last bottom tile drawn

        // For each tile position
        for (int y = top; y < bottom; ++y)
        {
            for (int x = left; x < right; ++x)
            {
                // If there is a visible tile in that position, draw it
                if (tiles[x, y].BlockType.Name != "Blank")
                {
                    Texture2D texture = tileContent["DirtBlock_" + getTileSetType(tiles,x,y)];
                    spriteBatch.Draw(texture, new Vector2(x * 16, y * 16), Color.White);
                    if (isMinimap)
                    spriteBatch.Draw(pixel, new Vector2(30+x, 30+y), Color.White);
                }

            }
        }

GetTileSetTypes — это функция для получения тайлов вокруг него для различных текстур, таких как DirtBlock_North, DirtBlock_Center и т. д.

Тайловое содержимое — это просто класс с моими блочными текстурами.


person Cyral    schedule 26.03.2012    source источник
comment
как вы определили, что это медленный код? сколько плиток обычно рисуется за кадр? где вы вызываете SpriteBatch.Begin и .End?   -  person Dave Cousineau    schedule 26.03.2012
comment
Это определенно проблема SpriteBatch. Когда я пропускаю только рисование плиток, но загружаю коллизию и т. Д., Я получаю 30 кадров в секунду. Это наводит меня на мысль, что метод отбраковки вверху по-прежнему рисует что-то за пределами экрана, а переключение графики является проблемой.   -  person Cyral    schedule 27.03.2012
comment
вам, вероятно, следует использовать профилировщик, чтобы вы могли точно определить, что вызывает его медленную работу. Пример: SlimTune; code.google.com/p/slimtune   -  person Dave Cousineau    schedule 27.03.2012


Ответы (1)


Попробуйте изменить SpriteBatch.Begin на deferred и объединить все тайлы в одну текстуру.

См. этот вопрос GameDev для получения информации о том, почему отложенный, скорее всего, самый быстрый вариант для вас.

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

Это можно исправить, объединив несколько спрайтов в одну текстуру и используя аргумент исходного прямоугольника. Это позволяет вам рисовать несколько спрайтов без замены текстуры. Для этого есть несколько библиотек OSS. Упаковщик листов спрайтов — мой фаворит.

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

Если ничего не помогает, я бы предложил использовать профилировщик, чтобы выяснить, какие биты работают медленно.

        //Init the holder
        _holder = new Rectangle(0, 0, TileWidth, TileHeight);

        //Figure out the min and max tile indices to draw
        var minX = Math.Max((int)Math.Floor((float)worldArea.Left / TileWidth), 0);
        var maxX = Math.Min((int)Math.Ceiling((float)worldArea.Right / TileWidth), Width);

        var minY = Math.Max((int)Math.Floor((float)worldArea.Top / TileHeight), 0);
        var maxY = Math.Min((int)Math.Ceiling((float)worldArea.Bottom / TileHeight), Height);

        for (var y = minY; y < maxY; y++) {
            for (var x = minX; x < maxX; x++) {

                _holder.X = x * TileWidth;
                _holder.Y = y * TileHeight;

                var t = tileLayer[y * Width + x];
                spriteBatch.Draw(
                    t.Texture,
                     _holder, 
                    t.SourceRectangle,
                    Color.White, 
                    0,
                    Vector2.Zero,
                    t.SpriteEffects, 
                    0);
            }
        }
person ClassicThunder    schedule 26.03.2012
comment
Хотя листы спрайтов, скорее всего, подойдут, OP может попробовать SpriteSortMode.Texture, а не просто Deferred. Это отсортирует все по текстуре, что сведет к минимуму переключение текстур. msdn.microsoft.com/en-us/ библиотека/ - person Dave Carlile; 26.03.2012
comment
Я собираюсь скоро внедрить SpriteSheets, но я пробовал использовать только одну текстуру вместо разных, и это было всего несколько кадров в секунду, так что я знаю, что это не основная причина. - person Cyral; 26.03.2012
comment
Спасибо, это хороший момент, я воздержался от предложения SpriteSortMode.Texture, потому что, если у него есть объекты или декорации, это может улучшить производительность при рисовании неперекрывающихся спрайтов одинаковой глубины не верно. Это вынуждает его использовать отдельный пакет спрайтов только для рендеринга тайлов, что сокращает время рендеринга тайловой карты, но ухудшает общий fps после учета NPC и пейзажей. - person ClassicThunder; 26.03.2012
comment
У меня есть отдельный spritbatch для рисования фона. Кроме того, правильно ли я делаю GetUpperBound? Для массива [,] - person Cyral; 26.03.2012