Re[12]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 14.12.10 09:46
Оценка:
Здравствуйте, samius, Вы писали:

PD>>Приходит WM_PAINT при движении контура или нет.

S>Да, приходит, прчем вместе с WM_ERASEBKGND и даже без контура чисто на WM_MOUSEMOVE (даже не нажатый).
S>Полагаю, ты немедленно избавишься от этого софта?

Так мне же уже Sinix объяснил, что у меня его нет


PD>>Ладно, давай не будем еще и здесь флейм на эту тему устраивать.

S>Ты начал. Уверен,что в большинстве современного софта проблемы производительности растут не из-за того что рисуются лишние пиксели в WM_PAINT.

Я лишь свои рекомендации дал.
With best regards
Pavel Dvorkin
Re[2]: GDI+ быстрый вывод
От: Аноним  
Дата: 14.12.10 15:34
Оценка:
Здравствуйте, vit_as, Вы писали:


_>Обновление графики только в событии Paint


всё мерцает. если вне события то просто не очищаются старые линии.

_>Чтобы не было мерцания у контрола надо поставить свойство DoubleBuffered=true


Двойной буффер/Оптимизированый (SetStyle) или вместе абсолютно не дают результат. что с ними что без.
Re[2]: GDI+ быстрый вывод
От: Аноним  
Дата: 14.12.10 15:44
Оценка:
Здравствуйте, samius, Вы писали:

S>За очистку фона перед OnPaint отвечает метод OnPaintBackground, который трогать не надо если не планируется сложный фон. Как правило хватает свойств BackColor/BakcgroundImage. Color.Transparent нельзя использовать в качестве цвета для очистки фона.


хех если рисовать всё в OnPaint и сделать так:
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            //base.OnPaintBackground(e);
        }


то тормоза отпадают но как уже понятно не очищается ничего.

base.OnPaintBackground слишком медленно работает
Re[3]: GDI+ быстрый вывод
От: samius Япония http://sams-tricks.blogspot.com
Дата: 14.12.10 16:49
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, samius, Вы писали:


А>хех если рисовать всё в OnPaint и сделать так:

А>
А>        protected override void OnPaintBackground(PaintEventArgs e)
А>        {
А>            //base.OnPaintBackground(e);
А>        }
А>


А>то тормоза отпадают но как уже понятно не очищается ничего.


А>base.OnPaintBackground слишком медленно работает


Взял форму из стандартного шаблона, внес изменения только в From1.cs (в дизайнере ничего не менял).
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Paint += Form1_Paint;

        SetStyle(ControlStyles.ResizeRedraw, true);
        //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }

    void Form1_Paint(object sender, PaintEventArgs e)
    {
        var rect = ClientRectangle;
        using (var pen = new Pen(Color.Red, 50))
            e.Graphics.DrawLine(pen, rect.Left, rect.Top, rect.Right, rect.Bottom);
    }
}


При запуске в винде с включенной опцией "Рисовать окно при перетаскивании", рисуется красная жирная линия с безбожным фликанием во время ресайза формы. Раскомментирование второй строчки SetStyle флики убирает.
Первая строчка SetStyle нужна для того что бы организовать автоматическую перерисовку при ресайзе формы.
Re: GDI+ быстрый вывод
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 14.12.10 20:58
Оценка: 3 (1) +1
Здравствуйте, Аноним, Вы писали:

А>Появилась задача сделать выделение областей мышкой на форме. пробовал множество способов — у всех (кроме 1) при каждом движении курсора всё (или только линии выделения) безбожно мигает. Да и не смог нагуглить как в GDI+/c# выводить графику в реал-тайме.


А>
А>        protected override void OnMouseMove(MouseEventArgs e)
А>        {
А>            m_LastMouseLocation = e.Location;

А>            if (m_InChoosing)
А>            {
А>                Graphics gr = this.CreateGraphics();
А>            }

А>            base.OnMouseMove(e);
А>        }
А>


В OnMouseMove ничего рисовать нельзя, иначе мерцание побороть не удастся. OnMouseMove должен устанвливать флаг обновления.

Контролу, на котором рисуешь, надо сделать вот это
            SetStyle(
                ControlStyles.AllPaintingInWmPaint /* | ControlStyles.DoubleBuffer*/| ControlStyles.UserPaint |
                ControlStyles.Opaque |
                ControlStyles.ResizeRedraw | ControlStyles.Selectable | ControlStyles.StandardClick, true);


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

