kaktus Опубликовано 17 августа, 2020 Поделиться Опубликовано 17 августа, 2020 (изменено) Здравствуйте. Кто может подсказать по поводу многопоточной отрисовки графики посредством GDI ? Суть в следующем - экран разбит на 5 столбцов шириной примерно 200 пикселей. В каждый столбец рисую несколько графиков используя Canvas.Pixels либо Canvas.MoveTo,LineTo. С целью ускорения вывода сделал отрисовку каждого столбца в своём потоке, а после отрисовки всех столбцов многопоточно,далее в основном потоке делаю вывод на экран посредством Canvas.Draw. Для каждого столбца создаётся своя bitmap на собственном CompatibleDC.Однако скорость не увеличивается, а наоборот уменьшается и довольно сильно. Путём отладки выяснил, что в момент выполнения Canvas.Pixels поток лочится и другие потоки не работают. Ещё раз повторю для каждого потока создаётся собственная bitmap привязанная к совственному CompatibleDC. т.е. при выполнении Thread(N).Bitmap.Canvas.Pixels[x,y] := RandomColor все остальные Thread лочатся до завершения операции в Thread(N). И не просто до завершения, а лок висит ещё довольно долго, что приводит к увеличению времени отрисовки по срвнению с однопоточным режимом. Реальные цифры таковы в однопоточном режиме заполнение одного столбца 400X100 случайными пикселями происходит примерно за 16 мсек. Двух столбцов 32 мсек. В многопоточном режиме - один столбец,один поток (блокировок потоков не возникает) 16мсек. два столбца, два потока 210 мсек. (чем больше столбцов тем дольше рисует по сравнению с однопотоком) выход пока видится только в создании массивов плоскостей пикселей не привязанных ни к какому DC и отрисовка на этих плоскостях графических примитивой с помощью например алгоритмов Брезенхема. Но хотелось бы DrawText со всем шрифтовым богатством Windows. Вопрос в следующем - рисовал ли кто в многопотоке функциями GDI и каким образм ? ps: WinXp / Delphi7 Изменено 17 августа, 2020 пользователем kaktus дополнение Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 19 августа, 2020 Поделиться Опубликовано 19 августа, 2020 В 17.08.2020 в 19:56, kaktus сказал: Thread(N).Bitmap.Canvas.Pixels[x,y] Загляни в реализцию этого метода. Я не уверен, что он использует чистый GDI. Вполне возможно, внутри ставится какой-то лок, который общий для всего окна. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 19 августа, 2020 Автор Поделиться Опубликовано 19 августа, 2020 Thread(N).Bitmap.Canvas.Pixels[x,y] наверняка лочится, однако я отрабатывал множество разных вариантов, в том числе и вариант API SetPixel(DC,X,Y,Color) - увы он тоже лочит потоки. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 20 августа, 2020 Поделиться Опубликовано 20 августа, 2020 13 часов назад, kaktus сказал: увы он тоже лочит потоки. Скорее всего, лочится не поток, а контекст. А поток ожидает его освобождения. Попробуй в потоке создавать контекст в памяти, а потом отображать его на нужный контекст блиттингом. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 20 августа, 2020 Автор Поделиться Опубликовано 20 августа, 2020 Я так и делал, вот код потока Спойлер mainDC := GetDC(vHndl); Try fDC := CreateCompatibleDC (mainDC); fBitmap := CreateCompatibleBitmap (mainDC,400,100); oldBitmap := SelectObject (fDC,fBitmap); For iy := 0 To 100 - 1 Do For ix := 0 To 200 - 1 Do SetPixel(fDC,ix,iy,RGB (random(255),random(255),random(255))); SelectObject (fDC,oldBitmap); DeleteObject(fBitmap); DeleteDC(fDC); ReleaseDC(vHndl,mainDC); Finally InterlockedDecrement(DV^); End; Это было для проверки времени выполнения и до вывода результата даже не дошло. Если закомментить SetPixel - то блокировок не возникает на любом количестве потоков. Однако любая графическая операция тут же начинает блокировать поток. Потоки начинают выполнятся последовательно. Я даже делал так - создавал и удалял CompatibleDC в основном потоке (использовал массив) а потокам раздавал уже готовый bitmap ( думал что может GetDC блокировало) - но нет, не помогло. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 20 августа, 2020 Поделиться Опубликовано 20 августа, 2020 Ну все правильно. Ты основной контекст получил, но не отпустил его. Зачем ты его держишь во время операций с контекстом из памяти? Получил - создал совместимый контекст - отпустил основной. Поработал с совместимым, блиттингом загнал его на основной и все. Может быть, этом причина. Но это не точно. Проверь, если не поможет, попробуй переключиться на DIB секции. У меня в уроке по созданию трейнера на плюсах есть работа с диб секциями. Но основной контекст не держи, мой тебе совет. А вообще, не видя весь код, сложно что-либо советовать. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 20 августа, 2020 Автор Поделиться Опубликовано 20 августа, 2020 Я так делал : Создавал несколько CompatibleDC/Compatible bitmap в основном потоке и отпускал DC. Затем запускал потоки и после их завершения освобождал Compatible ресурсы. Увы - тоже не взлетело. А как прикрепить файл - rar архив,? Я не нашёл. ps: Мне почему то кажется, что сама операция SetPixel содержит код который включает механизм блокировки, (например SetPixel вызывает SendMessage) я в гугле нашёл множество аналогичных вопросов на эту проблему, но не нашёл ни одного решения - кроме отсылок к msdn и уверений, что это возможно "мамой клянусь". Я больше склоняюсь к мысли что это невозможно и придётся работать с массивами и низкоуровневыми алгоритмами отрисовки. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 20 августа, 2020 Автор Поделиться Опубликовано 20 августа, 2020 (изменено) Про SendMessage в SetPixel я конечно погорячился но мне простительно, это я от отчаяния. (Что конечно не отрицает возможности наличия других типов блокировок в графических функциях GDI) Привожу максимально упрощённый код для тестирования многопоточной отрисовки. К качеству прошу не придираться - код для демонстрации. https://fex.net/ru/s/lxdcmrp не смог найти как прикрепить файл к форуму, через некоторое время ссылка протухнет и для потомков ничего не останется. ps: С++ я тоже понимаю со словарём Поэтому если кто-нибудь решится привести рабочий пример многопоточной отрисовки на C++ это тоже будет считаться благородным поступком и зачтётся плюсиком в карму. Изменено 20 августа, 2020 пользователем kaktus дополнение Ссылка на комментарий Поделиться на другие сайты Поделиться
imaginary Опубликовано 20 августа, 2020 Поделиться Опубликовано 20 августа, 2020 Не знаю как работает Bitmap.Canvas.Pixels[x,y], но мне кажется это не просто доступ к массиву, если бы у тебя сразу bitmap обрабатывалась как массив, тогда бы у тебя и один поток наверное всё бы успевал, SetPixel это вообще супер медленный метод! А несколько потоков простых, например созданных через CreateThread и работающих со своими bitmap вообще никак не могут друг друга заблокировать, они при такой проблеме скорей бы вызвали ошибку, это всё язык на котором ты пишешь и его навороты, что он там сотворяет внутри. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 20 августа, 2020 Автор Поделиться Опубликовано 20 августа, 2020 (изменено) Цитата ... SetPixel это вообще супер медленный метод! ... Да это вобщем-то неважно, речь идёт о том что параллельная работа с GDI невозможна. Не нравится SetPixel - попробуй TextOut раз 500 на поток. Окажется что в однопотоке это всё выполняется быстрее чем в многопотоке. А если попытаться проследить выполнение кода - окажется, что потоки встают в очередь и выполняются последовательно. Цитата ... А несколько потоков простых, например созданных через CreateThread и работающих со своими bitmap вообще никак не могут друг друга заблокировать... - Угу, я тоже так думал. Попробуй на C++ это сотворить - сильно удивишся. Блокировка идёт не на уровне bitmap (я про тот который HBITMAP потому что tBimap считается потокобезопасным, а значит однозначно набит Locka-ами/UnLock-ами ) и блокировка идёт даже не на уровне bitmap а скорее либо на уровне драйвера видеокарты (и CompatibleDC тут не спасает) либо сама Windows не позволяет выполнять графические функции API в нескольких потоках. Цитата ...это всё язык на котором ты пишешь и его навороты... Та там практически чистый API. Всякие удобные обёртки типа tThread к проблеме отношения не имеют. Посмотри код который я приложил - он хорошо документирован. Попробуй его выполнить на C++ тогда поймёшь масштаб беды Я конечно могу на С++ пример приложить, но на это уйдёт крайне много времени т.к. мне придётся начинать с установки среды программирования. И программу на C++ мне придётся писать чуть ли не с букварём. Я надеюсь что на форуме есть люди которые умеют и C++ и ObjectPascal. Изменено 21 августа, 2020 пользователем Xipho Для цитирования нужно выделять часть текста собеседника, которую хочешь процитировать, и кликнуть по появившемуся "цитировать выделенное". В следующий раз будет пред. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 21 августа, 2020 Поделиться Опубликовано 21 августа, 2020 9 часов назад, kaktus сказал: Я надеюсь что на форуме есть люди которые умеют и C++ и ObjectPascal. Есть такие люди у нас. И imaginary умеет в плюсы. Возможно, она сможет вопроизвести твою проблему. Я тоже попробую это сделать, но у меня со свободным временем очень туго, увы. Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 23 августа, 2020 Автор Поделиться Опубликовано 23 августа, 2020 Хм... Всё страньше и страньше ... Напоминаю - программа заполняет две области 400x100 случайными пикселями. Мучая несчастный GDI заметил такую странность. (Я сейчас исключительно про однопоточный режим. Вся работа происходит в основном потоке программы). Время работы программы меняется непредсказуемым образом, но принимает всего три значения - 32 мсек. 86мсек и 130мсек. ну плюс/минус конечно. Измеритель у меня точный - QueryPerfomanceCounter, не какой-нибудь там GetTickCount . Комп не занят, 4ре ядра... Ещё страньше то, что API SetPixel выполняется дольше чем Canvas.SetPixels[x,y] Хотя Canvas.SetPixels обёртка вокруг API SetPixel. Чудеса вобщем. Ну да ладно - это чисто теоретический интерес, пока отложим. Приятная новость заключается в том, что под Win7 время заполнения на нескольких потоках по крайней мере не больше чем на одном потоке. Уже радует. Ложка дёгтя тут заключается в том, что есть некая технологическая программа, которая работает исключительно на winXP (HASP, там ... все дела). И на таких компах ускорения отрисовки не получится. Хотя возможно и Windows7 не взлетит, проверю - напишу результат. Далее по плану помучать DIBsection. Есть подозрение что CompatibleDC это всё таки про видеокарту и соответственно не shared ресурс, а DIB это про ОЗУ и возможно windows не будет разделять доступ к таким ресурсам. ps: Если я пишу где-то глупость, то не стесняйтесь поправляйте меня. Я полез Куда Макар телят не гонял в несвойственные мне дебри и естественно буду косячить, Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 23 августа, 2020 Автор Поделиться Опубликовано 23 августа, 2020 Ура - DIB работает !!! И все странности со временем выполнения пропали. Скорость отрисовки тоже повысилась. 1 столбец - 10мсек. 2 столбца 14 мсек. Я так-же очень доволен тем фактом что нужные мне графические функции API типа TextOut поддерживают полноценную работу с DIB. Так-же приятный бонус, что теперь можно писать напрямую в массив памяти и несомненно это будет быстрее чем SetPixel (.... не выдержал, проверил - 2 мсек. Более чем 10и кратное ускорение, если учитывать накладные расходы на обслуживание потоков..) Предварительные выводы - 1.CreateCompatibleBitmap создаёт BITMAP привязанный к дайверу видеокарты (возможно к памяти видеокарты) 2. Параллелить вывод (если позволяет логика приложения) можно используя DIB секции. А судя по странностям с задержками вывода на основной DC так вообще предпочтительнее выводить сначала в DIB а потом BitBlt в основной DC. Всё равно народ в основном через двойной буфер выводит, так и пусть этот буфер будет DIB типа. 3. DIB секции поддерживают графические операции API и прямой доступ к плоскости изображения и скорее всего поддерживают все графические функции API (например кривые Безье) 4. Я маньяк (волосы торчат, глаза красные - пора спать) Если какие вопросы будут - пишите в ветку, спрашивайте. Я ещё какое-то время сюда буду заходить на всякий случай. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 23 августа, 2020 Поделиться Опубликовано 23 августа, 2020 Хотел предложить попробовать DIB секции, но ты уже сам до этого варианта дошел )) Молодец ) Я рад, что у тебя получилось ) Ссылка на комментарий Поделиться на другие сайты Поделиться
kaktus Опубликовано 23 августа, 2020 Автор Поделиться Опубликовано 23 августа, 2020 В 20.08.2020 в 21:41, Xipho сказал: Проверь, если не поможет, попробуй переключиться на DIB секции. У меня в уроке по созданию трейнера на плюсах есть работа с диб секциями Ты предложил - и я услышал. Тестирование DIB секций я начал с твоей подачи. Тебе большое человеческое спасибо. Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 23 августа, 2020 Поделиться Опубликовано 23 августа, 2020 2 часа назад, kaktus сказал: Ты предложил - и я услышал А, старею ) Забыл, что все-таки успел это предложить ранее ) Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения