— Pixels Commander

[ In English, На русском ]

Рендеринг HTML/CSS через WebGL для максимального быстродействия и безграничных возможностей анимации в Web

В последнее время в среде веб разработчиков активно развернулась дискуссия на тему «DOM — это медленно». Этот тезис действительно справедлив. Любое изменение DOM создает волну событий по всему документу и если десктопные браузеры могут справиться с такой нагрузкой, то мобильные и встроенные системы зачастую буксуют. Именно сложность DOM модели не позволяет достигнуть заветных 60 FPS, создает задержки при анимациях и всячески расстраивает пользователей и разработчиков.

Самым болезненным моментом является анимирование слоев. Решение проблемы плавной анимации — одна из основных задач кросс-платформенной Web разаработки. Активные исследования в этой области идут уже давно ( получив дополнительное ускорение в 2011 году после отказа Facebook от разработки Web приложения), однако первые системные решения были получены совсем недавно. Все они в целом делятся на два направления:

  • Ускорение классических DOM операций;
  • Использование альтернативных способов рендеринга контента.

И если первая ветвь — ускорение DOM уже уперлась в теоретические возможности современных API, то альтернативные методы рендеринга еще обладают большим нераскрытым потенциалом. Одним из самых значительных достижений в области создания NO DOM приложений стал React-Canvas, использующий как понятно из названия Canvas API для отображения контента, при этом он позволяет по прежнему оперировать текстовым контентом и реализовывать сетки с помощью React компонентов <Surface>, <Layer>, <Group>, <Text>, <Image>, <ListView>. Этот подход показавший неплохие результаты с точки зрения быстродействия страдает врожденными пороками вероятно не совместимыми с жизнью:

  • Лучшее решение будет независимо от конкретного фреймворка и будет обладать скорее свойствами полифила для проблемы быстродействия;
  • Оно не будет предлагать разработчику новые концепции и компоненты, оставляя его работать в привычном HTML/CSS окружении;
  • Мир игровых движков давно прошел этап соревнования 2d Canvas / WebGL. Уже несколько лет считается общепризнанным более высокое быстродействие последнего. Идеальное решение будет использовать WebGL с фоллбеком на Canvas;
  • Хорошее решение не будет лишать разработчика DOM дерева, ведь дерево позволяет отслеживать состояние приложения, эффективно отлаживать и стилизовать контент через Dev Tools.

Исходя из этих тезисов мы начали работу над своим решением проблемы «медленного  DOM». Результатом исследований стал HTML GL — библиотека позволяющая рендерить HTML/CSS через WebGL, не требующая от разработчиков изучения новой предметной области или привязки к конкретному фреймворку.

 

Как работать с HTML GL?

GitHub репозиторий

Подключите htmlgl.js к проекту. Используйте имя тега <html-gl> для элементов которые собираетесь анимировать. Эти элементы будут отображены в WebGL, а их CSS Transform свойство станет фасадом для трансформаций их WebGL представлений. Сам DOM элемент не будет отображен или анимирован. Все трансформации происходят на GPU и касаются лишь WebGL текстуры представляющей элемент, что значительно сокращает количество расходуемых ресурсов. Отличительной особенностью HTML GL является наличие пусть скрытого, но актуального DOM дерева которое часто бывает полезно при отладке приложения.

 

Примеры

  • FX Demo WebGL — это не только скорость, но и безграничные возможности для эффектов
  • Basic HTML GL демонстрация использования HTML GL на простом контенте, выполняется анимации элемента с помощью Velocity.js, а так же трансформации через CSS Transform
  • Basic DOM тот же проект, однако HTML GL отключен и все операции выполняются над DOM узлами, можно заметить, что в пик анимации слой рисуется заново, что равносильно задержке на мобильном устройстве
  • Advanced content HTML GL слайдер с несколькими уровнями вложенности содержимого, отображен через HTML Gl и анимирован Velocity.js
  • Advanced content DOM

 

«Под капотом»

Основной идеей является управляемая растеризация HTML узла и отображение результата растеризации на полноэкранной WebGL поверхности.

  • На странице создается элемент web-gl
  • Его контент растеризуется
  • Отображается на полноэкранном WebGL контексте в виде 2d спрайта, при этом сам DOM элемент скрыт
  • style.transform элемента привязаны к его WebGL представлению и при изменении трансформируют WebGL представление
  • При изменении контента или стилей компонента (DOM Mutation Observers / Events) он заново растеризуется и WebGL представление обновляется

Заключение

Как видите схема работы  позволяет смешивать WebGL и классический DOM контент в пределах одного приложения, не ограничивает разработчика в выборе фреймворка, не навязывает изучение дополнительных API. Просто добавьте <html-gl>

