Оптимизация анимаций, мультимедиа и изображений

Создавайте приложения универсальная платформа Windows (UWP) с плавной анимацией, высокой частотой кадров и высокопроизводительными захватами и воспроизведением мультимедиа.

Сделать анимацию гладкой

Ключевым аспектом приложений UWP является плавное взаимодействие. Это включает в себя сенсорные манипуляции, которые "прилипают к пальцу", гладкие переходы и анимации, а также небольшие движения, которые обеспечивают обратную связь с входными данными. В платформе XAML существует поток, называемый потоком композиции, предназначенным для композиции и анимации визуальных элементов приложения. Так как поток композиции отделен от потока пользовательского интерфейса (потока, выполняющего платформу и код разработчика), приложения могут достичь согласованной частоты кадров и плавной анимации независимо от сложных параметров макета или расширенных вычислений. В этом разделе показано, как использовать поток композиции для обеспечения плавности анимации приложения. Дополнительные сведения об анимациях см. в обзоре анимаций. Дополнительные сведения о повышении скорости реагирования приложения при выполнении интенсивных вычислений см. в статье "Сохранение отклика потока пользовательского интерфейса".

Использование независимых анимаций вместо зависимых анимаций

Независимые анимации можно вычислить с начала до конца во время создания, так как изменения свойства, анимированные не влияют на остальные объекты в сцене. Поэтому независимые анимации могут выполняться в потоке композиции вместо потока пользовательского интерфейса. Это гарантирует, что они остаются гладкими, так как поток композиции обновляется по согласованной частотой.

Все эти типы анимаций гарантированно являются независимыми:

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

Почти все анимации в платформе XAML независимы по умолчанию, но есть некоторые действия, которые можно предпринять для отключения этой оптимизации. Особенно остерегайтесь этих сценариев:

  • Задание свойства EnableDependentAnimation, чтобы разрешить зависимой анимации выполняться в потоке пользовательского интерфейса. Преобразуйте эти анимации в независимую версию. Например, анимация ScaleTransform.ScaleX и ScaleTransform.ScaleY вместо ширины и высоты объекта. Не бойтесь масштабировать объекты, такие как изображения и текст. Платформа применяет двулинейное масштабирование только в то время как ScaleTransform анимируется. Изображение или текст будет перераспрекрыт в окончательном размере, чтобы убедиться, что оно всегда ясно.
  • Создание обновлений кадра, которые эффективно зависят от анимации. Примером этого является применение преобразований в обработчике события CompositonTarget.Rendering .
  • Выполнение любой анимации, считающейся независимым в элементе с свойством CacheMode , равным BitmapCache. Это считается зависимым, так как кэш должен быть повторно растеризован для каждого кадра.

Не анимируйте webView или MediaPlayerElement

Веб-содержимое в элементе управления WebView не отображается непосредственно платформой XAML, поэтому требуется дополнительная работа для создания с остальной частью сцены. Эта дополнительная работа добавляется при анимации элемента управления на экране и может привести к проблемам синхронизации (например, HTML-содержимое может не синхронизироваться с остальным содержимым XAML на странице). Когда необходимо анимировать элемент управления WebView , переключите его на WebViewBrush в течение длительности анимации.

Анимация MediaPlayerElement является аналогично плохой идеей. Помимо ущерба производительности, он может вызвать слезоточивые или другие артефакты в видеоконтенте, воспроизводимом.

Примечание. Рекомендации в этой статье для MediaPlayerElement также применимы к MediaElement. MediaPlayerElement доступен только в Windows 10 версии 1607, поэтому если вы создаете приложение для предыдущей версии Windows, необходимо использовать MediaElement.

Использование бесконечных анимаций с разреженным

Большинство анимаций выполняются в течение указанного периода времени, но при задании свойства Timeline.Duration значение Forever позволяет анимации выполняться неограниченное время. Мы рекомендуем свести к минимуму использование бесконечных анимаций, так как они постоянно используют ресурсы ЦП и могут предотвратить переход ЦП в низкое питание или состояние простоя, что приводит к более быстрому запуску питания.

