Сократить время, затрачиваемое на процессор в VCL - PullRequest
3 голосов
/ 27 января 2012

Для проигрывателя MIDI важно воспроизводить ноты как можно точнее. Мне это никогда не удавалось, всегда обвиняя таймер (см. Предыдущий поток: Как предотвратить подсказки, прерывающие таймер ). Недавно я приобрел ProDelphi и начал измерять, что именно потребляет столько времени. Результат был довольно удивительным, см. Пример кода ниже.

procedure TClip_View.doMove (Sender: TObject; note, time, dure, max_time: Int32);
var x: Int32;
begin
{$IFDEF PROFILE}Profint.ProfStop; Try; Profint.ProfEnter(@self,1572 or $58B20000); {$ENDIF}
   Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
   Image.Picture.Bitmap.Canvas.MoveTo (FPPos, 0);
   Image.Picture.Bitmap.Canvas.LineTo (FPPos, Image.Height);
   x := time * GPF.PpM div MIDI_Resolution;
   Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
   Image.Picture.Bitmap.Canvas.MoveTo (x, 0);
   Image.Picture.Bitmap.Canvas.LineTo (x, Image.Height);
   FPPos := x;
//   Bevel.Left := time * GPF.PpM div MIDI_Resolution;
{$IFDEF PROFILE}finally; Profint.ProfExit(1572); end;{$ENDIF}
end; // doMove //

Измерения (без кода отладки на Intel i7-920, 2,7 ГГц):

  1. 95 микросекунд для кода, как показано
  2. 5,609 миллисекунд, когда все закомментировано, за исключением теперь закомментированного оператора (Bevel.Left :=)
  3. 0,056 микросекунд, когда весь код заменяется на x := time * GPF.PpM div MIDI_Resolution;

Простое перемещение по Bevel стоит в 60 раз дороже, чем просто рисование на Canvas. Это удивило меня. Результаты измерения 1 очень хорошо слышны (происходит нечто большее, чем просто это), но 2 и 3 нет. Мне нужна какая-то форма обратной связи с пользователем, так как то, что сейчас обрабатывает игрок, принята какая-то линия над барабаном пианино. В моем бесконечном поиске сокращения циклов ЦП в цикле timed-event у меня есть несколько вопросов:

  • Почему перемещение по скосу стоит столько времени?
  • Есть ли способ уменьшить больше циклов ЦП, чем при рисовании на растровом изображении?
  • Есть ли способ уменьшить мерцание при рисовании?

Ответы [ 3 ]

10 голосов
/ 27 января 2012

Вы не сможете изменить мир, ни VCL, ни Windows.Я подозреваю, что вы просите слишком много к этим ...

ИМХО, вам лучше немного изменить свою архитектуру:

  • Обработка звука должна быть в одном (или более) отдельном потоке(s) и не должны быть вообще связаны с пользовательским интерфейсом (например, не отправлять сообщения GDI с него);
  • Обновление пользовательского интерфейса должно выполняться с использованием таймера с разрешением 500 мс (полсекунды обновлениядостаточно реактивный), не каждый раз, когда происходит изменение.

То есть секвенсор не будет обновлять пользовательский интерфейс, но пользовательский интерфейс периодически спрашивает секвенсор, каково его текущее состояние.Это будет ИМХО намного более гладким.

Чтобы ответить на ваши точные вопросы:

  • "Перемещение фаски" фактически отправляет несколько сообщений GDI, и рендеринг будет выполняться GDIстек (gdi32.dll) с использованием временной битовой карты;
  • Попробуйте использовать меньшую битовую карту или попробуйте использовать отображение буфера Direct X;
  • Попробуйте DoubleBuffered := true в событии TForm.OnCreate,или используйте выделенный компонент (TPaintBox) с глобальным растровым изображением для всего содержимого компонента, с чем-то вроде этого для сообщения WM_ERASEBKGND.

Некоторый код:

   procedure TMyPaintBox.WMEraseBkgnd(var Message: TWmEraseBkgnd);
   begin
     Message.Result := 1; // no erasing is necessary after this method call
   end;
3 голосов
/ 27 января 2012

У меня такое ощущение, что ваша битовая буферизация неверна. Когда вы перемещаете свой клип, его вообще не нужно перерисовывать. Вы можете попробовать с этой структурой компонента клипа:

TMidiClip = Class(TControl)
Private
 FBuffer: TBitmap;
 FGridPos: TPoint;
 FHasToRepaint: Boolean;
Public
  Procedure Paint; Override; // you only draw the bitmap on the control canvas
  Procedure Refresh; // you recompute the FBuffer.canvas
End;

Когда вы изменяете некоторые свойства, такие как «длина галочки клипа», вы устанавливаете для «FHasToRepaint» значение true, но не при изменении «FGridPos» (позиция в сетке). Так что большую часть времени в вашем событии Paint у вас есть только копия вашего FBuffer.

На самом деле, это очень зависит от дизайна вашей сетки и ее дочерних элементов (клипов). Я могу ошибаться, но кажется, что ваш дизайн недостаточно разложен в элементах управления: основная сетка должна быть TControl, клип должен быть TControl, даже события в клипе должны быть некоторыми TControls ... Вы можете определить только таким образом, сильно оптимизированная система буферизации растровых изображений (или «двойная буферизация»).

О таймере: вы должны использовать музыкальные часы, которые обрабатывают на аудиосэмпл , иначе у вас не будет достаточно хорошего разрешения. Такие часы могут быть реализованы с помощью «драйвера аудио Windows» (mmsystem.pas) или драйвера «Asio» (у вас есть интерфейс в BASS, например, проект Delphi Asio Vst).

2 голосов
/ 27 января 2012

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

Судя по вашим комментариям, похоже, что ваша обработка звука блокируется потоком пользовательского интерфейса. Не позволяйте этому случиться, и ваши проблемы со звуком исчезнут. Если вы используете что-то вроде TThread.Synchronize для запуска событий VCL из аудиопотока, это будет блокировать в потоке пользовательского интерфейса. Вместо этого используйте асинхронный метод связи.

Предлагаемая вами альтернатива ускорения VCL не является жизнеспособной. Вы не можете изменить VCL, и даже если бы вы могли, узким местом может быть легко лежащий в основе код Windows.

...