Щас скажу страшное — для простоты кода, для подавления мерцания с артефактами вся отрисовка только OnPaint в оба битмапа и на главный Graphics !!! Это если ты конечно не игру пишешь и нет никаких тяжелых анимаций.

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

        private void ReCreateMainDrawing()
        {
            if (_mainDrawing == null || _mainDrawing.Width != _drawingSize.Width ||
                _mainDrawing.Height != _drawingSize.Height)
            {
                if (_mainDrawing != null)
                    _mainDrawing.Dispose();
                _mainDrawing = new Bitmap(_drawingSize.Width, _drawingSize.Height, PixelFormat.Format32bppPArgb);
                _bRedraw = true;
            }
        }


Для вспомогательного примерно так же, но см. ниже — фоном для вспомогательного будет основной.


Итого, как происходит отрисовка

0. В OnMouseMove управляем флагами для перерисовки, что нужно обновлять.
1. в OnPaint проверяем, надо ли перерисовать статический контент, если надо — перерисовать основной битмап.
2. в OnPaint проверяем, надо ли перерисовать динамический контент, если надо — перерисовываем вспомогательный битмап, в качестве фона — основной битмап.
3. в конце отрисовываем или основной битмап или вспомогательный вот такой функцией

        protected void ApplyDrawing(Bitmap drawing, PaintEventArgs e, bool bFull)
        {
            if (bFull)
                e.Graphics.DrawImageUnscaled(drawing, 0, 0);
            else
                e.Graphics.DrawImage(drawing,
                                     e.ClipRectangle,
                                     e.ClipRectangle.Left,
                                     e.ClipRectangle.Top,
                                     e.ClipRectangle.Width,
                                     e.ClipRectangle.Height,
                                     GraphicsUnit.Pixel);
        }
Re[2]: GDI+ быстрый вывод
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.12.10 04:05
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Итого, как происходит отрисовка


I>0. В OnMouseMove управляем флагами для перерисовки, что нужно обновлять.

I>1. в OnPaint проверяем, надо ли перерисовать статический контент, если надо — перерисовать основной битмап.
I>2. в OnPaint проверяем, надо ли перерисовать динамический контент, если надо — перерисовываем вспомогательный битмап, в качестве фона — основной битмап.
I>3. в конце отрисовываем или основной битмап или вспомогательный вот такой функцией

+1
Примерно к такой же схеме я пришел когда занимался ГИС-ом, где была и статическая информация (очень много, до нескольких секунд, или даже до минуты вывода GDI средствами) и динамическая, которая всегда должна быть актуальна, даже во время длительной отрисовки статической. Т.е. паузы в отображении динамической информации быть не должно.

Отличия были в том, что динамической информации было как-правило мало, и она не требовала буферизации. Но само-собой, выводилась не на DC контрола, а в итоговый буфер, где склеивались различные буфера, после чего этот буфер копировался на DC контрола.
А статических буферов было несколько. Так же был D3D рельеф ортогонального вида, который тоже приходилось буферизовывать, т.к. рисовался он немаленьким mesh-ем.

Для комфортных Pan/Zoom/ResizeWindow приходилось держать буферы значительно превышающие размеры окна. Это позволяло при изменениях параметров вида получать превью статической информации с помощью трансформаций буферов. После завершения выбора параметров вида (т.е. когда пользователь повозит мышкой и успокоится), начиналась фоновая отрисовка статических буферов с регулярным (раз 5-10 в секунду) выбросом нарендеренной статики на девайс контрола. Динамическая информация при этом обновлялась от 20и раз в секунду.

Специально для Павла: в 2004м году вся эта кухня позволяла комфортно работать на 4м пне с 256Мб оперативки с интегрированной графикой на 1м тогда фреймворке . Работало и на 3м пне. Но там даже винда сама по себе тормозила.
Ну и как-то на фоне всего происходящего, экономить на том что бы делать Invalidate по ребрам раббера, просто в голову не приходило.
Re: GDI+ быстрый вывод
От: dsorokin Россия  
Дата: 15.12.10 07:23
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Появилась задача сделать выделение областей мышкой на форме.


1. В обработчике OnMouseMove вызывать у контрола Invalidate с заданной областью. Чем меньше и точнее область, тем лучше.

2. В обработчике OnPaint перерисовать, но только то, что пересекается с PaintEventArgs.ClipRectangle. В этот прямоугольник как раз попадет та область, которую задал при вызове Invalidate (или череде вызовов). Чем она меньше, а также чем алгоритмически лучше происходит вычисление пересечения, тем быстрее отрисовка.

