Конференция "WinAPI" » BitBlt
 
  • dmk © (09.02.17 15:47) [0]
    Подскажите пожалуйста, есть ли в WinApi возможность дождаться окончания вызова функции? Проблема в том, что в потоках рисуется буфер, который потом выводится на экран. Сразу после вызова BitBlt потоки успевают нарисовать в буфере обновленное содержимое, а отрисовка BitBlt еще не завершена. Получается наложение нового на старое. Можно конечно 2 буфера чередовать, но вдруг в WinApi что есть, чтобы дождаться окончания вывода?
  • NoUser © (09.02.17 17:02) [1]
    > дождаться окончания вывода?
    У тебя два потока, - один 'рисует' в hdcSrc, второй вызывает BitBlt ?

    Да, есть: EnterCriticalSection/LeaveCriticalSection.

    Можно также, для большей утилизации многоядерности: TryEnterCriticalSection с дополнительным буфером ...
  • Игорь Шевченко © (09.02.17 19:27) [2]
    BitBlt не подразумевает асинхронное выполнение
  • dmk © (09.02.17 19:41) [3]
    >Да, есть: EnterCriticalSection/LeaveCriticalSection.
    Так у меня через TThread.Synchronize процедура обновления. Т.е. там уже CriticalSection используется. Значит 2 буфера.

    >BitBlt не подразумевает асинхронное выполнение
    Так о том и пишу. Блок памяти в момент вывода досутпен для изменения :(
  • Игорь Шевченко © (09.02.17 21:39) [4]
    dmk ©   (09.02.17 19:41) [3]

    Значит надо перед вызовом BitBlt заблокировать буфер функцией EnterCriticalSection, а после вызова разблокировать LeaveCriticalSection
    А изменяющий код перед изменениями тоже должен вызвать EnterCriticalSection, а после изменения LeaveCriticalSection, вроде азы синхронизации.
  • NoUser © (10.02.17 00:36) [5]

    > Сразу после вызова BitBlt поток(и)! успевают нарисовать в буфере обновленное одержимое

    у тебя больше одного дополнительного потока рисуют одну картинку?

    Тогда Synchronize тебе не поможет - оно работает только между тем кто вызвал и основным потоком.
  • Pavia © (10.02.17 08:40) [6]

    > Получается наложение нового на старое. Можно конечно 2 буфера
    > чередовать, но вдруг в WinApi что есть, чтобы дождаться
    > окончания вывода?

    Нету. Предполагается, что по выходу данные забраны на вывод.  

    Но бага есть! Толи в самой винде, толи в драйверах. Скорее первое.


    > Так у меня через TThread.Synchronize процедура обновления.

    Это ничего не значит. Нужен код или схема.


    > Игорь Шевченко ©   (09.02.17 21:39) [4]

    Азы синхронизации говорят, что если надо заблокировать данные(буфер, блок данных), то надо использовать Mutex (исключительный доступ). Или замок к примеру Canvas.Lock.
  • Игорь Шевченко © (10.02.17 10:19) [7]
    Pavia ©   (10.02.17 08:40) [6]

    Я поражаюсь твоему апломбу вкупе с твоим невежеством.
  • Pavia © (10.02.17 11:33) [8]

    > Я поражаюсь твоему апломбу вкупе с твоим невежеством.

    Люблю правду и докапываюсь до истины. А самое главное я верю в то, что я говорю так как говорю правду. И вера моя основана на книгах, которые я прочитал и на экспериментах которые я поставил.
    Все мы люди и можем ошибаться.

    Так вот с BitBlt и синхронизацией я экспериментировал правда давненько и SDK в плоть до DDK прошерстил, а вот это было пару месяцев назад.

    Что касается критической секции и мьютекса. Так это всего лишь указание на правильное использование слов с учётом их семантики. Есть такая вещь называется метафора и аналогия, и ими следует пользоваться вместо того что-бы грубо нарушать качество передачи смысла.  

    А если тут найдётся умник который скажете, что Canvas.Lock основан на критических секциях. То уж извините это называется медвежьей услугой. Так как этот человек предлагает вам начать изобретать свой велосипед вместо того что-бы пользоваться готовым.

    Вообще есть у меня в планах написать статью или брошюрку с описанием параллельности. Как что и зачем. Вот только шаблон книги надо подобрать.  Может посоветовать, что за рыбу взять?
  • dmk © (10.02.17 13:36) [9]
    >Предполагается, что по выходу данные забраны на вывод.

    Вопрос решился простым циклом ожидания.
    Вот такой цикл отрисовки.

    procedure QThread.Execute;
    var
     cP: TFloatPoint;
     Params: TDrawParams;

    label
     StopDraw;

    begin
     //Параметры по умолчанию
     Params := FillParams;

     //Центр вращения
     cP.X := cX;
     cP.Y := cY;

     //Порядковый номер потока
     NumberID := GetFreeID(ThreadID);

     //Заполняем параметры
     Params.cX := cX;
     Params.cY := cY;

     //Изменяем только угол
     Params.Angle := gAngle;

     //Отсечение всего региона
     if bClip then
       Params.R := lmR else
       Params.R := Rgn;

     //Цикл отрисовки
     while (gDrawCounter < gNumDrawCycles) do
     begin
       //Превышение индекса объектов
       //учитывается в программе отрисовки
       gDrawProc(Params);

       //Обновляет буфер самый шустрый поток
       if (ThreadID = gFirstID) then
       begin
         //Обновляем буфер
         //Synchronize(UpdateThreads); //<- Работает медленнее
         UpdateThreads; //<- Работает бытсрее
         //Прогресс
         gProgress := Round(gDrawCounter / (gNumDrawCycles - 1) * 100);
         //Кол-во циклов отрисовки
         Inc(gDrawCounter);
         //Следующий угол
         gAngle := RorAngle(gAngle, gAngleInc);
         //Сброс кол-ва отрисованных объектов
         gObjectIndex := 0;
       end;//if (gNumEntered)

       //Устанавливаем флаг завершения отрисовки
       FPaints[NumberID] := True;

       //Остальные потоки ждут здесь
       //пока первый поток не отрисует буфер
       //или все потоки не соберутся здесь
       while (not PaintsFinished) do
       begin
         //Проверка на превышение циклов отрисовки
         if (gDrawCounter >= gNumDrawCycles) then goto StopDraw;
       end;

       //Проверка на ESC
       if gESC then goto StopDraw;
     end;//while

    StopDraw:

     //Устанавливаем флаг завершения отрисовки
     FPaints[NumberID] := True;

     //Ждем здесь пока остальные потоки не закончат отрисовку
     while (not PaintsFinished) do
     begin
     end;
    end;


    Вот такая процедура вывода

    procedure UpdateThreads;
    var
     dp: TDrawParams;

    begin
     DrawProgress(gProgress, false);
     BitBlt(pDC, Rgn.X, Rgn.Y, Rgn.W, Rgn.H, sBmp.MemDC, Rgn.X, Rgn.Y, SrcCopy);

     //Счетчик обновлений
     Inc(gNumBufferUpdates);
    end;


    В таком виде работает нормально, но есть асинхронное поведение ядер.
    Несмотря на InterlockedIncrement некоторые потоки успевают нарисовать быстрее.
    Синхронность отсутствует вообще. Видимо из-за разницы в ГГц-ах.
  • NoUser © (10.02.17 13:36) [10]
    > SDK в плоть до DDK ))


    > написать статью или брошюрку
    ты не стесняйся, пиши все мысле-тезы сюды.

    ( 'Рыба': http://www.kurzenkov.com/Articles/multithreading1.html )
  • Игорь Шевченко © (10.02.17 13:54) [11]
    Pavia ©   (10.02.17 11:33) [8]


    > И вера моя основана на книгах, которые я прочитал и на экспериментах
    > которые я поставил.


    Если бы среди прочитанных книг был учебник по грамматике русского языка - было бы еще лучше. А так - просто смешно.
  • Inovet © (10.02.17 17:06) [12]
    > [8] Pavia ©   (10.02.17 11:33)
    > Вообще есть у меня в планах написать статью или брошюрку
    > с описанием параллельности. Как что и зачем. Вот только
    > шаблон книги надо подобрать.  Может посоветовать, что за
    > рыбу взять?

    Совет был дан кем-то из классиков: если можешь не писать - не пиши.
  • Eraser © (10.02.17 20:49) [13]

    > dmk ©   (09.02.17 15:47) 


    > Сразу после вызова BitBlt потоки успевают нарисовать в буфере
    > обновленное содержимое, а отрисовка BitBlt еще не завершена.
    >

    1. есть такое понятие, как двойная буферизация, погугли.

    2. если нужна скорость - выкинь GDI и используй Direct2D http://docwiki.embarcadero.com/RADStudio/Seattle/en/Using_the_Direct2D_Canvas

    3. Если вдруг надумал рисовать в потоках VCL оберткой над GDI, нужно везде использовать блокировки canvas, если интересно почему - см. FreeMemoryContexts из модуля Vcl.Graphics, там очень захватывающая история.
  • dmk © (10.02.17 23:19) [14]
    Eraser ©   (10.02.17 20:49) [13]

    Да не. Цель наоборот избавиться от привязки к чужим технологиям. От ОС нужен только вывод, а так у меня библиотека сама многое рисует. Суть мультипоточности в моем случае сделать отрисовку быстрее и сохранить порядок наложения рисуемых объектов. Чтобы результат был одинаковый в одном потоке или нескольких.

    Тут скриншот: http://hostingkartinok.com/show-image.php?id=347c22d0a4d969a5a9781c625d8f84b3
    Тут exe: https://cloud.mail.ru/public/5mkt/mK1uRZZLT

    Главное треугольник не трогать. Не допилен.
  • dmk © (10.02.17 23:20) [15]
    Под 32 бита наверно не пойдет.
  • Eraser © (11.02.17 02:16) [16]

    > dmk ©   (10.02.17 23:19) [14]

    видимо речь про многопоточный 3D рендер, а не вывод.
    мне кажется это весьма сложная тема, тут без серьезного углубления в теорию никак, с кондачка вряд ли выйдет.
  • dmk © (11.02.17 03:10) [17]
    Там и 3D и 2D. Типа своего директ икса.
  • Pavia © (11.02.17 10:12) [18]

    >  Суть мультипоточности в моем случае сделать отрисовку быстрее
    > и сохранить порядок наложения рисуемых объектов. Чтобы результат
    > был одинаковый в одном потоке или нескольких.

    Что-бы сохранить порядок нужно либо Z-буфер либо разбивать экран на разные квадраты каждый квадрат обрабатывается своим потоком.
    Либо как во всех движках на фрагменты. Все они помещаются в очередь и потоки по очереди берут из неё задание на рендеринг. Или по кадрам один кадр рисует один поток второй кадр второй поток.
  • han_malign © (20.04.17 15:12) [19]

    > Но бага есть! Толи в самой винде, толи в драйверах. Скорее первое.

    - это не бага
    https://msdn.microsoft.com/en-us/windows/hardware/drivers/display/asynchronous-rendering
    GdiFlush()
    https://msdn.microsoft.com/en-us/library/windows/desktop/dd144844(v=vs.85).aspx
 
Конференция "WinAPI" » BitBlt
Есть новые Нет новых   [119446   +39][b:0][p:0.001]