Максимально быстрая отрисовка битмапа(GDI).

Тема в разделе "WASM.WIN32", создана пользователем Sholar, 11 дек 2011.

  1. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Средствами GDI нужно максимально быстро отрисовать некоторое количество прямоугольников на рабочем столе. В качестве теста скомпилировал кодес, который отрисовывает один единственный прямоугольник. Тормоза были жуткие. Вот код отрисовки:
    Код (Text):
    1. RECT rc;
    2. while(1)
    3. {
    4.      rc.bottom = yStart+32;
    5.      rc.left  = xStart;
    6.      rc.right = xStart+32;
    7.      rc.top = yStart;
    8. BitBlt(
    9. hdc, xStart, yStart, ptSize.x, ptSize.y,
    10. hdcMem, ptOrg.x, ptOrg.y, SRCCOPY
    11. );
    12. xStart += 0.01;
    13. InvalidateRect(NULL,&rc,TRUE);
    14. }
    Я что-то делаю не так, или средствами GDI мою задачу не решить в принципе?
     
  2. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    что это за безобразие? - ты по 100 раз выводишь прямоугольник на одном месте и удивляешься что он тормозит :))
    GDI при таких простых операциях как копирование прямоугольников использует аппаратное 2D ускорение (оно сейчас во всех видеокартах есть), так что если не делать откровенных глупостей то ничего там не тормозит.
     
  3. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Прямоугольник плавно передвигается вправо.
     
  4. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    На экране прямоугольник может "плавно" передвигаться только кратно целым пикселам, поэтому прибавляя по 0,01 ты 100 раз рисуешь его на одном месте. Округление до целого происходит в строчках rc.left = xStart; rc.right = xStart+32;.
    GDI+ позволяет работать с нецелыми координатами, но непосредственно при отрисовке они всё равно будут округляться уже внутри функций рисования.
    Для настоящей плавности используй синхронизацию по таймеру, тогда никакх перегрузок не будет.
     
  5. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Да да, это я понимаю. Специально так сделано, чтобы прямоугольник мгновенно не убегал за экран. А тормоза о которых я говорил связаны с перерисовкой рабочего стола. Во первых виндовс почему-то перерисовывает всю область рабочего стола, хотя я задал RECT который нужно перерисовывать, а во вторых это все происходит очень медленно.
     
  6. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Естественно очень медленно - ведь видеокарта дико перегружена твоими командами по рисованию прямоугольника на одном месте и всему остальному приходится ждать своей очереди. Чтобы он не убегал слишком быстро используй SetTimer и будет тебе счастье.
     
  7. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Графика дело тонкое.
    Рисовать надо экономно, обетом сказали.
    Да GDI ускорен. Но передача команды в видео карту дело не быстрое.
    Если говорить про GDI, порой гораздо быстрее нарисовать данные с использованием CPU в основная память, а потом вывести на экран. Всё это можно сделать средствами GDI.

    Она не рисует прямоугольник, а копирует картинку.
    hdcMem - я так понимаю рисунок расположен в основной памяти компьютера.
    Представь тормоза от пересылки всей картинке через шину видео карты. Процесс не быстрый. Более того команда синхронная это значит что процессор будет ждать пока она перепишется.
    Это дело не быстрое нежели чем переслать одну команду. Вот если бы ты расположил картинку в видео памяти, то скорость была бы выше.
     
  8. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Ммм, как это можно сделать?
     
  9. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Читай MSDN.

    http://msdn.microsoft.com/en-us/library/dd183382(v=vs.85).aspx

    Device-dependent bitmaps (DDB) что в переводе означает зависящие от устройства битовые карты.
    ...
    are still useful for better GDI performance and for other situations. часто используются для повышения производительности GDI и других ситуациях.

    PS. Сам я этим занимался когда у всех еще win98 стоял. Сейчас говорят не актуально. Так что быстрее рисовать в памяти, а потом выводить.
     
  10. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Надо сказать, что InvalidateRect() не должен вызываться вместе с кодом рисования.
    Таймер хорошо, но поток будет лучше в этом случае.
    В потоке надо сделать цикл такого рода:
    Код (Text):
    1. while (1)
    2. {
    3.     Sleep (1000/24);
    4.     //
    5.     // Изменить координаты рисуемого объекта...
    6.     // 'rect' должен объединять положения объекта до И после изменения координат.
    7.     //
    8.     InvalidateRect (hWnd, &rect, TRUE);
    9.     UpdateWindow (hWnd); // <-- Внутри вызовется "case WM_PAINT".
    10. }
    А рисовать надо в ответе на WM_PAINT - по координатам объекта - они должны быть известны потоку
    и обработчику WM_PAINT.
     
  11. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Sholar
    Оптимизация начинается не с того чтобы делать бессмысленные действия более эффективным способом, а с того чтобы их не делать совсем. То о чём пишет Pavia это не твоя ситуация. Ведь ускорив работу программы ты опять столкнёшся со "слишком быстрым убегаением", который опять "победишь" измельчением шага и опять добьёшься перегрузки видеокарты только на другом витке "оптимизации" :))
     
  12. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Дело в том, что у меня вообще нет ни выборки сообщений, ни оконной процедуры, ни самого окна. Рисование происходит поверх всего на дисплее(GetDC(NULL)). Учел ваши замечания и сделал так:
    Код (Text):
    1. int x = 100, y = 100;
    2.  
    3.  
    4.  DWORD WINAPI Proc(PVOID p)
    5.  {
    6.      RECT rect;
    7.      while(1)
    8.      {
    9.          Sleep(100);
    10.          rect.bottom = y+32;
    11.          rect.left = x;
    12.          rect.right = x+32;
    13.          rect.top = y;
    14.          x++;
    15.          InvalidateRect(NULL,&rect,NULL);
    16.          UpdateWindow(NULL);
    17.      }
    18.  }
    19.  
    20. int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR, int)
    21. {
    22.        //Загрузка битмапа из файла, ничего интересного.
    23.        HBITMAP hBmp=ULLoadImage(_T("img.bmp"),NULL,TRUE,NULL);
    24.        HDC hDC = GetDC(NULL);
    25.    
    26.  
    27.         CreateThread(NULL,1024,Proc,NULL,0,NULL);
    28.         DWORD time = timeGetTime();
    29.         while(1)
    30.         {
    31.             if((timeGetTime()-time) > 100)
    32.             {
    33.                 DrawBitmap(hDC,hBmp,x,y);
    34.                 time = timeGetTime();
    35.             }
    36.            
    37.         }
    38.    
    39.      ReleaseDC(NULL,hDC);
    40. }
    Лаги жуткие. У меня довольно слабая интегрированная видеокарта на 128 мб, но сомневаюсь, что из-за этого все может быть настолько плохо.
     
  13. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Sholar
    Ошибок море
    1) бесконечный цикл.
    2) нет обработки сообщений.
    3) Ошибки с потоком.

    Остальные не перечисляю.
     
  14. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Не важно, мне только протестировать нужно.
    Зачем мне эта обработка, когда у меня нет окна?
    Тоже не важно. Все эти ошибки не влияют непосредственно на отрисовку битмапа.
     
  15. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Откуда ты знаешь?
     
  16. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Sholar
    Команда sleep передаёт остаток кванта времни другим потокам. У тебя она в одном потоке есть, а в другом нет - там вместо неё опять бессмысленная загрузка процессора сравнениями времени. Естественно что этот "агрессивный" поток грузит процессор и вытесняет с него более "деликатный" который ему постянно уступает. И это тоже источник "лагов".
     
  17. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Да я как только не смотрел, и в обоих потоках Sleep`ы ставил, и менял их на таймеры и чего только не делал. Результат идентичный. Почему-то если рисовать на каком-нибудь окне, то все ок. А при отрисовке на GetDC(0), происходят какие-то странности.
     
  18. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    Почему-бы не взять книгу и не прочитать как нужно работать с графикой? Не понимаю людей.
     
  19. Sholar

    Sholar New Member

    Публикаций:
    0
    Регистрация:
    16 окт 2011
    Сообщения:
    189
    Да мне как бы уже объяснили, что с GDI на рабочем столе нормально рисовать я не смогу. Наверное придется копать в сторону DX с его оверлеями.
     
  20. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Если нет окна, то надо его изобрести.
    Например, окном может быть поверхность вашего прямоугольника.
    В таком случае - в потоке, просто вызывается MoveWindow(hWnd,x,y,width,height,1) на новые координаты.
    У окна нет бордюра и нету заголовка и нету фона (WM_ERASEBKGND возвращаем 1).
    Тогда в WinMain() не будет бесконечного цикла, а будет нормальный цикл сообщений.
    Естественно, в WM_PAINT просто рисуем, то что надо двигать.