Есть VCL приложение. Хочу в тот момент, когда форма ничего не делает, а проверяет наличие сообщений, проверять пару своих переменных. Как добраться цикла, в котором PeekMessage, TranslateMessage и DispatchMessage?
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
01-07-2020 01:18 | Комментарий к предыдущим ответам
>>> Никто не читает блог GunSmoker'a
На картинке этого не написано. Ну а если знаете, где в блоге (он, кстати, немаленький) написано, как помочь автору вопроса - то почему бы не написать ссылочку на соответствующую статью? А то получается почти также, как ответ на любой вопрос по линуксу: "кури мануал".
18-05-2020 01:48 | Комментарий к предыдущим ответам
Раз это игровой движок то разумно, что надо рендерить только самую последнюю версию кадра. А если главный поток не успевает - то эти кадры, которые вроде как и рассчитаны - надо выбрасывать без сожаления. А то получится, как в картошечке всем известной: я стреляю, а снаряд идеально проходит сквозь танк, потому что для клиента танк рисуется здесь, а для сервера (который обсчитывает коллижен-модель) этот танк уже давно уехал и находится где-то далеко. Потому, кажется разумным использовать тройную буферизацию: кадр, который рассчитывается (с ним работает ТОЛЬКО дочерний поток и главный НИКОГДА не обращается), кадр, который рассчитан, но к выводу которого ещё не приступали (в него перекидывает данные из предыдущего буфера дочерний поток и забирает родительский поток, деление критическими секциями, или флагом, оперируемым по InterlockedExchange ), кадр, который уже рассчитан и уже выводится (к нему доступ только от основного потока, как только он закончит вывод предыдущего кадра - он копирует из предыдущего буфера новый рассчитанный кадр и начинает его рисовать).
Идеально предусмотреть, конечно, счётчик потерянных кадров (дочерний поток рассчитал, но основной поток не забрал) и излишнее число раз выведенных кадров то есть основной поток уже готов выводить новый кадр, но дочерний ничего не просчитал, а потому либо основной поток повторно выводит тот же кадр, либо пропускает ход). Если теряется много кадров - проблема в рендеринге, нужно сокращать количество спрайтов (вместо 1000 осколков, генерируемых гранатой - выпускать только 100, просчитывать можно все 1000, но визуализировать только 100, могут быть замечания от игрока, но зато не будет тормозов). Реализовать это можно даже динамически, непрерывно в фоновом idle потоке анализируя статистику. Хотя тут смотреть надо, если FPS достаточен (30 кадров и выше) - то ничего делать не надо, просто расчёты слишком простые, можно уронить приоритет потока расчётов. Если же наоборот, рендеринг успевает и многократно выводит один и тот же кадр - проблема в расчёте. Нужно сократить точность расчёта, усилить отсечение (например, рейтрейсиг звука вести не до затухания -200дБ, а до затухания -100дБ, уменьшить число шагов просчёта "искусственного интеллекта" и прочие расчётные упрощения, кое что можно, наверное, сделать и в динамике).
И только если расчёт и рендеринг работают согласованно, не слишком много пропуская кадром и не слишком много проводя расчётов "вхолостую" - только тогда игра будет хороша. И можно будет на первом пне поиграть в какой-нибудь FEAR. Да, ни красивых блёсток, ни отражений, да и противники не будут блистать интеллектом, но кто говорил, что владельцы столь древних компьютеров должны получать всё? Поиграл - и хватит.
Вообще-то в настройках проекта задается то, откуда что брать и куда что класть.
Второй момент, у нас все же ООП, так что можно использовать не оригинальный класс, а попробовать создать наследника, который обладает нужными особенностями. И не придется делать свою версию VCL-юнита.
Я тут вдруг подумал... что со мной бывает не часто. :) И положил копию forms.pas в папку со своим проектом. И компилятор, как я смотрю, использует теперь эту копию. Я еще не проверял, но правильно ли я понимаю, что я теперь смогу ее замодить, и получу оттуда доступ к своим переменным и отвечу на свой вопрос?
>> А какую такую супер задачу решает Ваш "поток главной нити", что каждый свой пук хочет донести немедленно?
это игровой движок. Нить считает кадры, которые главный поток должен отрисовать на канвасе. Таймингом тоже занимается нить, главный поток не может рассчитывать тайминг, он должен внешние события обрабатывать, и у него все нестабильное в этом плане.
Да, про буфер мысль хорошая... картинку и ее время в массив складывать. Может я так и сделаю, если будет плохо работать при больших массивах спрайтов. Пока их до 100 штук, вроде, и так тянет.
А может перепишу главное окно на API, там не так много надо будет сделать.
Я прошу прощения... на всякий случай обращу внимание, что в предложенной мной схеме работы ОБЯЗАТЕЛЬНО после каждого "пука" поток расчетов ОБЯЗАН вызвать, например, WaitSingleObject заведомо "опущенного" эвента с ожиданием 0. Если основному потоку нечем заняться, то расчетный поток мгновенно получит ресурсы обратно и продолжит работу. Иначе ему малость придется "покурить бамбук" - он не один в виндах, есть еще потребители тиков.
12-05-2020 21:47 | Вопрос к автору: запрос дополнительной информации
Тут... как-то... не малина, и понятие "отжать" не в словаре. А Вы, батенька, в курсе баттон-клика? Вообще основ "движка виндового"? Вы напоминаете Диму Семицветова из "Берегись автомобиля", который обвинил Деточкина в "... он посягнул на самое святое... на Конституцию...". Поэтому вопрос...
А какую такую супер задачу решает Ваш "поток главной нити", что каждый свой пук хочет донести немедленно?
Сделайте кольцевой буфер достаточного размера с результатами "пуков" и флажком. Поток сложил очередной пук и выставил флажок, пошел дальше. ВСЕ!!!
Основная нить нашла флажок, обработала "пук", флажок сняла, перешла к следующей ячейке "пуков"... и так по кругу, пока не наткнется на отсутствие флажка.
И две ситуации - потоку некуда сложить очередной "пук", основной поток обнаружил, что нет очередного "пук-а" для обработки.
Обе ситуации взаимосвязаны и несовместимы. Лучше иметь вторую - значит ресурсов основного потока хватает и беспокоиться не о чем, достаточно просто запомнить индекс в кольцевом буфере и успокоиться до очередного OnIdle. А вот первая ситуация сложнее - расчеты настолько "плевые", что их обработка дольше, чем они сами. Если, конечно, это не контроллер ядренной станции, то тупо притормозите поток с расчетами. Можете где-нить в логе это зафиксировать, типа "вот я такой быстрый - главный поток обогнал".
Вот такие мысли... вслух.
Да, вы, конечно, все правильно говорите, и реализацию Application.run я посмотрел, там видно когда onIdle вызывается.
Пользователь не заметил бы, там мало работы предполагалось делать. :)
У меня такая задачка: главное окно запускает нить, которая много считает. я смотрю тайминг в нити:
repeat
замер t1
много математики
замер t2
postmessage (главный, забери результаты)
WaitForMultipleObjects пока главный не забрал
замер t3
until StopRequired;
Так вот t2-t1 - 40 мсек, это понятно, там много работы, а t3-t2 - еще 20 мсек. Вот я и хотел эти 20 миллисекунд отжать на флагах, избавившись от сообщений и ивентов.
Может есть другой правильный способ общения потока с формой?
Пользователю не очень понравится, если приложение будет с задержкой реагировать на мышку или нажатие кнопки на клавиатуре. Поэтому и предлагаю событие OnIdle: когда все сообщения из очереди вытащены и обработаны, тогда можно и сторонними делами заняться.
Application.OnMessage - вызывается при наличии сообщения, перед обработкой Application.OnIdle - вызывается при отсутствии сообщения
В каждой итерации главного цикла отрабатывает либо первое либо второе. "Используй то, что под рукою, и не ищи себе другое" (С)
Я не совсем хорошо шарю в VCL.... процедура TApplication.ProcessMessage ведь не впадает ни в какие ожидания в момент своего выполнения? Если так, то мне можно попробовать подменить TApplication.HandleMessage на свою? Получится?
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.