Добавление обработчика для CompositionTarget.Rendering аналогично выполнению бесконечной анимации. Обычно поток пользовательского интерфейса активен только при выполнении работы, но добавление обработчика для этого события заставляет его запускать каждый кадр. Удалите обработчик, когда не будет выполнена работа, и повторно зарегистрируйте его, когда это необходимо еще раз.

Использование библиотеки анимации

Пространство имен Windows.UI.Xaml.Media.Animation включает в себя библиотеку высокопроизводительных, гладких анимаций, которые имеют внешний вид и чувствуют себя в соответствии с другими анимациями Windows. Соответствующие классы имеют "Theme" в их имени и описаны в обзоре анимаций. Эта библиотека поддерживает множество распространенных сценариев анимации, таких как анимация первого представления приложения и создание переходов состояния и содержимого. Мы рекомендуем использовать эту библиотеку анимации, если это возможно, чтобы повысить производительность и согласованность пользовательского интерфейса UWP.

Примечание. Не все свойства можно анимировать с помощью библиотеки анимации. Сценарии XAML, в которых библиотека анимации не применяется, см. в разделе "Раскадровки анимаций".

Анимировать свойства CompositeTransform3D независимо

Вы можете анимировать каждое свойство составного объекта CompositeTransform3D независимо, поэтому применять только необходимые анимации. Примеры и дополнительные сведения см. в разделе UIElement.Transform3D. Дополнительные сведения о анимации преобразований см. в разделах раскадровки анимаций и анимации ключевых кадров и анимаций функций.

Оптимизация ресурсов мультимедиа

Аудио, видео и изображения являются убедительными формами содержимого, которые используют большинство приложений. По мере увеличения частоты захвата мультимедиа и перехода от стандартного определения к высокому определению объем ресурсов, необходимых для хранения, декодировки и воспроизведения этого содержимого увеличивается. Платформа XAML основана на последних функциях, добавленных в подсистемы мультимедиа UWP, чтобы приложения получили эти улучшения бесплатно. Здесь мы объясним некоторые дополнительные трюки, которые позволяют получить наиболее важные средства массовой информации в приложении UWP.

Выпуск потоков мультимедиа

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

Например, если приложение работает с объектом RandomAccessStream или объектом IInputStream, обязательно вызовите метод закрытия объекта, когда приложение завершит работу с ним, чтобы освободить базовый объект.

Отображение полноэкранного воспроизведения видео по возможности

В приложениях UWP всегда используйте свойство IsFullWindow в MediaPlayerElement для включения и отключения полной отрисовки окна. Это обеспечение оптимизации уровня системы используется во время воспроизведения мультимедиа.

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

Существуют законные причины наложения элементов XAML на MediaPlayerElement, которые занимают полную ширину и высоту экрана, например закрытые подпись или моментальные элементы управления транспортировкой. Не забудьте скрыть эти элементы (задать Visibility="Collapsed"), если они не нужны для обратного воспроизведения мультимедиа в его наиболее эффективное состояние.

Отображение деактивации и экономии энергии

Чтобы предотвратить деактивацию отображения, если действие пользователя больше не обнаружено, например при воспроизведении видео приложения, можно вызвать DisplayRequest.RequestActive.

Чтобы сохранить питание и время работы батареи, необходимо вызвать DisplayRequest.RequestRelease , чтобы освободить запрос на отображение, как только он больше не требуется.

Ниже приведены некоторые ситуации, когда следует освободить запрос на отображение:

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

Поместите другие элементы на сторону внедренного видео

Часто приложения предлагают внедренное представление, в котором воспроизводится видео на странице. Теперь вы, очевидно, потеряли полноэкранную оптимизацию, так как MediaPlayerElement не является размером страницы и есть другие объекты XAML. Остерегайтесь непреднамеренно входить в этот режим, нарисовав границу вокруг MediaPlayerElement.

Не рисуйте элементы XAML на вершине видео, когда он находится в внедренном режиме. Если вы делаете, платформа вынуждена сделать немного дополнительной работы, чтобы создать сцену. Размещение элементов управления транспортом под внедренным элементом мультимедиа вместо видео является хорошим примером оптимизации для этой ситуации. На этом изображении красная полоса обозначает набор элементов управления транспортировкой (воспроизведение, приостановка, остановка и т. д.).