3. Мерцание убрать через двойную буферизацию у контрола (установить стили через SetStyle).

4. Рисовать напрямую через CreateGraphics — часто плохая идея. Но бывают исключения. Здесь не тот случай.

Нужно понимать, что Invalidate посылает сообщение, которое ставится в очередь потока GUI. Можно вызывать Invalidate многократно. Когда OnMouseMove отработает, поток GUI достанет из очереди все накопившиеся сообщения Invalidate и сделает вызов OnPaint. Таков основной цикл.

з.ы. Очень жаль, что в WPF и Silverlight совсем не так. Оттуда безбожно вырезали этот замечательный механизм, который позволял отрисовывать десятки тысяч разбросанных графических элементов одновременно. При хороших алгоритмах и доступной памяти можно было держать и сотни тысяч элементов или даже более. Но нужно было думать. Сейчас в WPF и Silverlight думает машина
Re[3]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 07:30
Оценка:
Здравствуйте, samius, Вы писали:

S>Специально для Павла: в 2004м году вся эта кухня позволяла комфортно работать на 4м пне с 256Мб оперативки с интегрированной графикой на 1м тогда фреймворке . Работало и на 3м пне. Но там даже винда сама по себе тормозила.


Надеюсь, ты понимаешь, что объем ОП здесь едва ли при чем, так как карта у тебя DDB (я полагаю), а поэтому хранится в видеопамяти (тоже полагаю) Тем более не имеет отношения к делу версия FW — здесь работает только подлежащий слой.

S>Ну и как-то на фоне всего происходящего, экономить на том что бы делать Invalidate по ребрам раббера, просто в голову не приходило.


Вольному воля.
With best regards
Pavel Dvorkin
Re[2]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 07:44
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Нужно понимать, что Invalidate посылает сообщение, которое ставится в очередь потока GUI. Можно вызывать Invalidate многократно. Когда OnMouseMove отработает, поток GUI достанет из очереди все накопившиеся сообщения Invalidate и сделает вызов OnPaint. Таков основной цикл.


Немного уточню. В очереди не будет нескольких сообщений. WM_PAINT в очереди всегда один (В действительности там флаг на все окна потока)
Если до его обработки добавляется новая инвалидная область, то Windows модифицирует параметры WM_PAINT, установив инвалидный прямоугольник как минимальный прямоугольник, включающий все ранее взятые и новый. Иными словами, если вначале инвалидить (0,0,100,100), а потом (200,200,300,300), то в OnPaint придет (0,0,300,300)


The QS_PAINT flag is handled differently. If a window created by the thread has an invalid region, the QS_PAINT flag is turned on. When the area occupied by all windows created by this thread becomes validated (usually by a call to ValidateRect, ValidateRegion, or BeginPaint), the QS_PAINT flag is turned off. This flag is turned off only when all windows created by the thread are validated. Calling GetMessage or PeekMessage has no effect on this wake flag.
With best regards
Pavel Dvorkin
Re[2]: GDI+ быстрый вывод
От: MxMsk Португалия  
Дата: 15.12.10 07:51
Оценка: +1
Здравствуйте, dsorokin, Вы писали:

D>з.ы. Очень жаль, что в WPF и Silverlight совсем не так. Оттуда безбожно вырезали этот замечательный механизм, который позволял отрисовывать десятки тысяч разбросанных графических элементов одновременно. При хороших алгоритмах и доступной памяти можно было держать и сотни тысяч элементов или даже более. Но нужно было думать. Сейчас в WPF и Silverlight думает машина

Какой механизм?
Re[4]: GDI+ быстрый вывод
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.12.10 08:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, samius, Вы писали:


S>>Специально для Павла: в 2004м году вся эта кухня позволяла комфортно работать на 4м пне с 256Мб оперативки с интегрированной графикой на 1м тогда фреймворке . Работало и на 3м пне. Но там даже винда сама по себе тормозила.


PD>Надеюсь, ты понимаешь, что объем ОП здесь едва ли при чем, так как карта у тебя DDB (я полагаю), а поэтому хранится в видеопамяти (тоже полагаю) Тем более не имеет отношения к делу версия FW — здесь работает только подлежащий слой.