Слайды доклада

  • Maxim Kravchenko

    Доклад на кодефесте взорвал голову, спасибо! =)
    Если привлекать к рендерингу WebGL, то это означает что JavaScript работает 60 раз в секунду, а следовательно, ради плавности, тяжелую логику в отклик на действия пользователя уже позволить нельзя.
    Понятное дело, что если использовать эту вещь в меру, то все обойдется, а если нужно чтобы некий эффект как в демке рендерился постоянно, то наверное придется ради профита отказаться от Event Driven в пользу отработки логики приложения во время прохода rendering loop. Все-таки JS однопоточный и это в некоторой степени ограничивает в использовании таких клёвых эффектов. Не приходили такие мысли?

  • Аноним

    Тут важно понимать, что эффекты рендерятся на GPU (пиксельные и вершинные шейдеры). Это значит, что на процессор и главный поток браузера они почти не влияют, поэтому без паники! Просто попробуй написать GL SL или взять с shadertoy.com и запплаить на свой HTML GL.

    Important thing here is that effects are GPU rendered (pixel and vertex shaders). It means that it do not affect main thread too much. I would not expect unsolvable performance issues here. So no worries, just try to write or copy/paste from shadertoy.com some GL SL and apply to your HTML GL. Cheers!

  • woot

    Sorry, I don’t think this makes sense. You should rather try to implement a better backend for WebKit.

  • Аноним

    The sense is to be able to do like http://pixelscommander.com/polygon/htmlgl/demo/filters.html . WebKit is almost dead BTW

  • http://bucaran.me Jorge Bucaran

    Wow, what do you mean by WebKit is almost dead?

  • Аноним

    Of course it is not literally dead, but saying «better WebKit will save humanity» sounds a bit weird since more and more projects move to Chromium and Blink right now. WebKit need a good kick to take the lead again.

  • Kyrre Eilertsen

    Eh.. chromium is webkit..

  • bob

    not being able to select text is deal-breaker

  • Аноним

    There are workarounds to implement selection and we are going to do that. Follow repository on github, there should be an update.

  • tips4design

    Can we use this to make snapshots of websites, simillar to what HTML2CANVAS (ttps://github.com/niklasvh/html2canvas) does?

  • Аноним

    No, html2canvas is the best approach you can get right now. But if you would like to have better one you definitely should read this http://pixelscommander.com/en/javascript/state-of-html-content-rasterization-draw-html-to-canvas-image/ and spread the word about Rasterization API proposed in the end.

  • David Spector

    As I understand it, this library automatically caches the image of a web page, or part of one, even if the DOM is dynamically changed over time, and automatically displays the cached image instead of the rendered DOM, permitting simple high-speed animations of the image that would not otherwise (at least on a mobile platform) be possible (with the limitation that magnifying the image necessarily introduces blur). In my opinion, this is an elegant solution to a common problem that deserves to be widely known.

  • Аноним

    Thanks, David. This is absolutely correct.

    HTML GL could be even faster if we have native Rasterization API. The problem is described here http://pixelscommander.com/en/javascript/state-of-html-content-rasterization-draw-html-to-canvas-image/ and this is the proposal draft https://gist.github.com/PixelsCommander/a0b5882139cbb8a1781c#file-proposal-md. You may help to make browsers better and HTML GL faster by spreading word about Rasterization API. The aim is to start discussion on this topic and make it visible for w3c group.

  • Max

    I’ve tried to make a demo with your advice from how to use section and it didn’t working unfortunately.

    all it does is adds opacity: 0; and some styles to html-gl tag

  • Аноним

    Could you share code?

  • Zdzichu

    The text in the Basic HTML GL demo is unbearably blurry. I’m using a normal 1080p monitor, not Retina. So it’s rather useless for text rendering and it has nothing to do with Retina.

  • Аноним

    It is a bug for your particular case and it will be solved if have more info. What was the screen resolution and window size?

  • Аноним

    It is a bug for your particular case and it will be solved if have more info. What was the screen resolution and window size?

  • Jay

    Why don’t you use distance fields to render fonts? Wouldn’t that solve your «blurry» problem?

  • Аноним

    Are you a Retina user? Retina compatibility was fixed few days ago but not reflected in demos yet. Check out recent htmlgl from github. Not sure what do you mean by solving blurry with distance fields could you elaborate please?

  • Jay

    I may be misunderstanding how you’re rendering the html elements to a texture. I haven’t read the code, but if you’re rendering the text in htmlgl’s JavaScript using WebGL, and you are not somehow using the browser itself to render text, then distance fields can be used as a GPU font cache format that allows for perfect scaling: http://youtu.be/CGZRHJvJYIg

  • Аноним

    Ah, got it. You mean Valve`s SDF. Not using it yet, but agree it is the most advanced way to render fonts in WebGL

  • http://workingdraft.de/237/ Revision 237: Remote Work und Informationsnachschub | Working Draft

    […] HTMLGL […]

  • Аноним

    Got any links to where this is being used in production?

  • Аноним

    Seems like devicePixelRatio isn’t being handled, as on my Retina screen the rendering is fuzzy instead of crisp. Would be nice to see that working. 🙂

  • Аноним

    Here’s a screenshot, the WebGL part fuzzy.

  • Аноним

    Oh!! Hehe. I didn’t read the TODO part before posting. x} Looking forward to it. It’s not a hard fix, just simply factor the device pixel ratio. It’s like 2 lines of code.

    EDIT: Well, maybe not 2 lines, but it’s simple: https://www.khronos.org/webgl/wiki/HandlingHighDPI

  • Аноним

    No problem, actually retina issue is fixed in recent version which is on GitHub

  • Аноним

    Oh!! Hehe. I didn’t read the TODO part before posting. x} Looking forward to it. It’s not a hard fix, just simply factor the device pixel ratio. It’s like 2 lines of code.

    EDIT: Well, maybe not 2 lines, but it’s simple: https://www.khronos.org/webgl/wiki/HandlingHighDPI

  • Аноним

    Oh!! Hehe. I didn’t read the TODO part before posting. x} Looking forward to it. It’s not a hard fix, just simply factor the device pixel ratio. It’s like 2 lines of code.

    EDIT: Well, maybe not 2 lines, but it’s simple: https://www.khronos.org/webgl/wiki/HandlingHighDPI

  • Аноним

    Here’s a screenshot, the WebGL part fuzzy.

  • Аноним

    Here’s a screenshot, the WebGL part fuzzy.

  • Аноним

    Seems like devicePixelRatio isn’t being handled, as on my Retina screen the rendering is fuzzy instead of crisp. Would be nice to see that working. 🙂