MediaPlayerElement with overlaying elements

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

MediaPlayerElement with neighboring elements

Задержка установки источника для MediaPlayerElement

Подсистемы мультимедиа являются дорогостоящими объектами, а платформа XAML задерживает загрузку библиотек DLL и создает большие объекты до тех пор, пока это возможно. MediaPlayerElement принудительно выполняет эту работу после установки источника с помощью свойства Source. Если пользователь действительно готов к воспроизведению мультимедиа задерживает большую часть затрат, связанных с MediaPlayerElement , как можно дольше.

Настройка MediaPlayerElement.PosterSource

Настройка MediaPlayerElement.PosterSource позволяет XAML выпускать некоторые ресурсы GPU, которые в противном случае использовались бы. Этот API позволяет приложению использовать как можно меньше памяти.

Улучшение очистки мультимедиа

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

  • Обновите значение ползунка на основе таймера, который запрашивает положение в MediaPlayerElement.MediaPlayer. Убедитесь, что для таймера используется разумная частота обновления. Свойство Position обновляется только каждые 250 миллисекунда во время воспроизведения.
  • Размер частоты шага ползунка должен масштабироваться с длиной видео.
  • Подпишитесь на события PointerPressed, PointerMoved, PointerReleased на ползунке, чтобы задать для свойства PlaybackRate значение 0, когда пользователь перетаскивает большой палец ползунка.
  • В обработчике событий PointerReleased вручную установите положение носителя в положение ползунка, чтобы обеспечить оптимальную привязку отпечатков во время очистки.

Сопоставление разрешения видео с разрешением устройства

Декодирование видео занимает много памяти и циклов GPU, поэтому выберите формат видео близко к разрешению, в котором он будет отображаться. Нет смысла использовать ресурсы для декодирования видео 1080, если он будет масштабироваться до гораздо меньшего размера. Многие приложения не имеют одного и того же видео, закодированного в разных разрешениях; но если он доступен, используйте кодировку, близкую к разрешению, в котором он будет отображаться.

Выбор формата мультимедиа может быть конфиденциальным и часто определяется бизнес-решениями. С точки зрения производительности UWP мы рекомендуем видео H.264 в качестве основного формата видео и AAC и MP3 в качестве предпочитаемых аудиоформатов. Для воспроизведения локального файла MP4 является предпочтительным контейнером файлов для видеоконтента. Декодирование H.264 ускоряется с помощью последнего графического оборудования. Кроме того, хотя аппаратное ускорение для декодирования VC-1 широко доступно, для большого набора графического оборудования на рынке, ускорение ограничено во многих случаях частичным ускорением (или уровня IDCT), а не полной разгрузкой оборудования на уровне пара (т. е. в режиме VLD).

Если у вас есть полный контроль над процессом создания содержимого видео, необходимо выяснить, как обеспечить хороший баланс между эффективностью сжатия и структурой GOP. Относительно меньший размер GOP с изображениями B может повысить производительность в режиме поиска или трюка.

При включении коротких, низкой задержки звуковых эффектов, например в играх, используйте WAV-файлы с несжатыми данными PCM, чтобы сократить затраты на обработку, типичные для сжатых звуковых форматов.

Оптимизация графических ресурсов

Масштабирование изображений до соответствующего размера

Изображения записываются с очень высоким разрешением, что может привести к приложениям, использующим больше ЦП при декодировании данных изображения и больше памяти после загрузки с диска. Но декодирование и сохранение изображения с высоким разрешением в памяти нет смысла, чтобы отобразить его меньше, чем его собственный размер. Вместо этого создайте версию изображения с точным размером, нарисуйте его на экране с помощью свойств DecodePixelWidth и DecodePixelHeight.

Не делайте этого:

<Image Source="ms-appx:///Assets/highresCar.jpg"
       Width="300" Height="200"/>    <!-- BAD CODE DO NOT USE.-->

Вместо этого используйте следующий код:

<Image>
    <Image.Source>
    <BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
                 DecodePixelWidth="300" DecodePixelHeight="200"/>
    </Image.Source>
</Image>