Карта у меня была векторная. А буферами ведал D3D. Где он их хранил — без понятия, он сам рулит. При 8М видеопамяти выбору у него много не было.
Мне это было не важно, т.к. весь мой пайплайн со склейкой буферов рельефа, статических данных карты, динамических, а так же рабберов и других инструметов взаимодействия, в том числе собственные прозрачные и всплыающие от наезда мыши панели и скроллбары, мог выкидывать 20 кадров в секунду в окне, распахнутом на весь рабочий стол.

А ты говоришь — давайте экономить и посылать на перерисовку по одному ребру раббера. Я полагаю, ты хочешь что-бы видеокарта не перегрелась, делая лишний битблит?
Re[7]: GDI+ быстрый вывод
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.12.10 08:38
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Да, но только при уходе всплывающего окна. Не беда.


Это не беда, это отстойный рендеринг.

PD>Ну а если тебе уж именно так хочется, могу предложить следующее решение. Снимаем с картинки 4 битмэпа толщиной в одну линию , то есть прямоугольник в виде битмэпов. Рисуем тут же линию твоего цвета. После передвижения мышки рисуем эти битмэпы обратно, снимаем новые. Можно даже и без битмэпов — просто снять значения с помощью GetPixel, сойдет

PD>Главное — никакой перерисовки, никаких WM_PAINT.

Это тянет на хороший студенческий подход

PD>InvalidateRect(верхняя линия прямоугольника);

PD>UpdateWindow();
PD>InvalidateRect(левая линия прямоугольника);
PD>UpdateWindow();
PD>и т.д.
PD>По идее UpdateWindow заставит немедленно перерисовать именно эту линию и снять флаг инвалидности.

И это говорит человек, который знает! Винапи
Re[9]: GDI+ быстрый вывод
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.12.10 08:44
Оценка: :)
Здравствуйте, Pavel Dvorkin, Вы писали:

S>>Чем так страшен WM_PAINT, если есть двойной буфер?

PD>На каждый мышкин чих перерисовывать прямоугольник ?

Конечно. БОлее того — это и в OnMouseMove придется делать точно так же. Потому что прямоугольник этот динамически меняется.

S>>А ради чего собственно выжимать такты на ровном месте? Тебя не устраивает скорость BitBlt?

PD>На каждое мышкино передвижение ?

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

Кроме того, почти всегда обновляется только конкретный регион.
Re[5]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 08:46
Оценка:
Здравствуйте, samius, Вы писали:

S>Карта у меня была векторная. А буферами ведал D3D.


Это значит, что у тебя был не GDI. Direct3D по крайней мере тогда работал через DirectDraw.

S>А ты говоришь — давайте экономить и посылать на перерисовку по одному ребру раббера. Я полагаю, ты хочешь что-бы видеокарта не перегрелась, делая лишний битблит?


Мне просто совсем не по нутру, когда ради того, чтобы переисовать периметр прямоугольника, перерисовавают его весь. Ну вот такой у меня характер
With best regards
Pavel Dvorkin
Re[11]: GDI+ быстрый вывод
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.12.10 08:47
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Хм. Если размер контура будет во весь экран ? Тоже тормозить не будет ? На любой видеокарте ? На машине с процессором 5-7 летней давнсти ? Не уверен.


Даже на 10 летнем процессоре тормозов не будет

Техника старая, как GDI32.
Re[8]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 09:06
Оценка:
Здравствуйте, Ikemefula, Вы писали:

I>Это тянет на хороший студенческий подход


Ну мы не в КСВ, дорогой. Если есть аргументы, давай. Без болтовни.

PD>>InvalidateRect(верхняя линия прямоугольника);

PD>>UpdateWindow();
PD>>InvalidateRect(левая линия прямоугольника);
PD>>UpdateWindow();
PD>>и т.д.
PD>>По идее UpdateWindow заставит немедленно перерисовать именно эту линию и снять флаг инвалидности.

I>И это говорит человек, который знает! Винапи



UpdateWindow заставляет немедленно выполниться обработчик WM_PAINT. А пока что там всего лишь один инвалидный прямоугольник шириной в 1 пиксель. После OnPaint он будет зарисован и флаг QS_PAINT в очереди сообщений снят.

MSDN: (выделено мной)

The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty. The function sends a WM_PAINT message directly to the window procedure of the specified window, bypassing the application queue. If the update region is empty, no message is sent.

Рихтер :


