Перевести эту статью меня побудило обсуждение записей «Почему WPF самый живойЭ» И «Семь лет WPF: что изменилосьЭ» Оригинальная статья была написана в 2011 году, когда еще был жив Silverlight, но информация о WPF не потеряла своей актуальности.
Сначала я не хотел публиковать эту статью.
Мне показалось, что это невежливо – о погибших надо говорить либо хорошо, либо ничего.
Но несколько разговоров с людьми, мнением которых я очень дорожу, заставили меня изменить свое мнение.
Разработчики, вложившие много сил в платформу Microsoft, должны знать о внутренних особенностях ее работы, чтобы в случае захода в тупик понять причины произошедшего и точнее сформулировать свои пожелания разработчикам платформы.
.
Я думаю, что WPF и Silverlight — хорошие технологии, но… Если вы следили за моим Твиттером в течение последних нескольких месяцев, некоторые из сказанных вещей могут показаться необоснованными нападками на производительность WPF и Silverlight. Почему я это написал? В конце концов, я потратил тысячи и тысячи часов своего времени на протяжении многих лет, продвигая платформу, развивая библиотеки, помогая членам сообщества и так далее.
Мне определенно лично интересно.
Я хочу, чтобы платформа становилась лучше.
Производительность, производительность, производительность
При разработке привлекательного, ориентированного на пользователя пользовательского интерфейса производительность является вашим приоритетом номер один.
Без этого все остальное не имеет смысла.
Сколько раз вам приходилось упрощать интерфейс из-за того, что он тормозил? Сколько раз вы придумывали «новую, революционную модель пользовательского интерфейса», которую приходилось выбрасывать в мусор, потому что существующие технологии не позволяли ее реализовать? Сколько раз вы говорили клиентам, что им нужен четырехъядерный процессор с частотой 2,4 ГГц, чтобы получить наилучшие впечатления? Клиенты неоднократно спрашивали меня, почему они не могут получить такое же удобство работы с WPF и Sliverlight, как с приложением для iPad, даже если компьютер в четыре раза мощнее.
Эти технологии могут подойти для бизнес-приложений, но они явно не подходят для потребительских приложений следующего поколения.
Но WPF использует аппаратное ускорение.
Почему вы считаете это неэффективным? WPF использует аппаратное ускорение, и некоторые его внутренние реализации реализованы очень хорошо.
К сожалению, эффективность графического процессора намного ниже, чем могла бы быть.
Система рендеринга WPF использует очень грубую силу.
Я надеюсь объяснить это утверждение немного ниже.
Анализ одного прохода рендеринга WPF Чтобы проанализировать производительность, нам нужно понять, что на самом деле происходит внутри WPF. Для этого я использовал PIX, профилировщик Direct3D, входящий в состав DirectX SDK. PIX запускает ваше приложение D3D и внедряет ряд перехватчиков во все вызовы Direct3D для их анализа и мониторинга.
Я создал простое приложение WPF, которое отображает два эллипса слева направо.
Оба эллипса одного цвета (#55F4F4F5) с черным контуром.
И как WPF это отображает?
Прежде всего, WPF очищает (#ff000000) грязную область, которую собирается перерисовать.
Грязные регионы нужны для уменьшения количества пикселей, отправляемых на финальную стадию слияния в конвейере графического процессора.
Мы можем даже предположить, что это уменьшит количество геометрии, которую придется повторно тесселить, подробнее об этом чуть позже.
После очистки загрязненного участка наш каркас выглядит вот так
После этого WPF делает что-то странное.
Сначала он заполняет буфер вершин, а затем рисует что-то похожее на прямоугольник поверх загрязненной области.
Сейчас кадр выглядит вот так (захватывающе, не правда ли?):
После этого он мозаизирует эллипс на ЦП.
Тесселяция, как вы, наверное, уже знаете, превращает геометрию нашего эллипса 100x100 в набор треугольников.
Это делается по следующим причинам: 1) треугольники являются естественной единицей рендеринга для графического процессора 2) тесселяция эллипса может привести всего к нескольким сотням треугольников, что намного быстрее, чем растеризация 10 000 пикселей со сглаживанием с использованием процессора (который Silverlight делает).
На скриншоте ниже показано, как выглядит тесселяция.
Читатели, знакомые с 3D-графикой, возможно, заметили, что это треугольные полоски.
Обратите внимание, что при тесселяции эллипс выглядит незавершенным.
На следующем этапе WPF берет тесселяцию, загружает ее в буфер вершин графического процессора и выполняет еще один вызов отрисовки с использованием пиксельного шейдера, настроенного на использование «кисти», настроенной в XAML.
Помните, я отметил неполноту эллипса? Это верно.
WPF генерирует то, что программисты Direct3D называют «списком строк».
Графический процессор понимает линии так же хорошо, как и треугольники.
WPF заполняет буфер вершин этими строками и знаете что? Верно, делает еще один вызов отрисовки? Набор строк выглядит следующим образом:
Теперь WPF закончил рисовать эллипс, не так ли? Нет! Вы забыли про схему! Контур – это также набор линий.
Это также отправляется в буфер вершин и выполняется еще один вызов отрисовки.
Схема выглядит так
На данный момент мы нарисовали один эллипс, поэтому наша рамка выглядит так:
Всю процедуру необходимо повторить для каждого эллипса в сцене.
В нашем случае дважды.
Я не понял.
Почему это плохо для производительности? Первое, что вы могли заметить, это то, что для визуализации одного эллипса нам потребовалось три вызова отрисовки и два вызова буфера вершин.
Чтобы объяснить неэффективность такого подхода, мне придется немного поговорить о том, как работает графический процессор.
Во-первых, современные графические процессоры работают ОЧЕНЬ БЫСТРО и асинхронно с процессором.
Но некоторые операции требуют дорогостоящего перехода из пользовательского режима в режим ядра.
Когда буфер вершин заполнен, его необходимо заблокировать.
Если буфер в данный момент используется графическим процессором, это заставляет графический процессор синхронизироваться с процессором и значительно снижает производительность.
Буфер вершин создается с помощью D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, но при блокировке (что случается часто) D3DLOCK_DISCARD не используется.
Это может привести к потере скорости (синхронизация графического процессора и процессора) графического процессора, если буфер уже используется графическим процессором.
В случае большого количества вызовов отрисовки мы имеем большую вероятность получить множество переходов в режим ядра и большую нагрузку на драйверы.
Чтобы повысить производительность, нам нужно передать как можно больше работы на графический процессор, иначе ваш процессор будет занят, а графический процессор будет простаивать.
Помните, что в этом примере мы говорили только об одном кадре.
Типичный интерфейс WPF пытается выводить 60 кадров в секунду! Если вы когда-либо пытались выяснить, почему ваш поток рендеринга использует так много ресурсов ЦП, вы, скорее всего, обнаружили, что большая часть нагрузки исходит от вашего драйвера графического процессора.
А как насчет кэшированной композиции? Это действительно повышает производительность! Без сомнения, оно увеличивается.
Кэшированная конструкция или BitmapCache кэширует объекты в текстуру графического процессора.
Это означает, что ваш процессор не нуждается в повторной тесселяции, а ваш графический процессор не нуждается в повторной растеризации.
При выполнении одного прохода рендеринга WPF просто использует текстуру из видеопамяти, повышая производительность.
Вот BitmapCache эллипса:
Но и в этом случае у WPF есть свои темные стороны.
Он выполняет отдельный вызов отрисовки для каждого BitmapCache. Не буду врать, иногда вам действительно нужно выполнить вызов отрисовки для рендеринга одного объекта (визуального).
Все может случиться.
Но давайте представим себе сценарий, в котором у нас есть с 300 анимированными эллипсами BitmapCached. Продвинутая система поймет, что ей необходимо отобразить 300 текстур, и все они расположены одна за другой в z-упорядочении.
После этого он будет собирать пакеты максимального размера, насколько я помню, DX9 может принимать до 16 входов сэмплера одновременно .
В этом случае мы получим 16 вызовов отрисовки вместо 300, что существенно снизит нагрузку на процессор.
В пересчете на 60 кадров в секунду мы уменьшим нагрузку с 18 000 вызовов отрисовки в секунду до 1125. В Direct 3D 10 количество входящих элементов намного выше .
Хорошо, я дочитал до этого места.
Расскажите мне, как WPF использует пиксельные шейдеры! WPF имеет расширяемый API пиксельных шейдеров и некоторые встроенные эффекты.
Это позволяет разработчикам добавлять в свой пользовательский интерфейс поистине уникальные эффекты.
При попытке сэмплировать шейдер на существующую текстуру в Direct 3D обычно используется промежуточный объект рендеринга.
в конце концов, вы не можете сэмплировать текстуру, в которую записываете! WPF тоже делает то же самое, но, к сожалению, он создает совершенно новую текстуру КАЖДЫЙ КАДР и уничтожает ее по завершении.
Создание и уничтожение ресурсов графического процессора — одна из самых медленных вещей, которые вы можете делать покадрово.
Обычно я не делаю этого даже при таком же объеме выделенной системной памяти.
Путем повторного использования этих промежуточных поверхностей можно добиться очень значительного повышения производительности.
Если вы когда-нибудь задавались вопросом, почему ваши шейдеры с аппаратным ускорением оказывают заметную нагрузку на ваш процессор, теперь вы знаете ответ. Но, может быть, именно так и следует рендерить векторную графику на графическом процессоре?
Microsoft приложила немало усилий для исправления этих проблем, к сожалению, это было сделано не в WPF, а в Direct 2D. Посмотрите на эту группу из 9 эллипсов, визуализированных Direct2D:
Помните, сколько вызовов отрисовки WPF требуется для рендеринга.
один эллипс с контуром? А как насчет блокировок буфера вершин? Direct2D делает это за ОДИН вызов отрисовки.
Тесселяция выглядит так
Direct 2D пытается отрисовать как можно больше одновременно, максимально используя графический процессор и минимизируя нагрузку на процессор.
Прочтите статью: рендеринг Direct2D на сайте конец этой страницы , где Марк Лоуренс объясняет многие внутренние принципы работы Direct 2D. Вы можете заметить, что несмотря на скорость Direct 2D, во второй версии есть еще больше областей, где он будет улучшен.
Вполне возможно, что версия 2 Direct 2D будет использовать тесселяцию с аппаратным ускорением DX11. Глядя на API Direct 2D, можно с уверенностью предположить, что значительная часть кода была взята из WPF. Смотри старое видео про Авалон , в котором Майкл Валлент рассказывает о разработке замены GDI на основе этой технологии.
Он имеет очень похожий API геометрии и терминологию.
Внутри он похож, но очень обтекаемый и современный.
А как насчет Сильверлайта? Я мог бы использовать Silverlight, но это было бы излишним.
Производительность рендеринга в Silverlight также низкая, но по другим причинам.
Он использует для рендеринга ЦП (даже шейдеры, насколько я помню, частично написаны на ассемблере), но ЦП как минимум в 10-30 раз медленнее GPU. В результате у вас остается гораздо меньше ресурсов ЦП для рендеринга пользовательского интерфейса и еще меньше для логики вашего приложения.
Его аппаратное ускорение очень слабое и почти точно копирует кэшированную сборку WPF и ведет себя аналогичным образом, вызывая отрисовку каждого объекта с помощью BitmapCache (визуальный элемент BitmapCached).
Так что же нам теперь делать? Этот вопрос мне часто задают клиенты, у которых есть проблемы с WPF и Silverlight. К сожалению, у меня нет четкого ответа.
Те, кто может создавать свои собственные структуры, адаптированные к их конкретным потребностям.
Остальным приходится с этим мириться, так как альтернатив WPF и SL в их нишах нет. Если мои клиенты просто разрабатывают бизнес-приложения, то у них не возникает особых проблем со скоростью, и они просто наслаждаются производительностью программистов.
Настоящие проблемы возникают у тех, кто хочет создавать действительно интересные интерфейсы (например, потребительские приложения или киоск-приложения).
После начала перевода появились новости о запланированной оптимизации производительности и использовании DX10-11 в WPF 4.6. Будут ли решены описанные в статье проблемы из новостей, не совсем ясно.
Оригинальная статья: Критическое глубокое погружение в систему рендеринга WPF Теги: #wpf #C++ #.
NET #Windows #.
NET #C++ #Разработка Windows
-
Огромная Денежная Прибыль На Автопилоте
19 Oct, 24 -
Получите Выгоду От Ввода Данных На Дому
19 Oct, 24 -
Удаленная Ит-Поддержка Выбор Компании
19 Oct, 24 -
Умный Дом На Базе Контроллера Rubetek Evo
19 Oct, 24 -
Любимый Белый Слон Падишаха
19 Oct, 24 -
Php-Дайджест № 131 (13 – 27 Мая 2018 Г.)
19 Oct, 24 -
Пойдем Пешком, А Google Нам Поможет
19 Oct, 24 -
Спбгу Приравняли К Мгу
19 Oct, 24