Эффективное рисование 2D-спрайтов (растровых изображений) - PullRequest
3 голосов
/ 21 августа 2010

Я пишу 2D движок на основе тайлов. В настоящее время моя процедура рисования использует библиотеку рисования C # для перерисовки каждой видимой плитки каждый раз, когда обновляется экран. Я получил прокрутку и масштабирование для работы, и все выглядит так, как я хочу. Но рутина медленная. Сейчас я пытаюсь улучшить это. У меня есть пара вопросов:

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

Во-вторых, насколько я понимаю, процедуры рисования C # не используют всю мощь графического процессора. Я думаю, что я должен попытаться сделать рисование в OpenGL (или DirectX, но я предпочитаю первое, так как это мультиплатформенный). Это поможет? Знаете ли вы какой-нибудь учебник по мозаике (или общему рисованию пикселей) для OpenGL? Справка по книге также может помочь.

В настоящее время я также не занимаюсь многопоточностью (на самом деле у меня есть только смутное представление о том, что это такое). Должен ли я попробовать многопоточность ящика? Или OpenGL сделает многопоточность для графики избыточной?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 21 августа 2010

Какую среду приложений вы планируете использовать?Методы эффективного рисования очень различаются между WinForms (Win32) и WPF.

Вы правы, что подпрограммы рисования .NET не в полной мере используют преимущества графического процессора.Используя DirectX или OpenGL, одна немедленная оптимизация будет заключаться в предварительной загрузке всех ваших плиток изображения (или, по крайней мере, всех плиток, необходимых для области непосредственного просмотра плюс немного больше) в память GPU с использованием списков изображений или списков отображения.Затем вы бы нарисовали плитки на поверхности по индексу - нарисуйте плитку N в точке x, y.Обычно это происходит намного быстрее, чем рисование на поверхности графического процессора с использованием растровых изображений, хранящихся в основной системной памяти, поскольку пиксели растрового изображения необходимо копировать в графический процессор для каждой нарисованной плитки, и это занимает много времени.Рисование по индексу также использует намного меньше памяти GPU, когда вы можете использовать одну и ту же плитку изображения в нескольких местах на выходе.

OpenGL против DirectX - ваш выбор, но IMO DirectX развивается более быстрыми темпами, предоставляя большеАппаратное ускорение функций, чем OpenGL.Драйверы OpenGL в Windows также имеют репутацию пренебрегаемых поставщиками оборудования.Их основное внимание уделяется их драйверам DirectX.

Подумайте, действительно ли вам нужен OpenGL или DirectX для приложения 2D-плиток.Требование OpenGL или DirectX уменьшит количество машин, особенно старых, которые могут запускать ваше приложение.OpenGL и DirectX могут быть излишними.Вы можете многое сделать с простым старым GDI, если вы умны в этом.

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

В общем, перемещение пикселей на экране обычно не подходит для многопоточности.У вас есть только одно устройство отображения (в большинстве случаев), поэтому использование нескольких потоков, пытающихся одновременно изменять пиксели, не работает.Чтобы позаимствовать выражение у руководства проекта: если женщина может создать ребенка за 9 месяцев, можете ли вы использовать 9 женщин, чтобы создать 1 ребенка за 1 месяц?;>

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

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

2 голосов
/ 21 августа 2010

Я не знаком с OpenGL, но я написал движок на основе плиток в ManagedDX (позже портирован на XNA).ManagedDX ограничен, но есть проект SlimDX , который все еще находится в активной разработке.

С DX вы можете загрузить каждую отдельную плитку в Texture.(Используя Texture.FromFile() или Texture.FromStream(), например), и имейте единственный экземпляр Sprite, рисующий их.Это работает довольно хорошо.Я группирую текстуры в простой класс с Point или Vector2 для их местоположений, устанавливая их только при изменении местоположения, а не каждый раз, когда вызывается метод draw.Я кэширую тайлы в памяти только для непосредственного экрана и одного или двух тайлов дальше, больше не нужно, так как файловый ввод-вывод достаточно быстр для извлечения новых тайлов при прокрутке.

struct Tile
{
    public Point Location;
    public Texture Texture;
}

Device device;
Sprite sprite;
List<Tile> tiles = new List<Tile>();

.ctor() {
    this.device = new Device(...)
    this.sprite = new Sprite(this.device);
}

void Draw() {
    this.device.Clear(ClearFlags.Target, Color.CornflowerBlue, 1.0f, 0);
    this.device.BeginScene();
    this.sprite.Begin(SpriteFlags.AlphaBlend);
    foreach (Tile tile in this.tiles) {
        this.sprite.Draw2D(tile.Texture, 
            new Point(0, 0), 0.0f, tile.Location, Color.White);
    }
    this.sprite.End();
    this.device.EndScene();
    this.device.Present();
}
2 голосов
/ 21 августа 2010

Если вы хотите использовать OpenGL, ваша лучшая ставка для 2d будет SDL . Использование OpenGL с C # никогда не будет таким переносимым просто из-за того, что он будет использовать оболочки .NET.

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

Для вашего вопроса об урезании или прокрутке лучшим маршрутом будет прокрутка. Когда вы рисуете с использованием GDI + (память, используемая System.Drawing), проблема с памятью гораздо меньше, чем с процессором. Вы всегда можете разбить карту на части и прокрутить их, а затем загрузить при необходимости, если она такая большая.

...