The QS_PAINT flag is handled differently. If a window created by the thread has an invalid region, the QS_PAINT flag is turned on. When the area occupied by all windows created by this thread becomes validated (usually by a call to ValidateRect, ValidateRegion, or BeginPaint), the QS_PAINT flag is turned off. This flag is turned off only when all windows created by the thread are validated. Calling GetMessage or PeekMessage has no effect on this wake flag.


Последующие InvalidateRect + UpdateWindow аналогичным образом сработают на оставшиеся 3 линии.

Контраргументы есть ?
With best regards
Pavel Dvorkin
Re[6]: GDI+ быстрый вывод
От: samius Япония http://sams-tricks.blogspot.com
Дата: 15.12.10 09:11
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, samius, Вы писали:


S>>Карта у меня была векторная. А буферами ведал D3D.


PD>Это значит, что у тебя был не GDI. Direct3D по крайней мере тогда работал через DirectDraw.

О, наверное в те времена DirectDraw умел работать с 3D и GDI?
Т.е. ты считаешь, когда у D3D буфера просишь создать Graphics (или на низком уровне получить HDC), то там на самом деле работает DirectDraw, а не GDI, когда ты отправляешь Graphics.DrawString?

PD>Мне просто совсем не по нутру, когда ради того, чтобы переисовать периметр прямоугольника, перерисовавают его весь. Ну вот такой у меня характер


Это трепетное отношение к premature optimization. А к характеру имеет отношение то, что ты хочешь что бы все остальные проявляли бы к ней такую же трепетность, а от студентов еще и требуешь (судя по всему). Извини, не хотел обсуждать твой характер, но тут такая провокация!
Re[10]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 09:16
Оценка:
Здравствуйте, Ikemefula, Вы писали:

PD>>На каждый мышкин чих перерисовывать прямоугольник ?


I>Конечно. Более того — это и в OnMouseMove придется делать точно так же. Потому что прямоугольник этот динамически меняется.


Конечно меняется.

На MouseMove при использовании механизма SetROP2 перерисовывают только рамку. Внутренность не перерисовывают, потому что незачем. Следующий mousemove приведет к тому же — вот и все.


case WM_MOUSEMOVE:
{
SetROP2
Select NULL_BRUSH в hdc
Select нужное перо в hdc
Rectangle по старому прямоугольнику (запомненному в первый раз по WM_LBUTTONDOWN, а потом измененному на mousemove, см. ниже
старый прямоугольник = новый прямоугольник (левый-верхний без изменения, правый-нижний из mousemove lParam)
Rectangle по новому прямоугольнику



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


Этим не процессор, а скорее всего видеокарта занимается.
With best regards
Pavel Dvorkin
Re[7]: GDI+ быстрый вывод
От: Pavel Dvorkin Россия  
Дата: 15.12.10 09:29
Оценка:
Здравствуйте, samius, Вы писали:

PD>>Это значит, что у тебя был не GDI. Direct3D по крайней мере тогда работал через DirectDraw.

S>О, наверное в те времена DirectDraw умел работать с 3D и GDI?



Наоборот. Direct3D работал через DirectDraw, а GDI сам по себе.
DirectDraw и GDI — это два разных механизма. Подробности у Фень Юаня, там об этом много.
GDI слишком медленный для игр, поэтому-то DirectDraw и был сделан.

S>Т.е. ты считаешь, когда у D3D буфера просишь создать Graphics (или на низком уровне получить HDC), то там на самом деле работает DirectDraw, а не GDI, когда ты отправляешь Graphics.DrawString?


HDC в DirectDraw получают иным способом, через directdraw surface

http://programmersforum.ru/showthread.php?p=432552

Ты просто, видимо, не в курсе всего этого.

PD>>Мне просто совсем не по нутру, когда ради того, чтобы переисовать периметр прямоугольника, перерисовавают его весь. Ну вот такой у меня характер


S>Это трепетное отношение к premature optimization. А к характеру имеет отношение то, что ты хочешь что бы все остальные проявляли бы к ней такую же трепетность, а от студентов еще и требуешь (судя по всему). Извини, не хотел обсуждать твой характер, но тут такая провокация!


Считай как хочешь. Я иначе не могу. Тебя никто не заставляет с этим соглашаться.
With best regards
Pavel Dvorkin
Re[11]: GDI+ быстрый вывод
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 15.12.10 10:16
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>На MouseMove при использовании механизма SetROP2 перерисовывают только рамку. Внутренность не перерисовывают, потому что незачем. Следующий mousemove приведет к тому же — вот и все.


А если надо будет чтото сложнее пустого прямоугольника рисовать ?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.