Производительность рендеринга WPF с помощью BitmapSource - PullRequest
7 голосов
/ 30 апреля 2009

Я создал элемент управления WPF (унаследованный от FrameworkElement), который отображает мозаичный рисунок, который можно панорамировать. Каждая плитка размером 256x256 пикселей при 24bpp. Я переопределил OnRender. Там я загружаю все новые плитки (как BitmapFrame), а затем рисую все видимые плитки, используя drawingContext.DrawImage.

Теперь, когда в каждом цикле рендеринга имеется больше, чем несколько новых плиток, частота кадров падает с 60 кадров в секунду до нуля примерно на секунду. Это не вызвано ни загрузкой изображений (которая занимает порядка миллисекунд), ни DrawImage (которое совсем не занимает времени, поскольку оно просто заполняет некоторую промежуточную структуру данных рендеринга).

Я предполагаю, что поток рендеринга сам дросселирует всякий раз, когда он получает большое количество (~ 20) новых экземпляров BitmapSource (то есть тех, которые он еще не кэшировал). Либо он тратит много времени на преобразование их в какой-либо внутренний DirectX-совместимый формат, либо это может быть проблема с кэшированием. Это не может быть исчерпано видео RAM; Перфоратор показывает пики ниже 60 МБ, у меня 256 МБ. Кроме того, Perforator говорит, что все цели рендеринга имеют аппаратное ускорение, поэтому этого тоже не может быть.

Любые идеи будут оценены!

Заранее спасибо

Daniel

@ RandomEngy:
BitmapScalingMode.LowQuality немного уменьшил проблему, но не избавился от нее. Я уже загружаю плитки в нужном разрешении. И это не может быть графический драйвер, который является современным (Nvidia).
Я немного удивлен, узнав, что масштабирование занимает столько времени. Как я понял, растровое изображение (независимо от его размера) просто загружается как текстура Direct3D, а затем масштабируется аппаратно. Фактически, после того, как растровое изображение было визуализировано впервые, я могу изменить его вращение и масштаб без каких-либо дальнейших остановок.

Ответы [ 2 ]

3 голосов
/ 01 мая 2009

Это не только с большим количеством изображений. Достаточно одного большого изображения, чтобы задержать рендеринг, пока оно не было загружено, и это может быть заметно, когда размеры вашего изображения начинают увеличиваться в тысячах.

Я согласен с вами, что это, вероятно, поток рендеринга: я провел тест, и поток пользовательского интерфейса все еще успешно отправлял сообщения, пока происходила задержка рендеринга из-за попытки отобразить полностью предварительно кэшированный BitmapImage.

Это должно быть что-то вроде преобразования или подготовки изображения, как вы спекулировали. Я пытался смягчить это в своем приложении, «рендеринг», но скрывая изображение, а затем раскрывая его, когда мне нужно показать его. Однако это далеко не идеально, потому что в любом случае происходит зависание при рендеринге.

(Изменить)

Некоторое продолжение: после обсуждения псевдонима MS WPF я обнаружил, что вызывает задержки. На моей машине с Server 2008 это была комбинация старых видеодрайверов, которые не поддерживают новую модель драйвера WDDM, и задержка для изменения размера изображения.

Если размер исходного изображения отличается от размера дисплея, это приведет к задержке потока рендеринга, прежде чем изображение появится. По умолчанию для изображения установлено высочайшее качество, но вы можете изменить параметры масштабирования для рендеринга, вызвав RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality);. Как только я это сделал, загадочное замораживание перед отображением изображения прошло. Альтернативой, если вам не нравится падение качества при масштабировании, является загрузка BitmapImage с DecodePixelWidth / Height, равным размеру, при котором оно будет отображаться. Затем, если вы загрузите BitmapImage в фоновом потоке, вы не должны задерживаться при его отображении.

0 голосов
/ 26 февраля 2010

Также попробуйте это;

/* ivis is declared in XAML <Image x:Name="iVis" UseLayoutRounding="True" SnapsToDevicePixels="True" /> */

iVis.Stretch = Stretch.None;
RenderOptions.SetBitmapScalingMode(iVis, BitmapScalingMode.NearestNeighbor);
RenderOptions.SetEdgeMode(iVis, EdgeMode.Aliased);
VisualBitmapScalingMode = BitmapScalingMode.NearestNeighbor;
iVis.Source = **** your bitmap source ****

У меня были некоторые проблемы с производительностью, когда я использовал огромное количество цветов канала "A", ожидая, пока изображение не будет масштабировано, и теперь оно работает намного лучше.

Кроме того, как вы сказали, что вы используете мозаичный рисунок?

Обычно вы используете TileBrush , чтобы просто установить его в качестве кисти на FrameworkElement. Если вы анимируете их или добавляете новые динамически, вы можете сгенерировать свои кисти, а затем применить их к своему объекту, как вы делаете это вручную, обязательно заморозьте их, если можете. Кроме того, VisualBitmapScalingMode является свойством любого Visual.

...