Единицы для DecodePixelWidth и DecodePixelHeight по умолчанию являются физическими пикселями. Свойство DecodePixelType можно использовать для изменения этого поведения: задание DecodePixelType логическим результатам в размере декодирования автоматически учитывает текущий коэффициент масштабирования системы, как и другое содержимое XAML. Таким образом, обычно следует задать значение DecodePixelType Логическому, если, например, вы хотите, чтобы ДекодePixelWidth и DecodePixelHeight соответствовали свойствам "Высота и ширина" элемента управления "Изображение", в котором будет отображаться изображение. При использовании физических пикселей по умолчанию необходимо самостоятельно учитывать текущий коэффициент масштабирования системы; и вы должны прослушивать уведомления об изменении масштаба, если пользователь изменяет свои настройки отображения.

Если в декодеPixelWidth/Height явно задано больше, чем изображение будет отображаться на экране, приложение будет ненужно использовать дополнительную память (до 4 байт на пиксель), что быстро становится дорогостоящим для больших изображений. Изображение также будет уменьшено с помощью двулинейного масштабирования, что может привести к его размытию для крупных факторов масштабирования.

Если декодеePixelWidth/DecodePixelHeight явно задано меньше, чем изображение будет отображаться на экране, оно будет масштабировано и может отображаться в пикселях.

В некоторых случаях, когда соответствующий размер декодирования не может быть определен заранее, следует отложить автоматическое декодирование правого размера XAML, которое будет делать лучшие усилия, чтобы попытаться декодировать изображение по соответствующему размеру, если явное декодирование ДекодированияPixelWidth/DecodePixelHeight не указано.

Вы должны задать явный размер декодировки, если вы знаете размер содержимого изображения заранее. Кроме того, в сочетании с набором DecodePixelType следует задать значение "Логический", если указанный размер декодера соответствует другим размерам элементов XAML. Например, если вы явно задали размер содержимого с помощью Image.Width и Image.Height, вы можете задать для DecodePixelType значение DecodePixelType.Логически использовать те же логические размеры пикселей, что и элемент управления Image, а затем явно использовать BitmapImage.DecodePixelWidth и/или BitmapImage.DecodePixelHeight, чтобы управлять размером изображения для достижения потенциально большой экономии памяти.

Обратите внимание, что Image.Stretch следует учитывать при определении размера декодированного содержимого.

Декодирование правого размера

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

В приведенных выше сценариях настройка явного размера декодирования — единственный способ добиться экономии памяти.

Перед настройкой источника всегда следует подключить BitmapImage к динамическому дереву. В любой момент, когда элемент изображения или кисть указан в разметке, это будет автоматически происходить. Ниже приведены примеры под заголовком "Примеры динамических деревьев". Всегда следует избегать использования SetSource и вместо этого использовать SetSourceAsync при настройке источника потока. И рекомендуется избегать скрытия содержимого изображения (с нулевой непрозрачностью или с свернутой видимостью) во время ожидания создания события ImageOpened. Это вызов суждения: вы не сможете воспользоваться автоматическим декодированием правильного размера, если это сделано. Если приложение должно изначально скрыть содержимое изображения, то при возможности он также должен задать размер декодировки явным образом.

Примеры динамического дерева

Пример 1 (хорошо) — универсальный идентификатор ресурса (URI), указанный в разметке.

<Image x:Name="myImage" UriSource="Assets/cool-image.png"/>

Пример 2 разметки— URI, указанный в коде программной части.

<Image x:Name="myImage"/>

Пример 2 кода позади (хорошо) — подключение BitmapImage к дереву перед настройкой UriSource.

