Как сделать экран недействительным быстрее? - PullRequest
1 голос
/ 17 апреля 2011

В моем текущем проекте (игре жизни) мне нужно перерисовать экран, так как движется около 200 объектов. Я могу придумать два пути, но понятия не имею, что будет быстрее:

Я могу либо:
1) Вызвать Invalidate () для всего экрана и в обработчике Paint:

void Paint(object sender, PaintEventArgs e)
{
   foreach(Cell c in ListOfCells)
          {
             e.DrawImage(c,c.x,c.y);
          }
}

2) Или я могу сделать недействительной каждую часть экрана для каждой ячейки:

public void MyInvalidate()
{
  foreach(Cell c in ListOfCells)
              {
                 Invalidate(c.X,c.Y,c.Width,c.Height);
              }


}

И иметь тот же обработчик, что и выше

Ответы [ 2 ]

4 голосов
/ 17 апреля 2011

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

Теперь в Game of Life вы изначально смотрите на довольно пустой экран. Однако по мере продолжения игр будет заполняться все больше и больше ячеек, в конечном итоге вся доска. Теперь правила GOL предписывают, что большинство этих ячеек будут меняться от одного цикла к другому.

Вам также необходимо понять, что означает "аннулирование" региона. В Windows недействительные области «складываются», образуя «область обновления», так что сообщение WM_PAINT может сообщить программе, какие части экрана должны быть нарисованы. В обработчике событий Paint вы используете RectVisible, чтобы определить, нужно ли обновлять ячейку.

Другими словами, «стоимость» выполнения метода 1 (при условии, что размер платы n):

(n x n) x (redraw cell + update cell on screen)

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

«Стоимость» выполнения метода 2:

(v) x (redraw cell + update cell on screen) + (n x n) x (RectVisible call)

где v = количество ячеек, которые изменились (см. Примечание ниже).

Итак, метод 1 быстрее, чем метод 2, если:

(n x n) x redraw < v x redraw + (n x n) x RectVisible
n2 x (redraw - RectVisible) < v x redraw
v > ((redraw - RectVisible) / redraw) x n2
v > (1 - RectVisible/redraw) x n2

Другими словами, количество измененных ячеек должно быть больше единицы за вычетом отношения (стоимость RectVisible / стоимость перерисовки). Теперь RectVisible обычно довольно быстрый по сравнению с отображением растрового изображения на экране, особенно если ваши ячейки имеют высокое разрешение. Следовательно, Rectvisible / redraw обычно очень мало, что делает метод 1 быстрее, чем метод 2, только когда, скажем,> 99% платы изменяется одновременно, что маловероятно.

Другими словами, вы обнаружите, что метод 2 обычно дает более высокую производительность, поскольку DrawImage обычно пропускает всю операцию, если изображение должно быть нарисовано вне области отсечения (то есть не в области обновления).

ПРЕДУПРЕЖДАЙТЕ - ВАШ КОД, КАК ЭТО НЕ БУДЕТ

Однако ваш код в методе 2 не будет работать. Вы должны помнить, чтобы «убрать» «мертвые» клетки, которые имели «живые» клетки в течение последнего цикла. Другими словами, вы лишаете законной силы клетки, которые «изменились», а не только те, которые «живы». Клетки, которые «живы» в течение нескольких циклов, , а не должны быть признаны недействительными. Таким образом, логика в вашем методе MyInvalidate имеет недостатки.

0 голосов
/ 17 апреля 2011

Второй код не принесет никакой пользы, если вы будете использовать тот же обработчик событий Paint, который вы использовали для первого, поскольку в основном вы будете перерисовывать ВСЕ ячейки несколько раз (потому что для каждой ячейки вы будете перерисовыватьвсе остальные клетки тоже).Чтобы это исправить, вы можете проверить e.ClipRectangle и перерисовать только те ячейки, которые попадают в этот прямоугольник.

Однако, если на экране есть только ячейки (так чтоу вас нет большого количества других элементов отображения, например, элементов управления пользовательским интерфейсом), тогда первый подход - лучший, который вы получите (т.е. просто Invalidate () на весь экран).Нарушение производительности происходит только тогда, когда вы лишаете законной силы ОЧЕНЬ неизменившихся областей экрана.

...