var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new URI("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);

Пример 2, код программной части (как не надо) — задание UriSource для BitmapImage до его подключения к дереву.

var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new URI("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;

Оптимизация кэширования

Оптимизация кэширования действует для изображений, использующих UriSource для загрузки содержимого из пакета приложения или из Интернета. Универсальный код ресурса (URI) используется для уникальной идентификации базового содержимого, а платформа XAML не будет загружать или декодировать содержимое несколько раз. Вместо этого он будет использовать кэшированные программные или аппаратные ресурсы для отображения содержимого несколько раз.

Исключение этой оптимизации заключается в том, что изображение отображается несколько раз в разных разрешениях (которые можно задать явным образом или с помощью автоматического декодирования правого размера). Каждая запись кэша также сохраняет разрешение изображения, и если XAML не может найти изображение с исходным универсальным кодом ресурса (URI), соответствующий требуемому разрешению, то он декодирует новую версию с таким размером. Однако он снова не скачивает данные закодированного изображения.

Следовательно, при загрузке образов из пакета приложения следует использовать UriSource и избегать использования потока файлов и SetSourceAsync, если это не требуется.

Изображения в виртуализированных панелях (ListView, например)

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

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

Растровые изображения программного обеспечения

Если изображение используется для не прямоугольной кисти или для NineGrid, изображение будет использовать путь растеризации программного обеспечения, который не будет масштабировать изображения вообще. Кроме того, он должен хранить копию образа как в программной, так и в аппаратной памяти. Например, если изображение используется в качестве кисти для многоточия, то потенциально большой полный образ будет храниться дважды внутри. При использовании NineGrid или не прямоугольной кисти приложение должно предварительно масштабировать изображения до приблизительного размера, в который они будут отображаться.

Фоновая загрузка изображения потока

XAML имеет внутреннюю оптимизацию, которая позволяет декодировать содержимое изображения асинхронно на поверхность в аппаратной памяти без необходимости промежуточной поверхности в программной памяти. Это снижает пиковое использование памяти и задержку отрисовки. Эта функция будет отключена, если выполнены какие-либо из следующих условий.

  • Изображение используется как NineGrid.
  • CacheMode="BitmapCache" устанавливается в элементе image или на любом родительском элементе.
  • Кисть изображения не прямоугольная (например, при применении к фигуре или тексту).

SoftwareBitmapSource

Класс SoftwareBitmapSource обменивается несжатными изображениями между разными пространствами имен WinRT, такими как BitmapDecoder, API камеры и XAML. Этот класс защищает дополнительную копию, которая, как правило, необходима с помощью WriteableBitmap, и это помогает уменьшить пиковую память и задержку источника на экран.

SoftwareBitmap, предоставляющий исходные сведения, также можно настроить для использования пользовательского IWICBitmap для предоставления перезагрузимого резервного хранилища, позволяющего приложению повторно сопоставить память по мере его соответствия. Это расширенный вариант использования C++ .

Приложение должно использовать SoftwareBitmap и SoftwareBitmapSource для взаимодействия с другими API WinRT, которые создают и используют изображения. Приложение должно использовать SoftwareBitmapSource при загрузке несжатых данных изображения вместо использования WriteableBitmap.

Использование GetThumbnailAsync для эскизов

Одним из вариантов использования масштабирования изображений является создание эскизов. Хотя вы можете использовать DecodePixelWidth и DecodePixelHeight для предоставления небольших версий изображений, UWP предоставляет еще более эффективные API для получения эскизов. GetThumbnailAsync создает эскизы изображений, кэшированных в файловой системе. Это обеспечивает еще большую производительность, чем API XAML, так как образ не должен быть открыт или декодирован.

FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".bmp");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

StorageFile file = await picker.PickSingleFileAsync();

StorageItemThumbnail fileThumbnail = await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 64);

BitmapImage bmp = new BitmapImage();
bmp.SetSource(fileThumbnail);

Image img = new Image();
img.Source = bmp;
Dim picker As New FileOpenPicker()
picker.FileTypeFilter.Add(".bmp")
picker.FileTypeFilter.Add(".jpg")
picker.FileTypeFilter.Add(".jpeg")
picker.FileTypeFilter.Add(".png")
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary

Dim file As StorageFile = Await picker.PickSingleFileAsync()

Dim fileThumbnail As StorageItemThumbnail = Await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 64)

Dim bmp As New BitmapImage()
bmp.SetSource(fileThumbnail)

Dim img As New Image()
img.Source = bmp

Декодирование изображений один раз

Чтобы предотвратить декодирование изображений несколько раз, назначьте свойство Image.Source из URI, а не с помощью потоков памяти. Платформа XAML может связать один и тот же универсальный код ресурса (URI) в нескольких местах с одним декодированным изображением, но он не может сделать то же самое для нескольких потоков памяти, содержащих одни и те же данные, и создает другой декодированные образы для каждого потока памяти.