Qt для WebAssembly
WebAssembly (или Wasm) — это двоичный формат байткода, предназначенный для выполнения в виртуальной машине внутри веб-браузера. Это позволяет развернуть приложение на устройстве с совместимым веб-браузером без каких-либо этапов установки. Приложение будет работать внутри защищённого контейнера в веб-браузере. Этот формат практически столь же быстр, как и родной машинный код, и сейчас поддерживается всеми основными веб-браузерами. Это делает его подходящим для приложений, которым не нужен полный доступ к возможностям устройства, но которые извлекают выгоду из быстрого и простого процесса установки.
Qt для WebAssembly — это плагин платформы, который позволяет создавать приложения Qt, которые могут быть интегрированы в ваши веб-страницы. Он не требует установки со стороны клиента и снижает использование ресурсов на стороне сервера.
Примечание: Qt для WebAssembly на данный момент находится в стадии технического предварительного просмотра.
Начало работы с Qt для WebAssembly
Установка Emscripten
emscripten — это инструментарий для компиляции в WebAssembly. Он позволяет запускать Qt в веб-приложении с почти родной скоростью без плагинов.
Для получения дополнительной информации об установке SDK Emscripten обратитесь к документации Emscripten.
После установки компилятор Emscripten должен быть в вашей переменной среды PATH. Проверьте это следующей командой:
em++ --version
Каждая младшая версия Qt ориентирована на конкретную минимальную версию Emscripten, которая не будет изменяться на протяжении всего срока службы этой младшей версии Qt. Бинарные пакеты Qt создаются с использованием этой версии SDK Emscripten. Мы рекомендуем установить минимальную версию Emscripten, соответствующую используемой версии Qt, особенно если вы используете бинарные пакеты.
Более новые версии Emscripten, превышающие минимальную версию, могут работать (и часто работают), но могут внести изменения в поведение, которые потребуют изменений в Qt.
Минимальные версии:
- Qt 6.2.0: 2.0.14
Используйте emsdk для установки конкретных emscripten версий. Например, чтобы установить её для Qt 6.2, введите:
- ./emsdk install 2.0.14
- ./emsdk activate 2.0.14
После установки в Windows emscripten должен быть в вашей переменной среды PATH. В macOS или Linux необходимо добавить его в переменную среды PATH, как показано ниже:
source /path/to/emsdk/emsdk_env.sh
Проверьте это следующей командой:
em++ --version
Загрузка бинарных файлов
Бинарные сборки можно загрузить в разделе «Загрузки» с помощью вашей учетной записи Qt.
Сборка Qt из исходных кодов
В качестве альтернативы вы можете загрузить исходные коды Qt в разделе «Загрузки» и собрать Qt из них.
Настройте Qt как кросс-компиляцию для wasm-emscripten платформы. Это неявно установит -static, -no-feature-thread, и -no-make examples параметры конфигурации. Поддержка многопоточности и сборка примеров могут быть включены путём удаления соответствующего параметра отключения. Сборки динамических библиотек на данный момент не поддерживаются.
Для Qt 6 вам понадобится хостовая сборка той же версии Qt, и добавить этот путь в QT_HOST_PATH, используя -qt-host-path аргумент конфигурации.
Хотя это должно обнаруживаться автоматически, вы можете дополнительно установить CMAKE_TOOLCHAIN_PATH на файл инструментальной цепочки Emscripten.cmake, который поставляется с SDK Emscripten, используя аргумент конфигурации -DCMAKE_TOOLCHAIN_FILE=.
./configure -qt-host-path /path/to/Qt/6.2.0/platform -xplatform wasm-emscripten -prefix $PWD/qtbase
В Windows убедитесь, что у вас есть MinGW в вашей переменной среды PATH и настройте с помощью следующего:
configure -qt-host-path C:\Path\to\Qt6 -no-warnings-are-errors -xplatform wasm-emscripten -platform win32-g++ -prefix %CD%\qtbase
Сборка необходимых модулей:
cmake --build . -t qtbase -t qtdeclarative [-t another_module]
Сборка и запуск вашего приложения
$ /path/to/qt-wasm/qtbase/bin/qt-cmake $ make
Это создаёт следующие файлы:
| Сгенерированный файл | Краткое описание |
|---|---|
| app.html | Контейнер HTML |
| qtloader.js | JS API для загрузки приложений Qt |
| app.js | JS API для загрузки приложений Qt |
| app.wasm | Двоичный файл приложения emscripten |
При развертывании приложения сжатие обычно выполняется на стороне сервера. Мы рекомендуем сжимать двоичные файлы wasm, поскольку это обычно уменьшает размер двоичного файла на 50%.
Дополнительная настройка
Qt для WebAssembly имеет несколько дополнительных аргументов конфигурации.
| Аргумент конфигурации | Краткое описание |
|---|---|
| -sse2 | Это включает автовекторизацию и поддержку Wasm SIMD путём добавления компилятора аргумента -msimd128. Кроме того, используются эмулированные и родные инструкции SSE optcode (в этот момент также будут использоваться пути кода SSE Qt). Только инструкции SSE1, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 и 128-битные AVX. Поддержка SIMD может потребовать активации в расширенных настройках браузера, таких как 'about:config' или 'chrome:flags'. Может возникнуть проблема с производительностью при использовании некоторых эмулированных инструкций SIMD, у которых нет родной поддержки optcode в браузере. Для получения дополнительной информации см. https://emscripten.org/docs/porting/simd.html |
| -feature-thread | Многопоточное wasm |
| -device-option QT_WASM_SOURCE_MAP=1 | Вариант отладки для создания source map |
| -feature-opengles3 | Использовать opengles3 в дополнение к стандартному opengles2 |
Тестирование и запуск вашего приложения
Вы можете запустить приложение следующим образом:
/path/to/emscripten/emrun --browser=firefox appname.html
Поддерживаемые браузеры и устройства
Рабочий стол
- Chrome
- Firefox
- Safari
- Edge(Chrome)
Если браузер поддерживает WebAssembly, то Qt должен работать.
Примечание: Qt имеет фиксированное требование к WebGL, также для приложений, которые не используют WebGL напрямую. Браузеры часто запрещают WebGL для устаревших/неподдерживаемых графических процессоров.
Мобильные устройства
- Браузер Chrome для Android
- Mobile Safari для iPhone/iPad
Примечание: В настоящее время нет поддержки ввода текста с виртуальной клавиатуры. Safari в настоящее время не поддерживает wasm-модули такого размера, которые производит Qt.
Qt не использует напрямую функции операционной системы, и не имеет значения, например, запущен ли FireFox на Windows или macOS. Qt использует некоторые адаптации операционной системы, например, для обработки клавиш ctrl/cmd на macOS.
Поддерживаемые модули Qt
Qt для WebAssembly поддерживает подмножество модулей и функций Qt. Список ниже содержит текущие протестированные модули.
- QtBase (см. раздел ограничений ниже)
- QtShaderTools
- QtDeclarative (см. раздел ограничений ниже)
- QtQuickControls2
- QtWebSockets
- QQtSvg
- QtMqtt
Другие модули не тестировались и могут или не могут работать.
Обратите внимание, что изменения и обновления браузеров также могут изменить поведение платформы Wasm, поэтому данная документация и перечисленные ниже ограничения могут быть не актуальны.
- Веб-браузеры регулярно обновляются, и они настраиваются и расширяются, иногда с неожиданными побочными эффектами для приложений, основанных на WebAssembly:
- Блокировщики JavaScript могут блокировать JavaScript без включения NoScript. Это означает, что содержимое <noscript> не отображается, и приложение, кажется, зависло на экране загрузки.
- Некоторые блокировщики рекламы блокируют все файлы .wasm с определённых хостов, таких как github.com
- privacy.resistFingerprinting=true (FireFox) отключает поддержку high-dpi — браузер будет отображаться как работающий на дисплее стандартного разрешения.
Ограничения QtBase и неподдерживаемые функции
QtBase для OpenGL рабочего стола, Vulkan или Metal
- Требуется WebGL, даже для приложений, которые не используют OpenGL напрямую. Все соответствующие браузеры поддерживают WebGL, но обратите внимание, что некоторые браузеры запрещают определённые более старые графические процессоры. Загрузчик Qt обнаружит это и отобразит сообщение об ошибке.
- Смешение OpenGL и растрового содержимого не поддерживается
- Вложенные окна OpenGL не поддерживаются. Композитор окна (в плагине платформы Qt для Wasm) поддерживает только растровые окна.
- QOpenGLWidget не поддерживается: QTBUG-66944
- Qt будет распознавать поддержку OpenGL как OpenGL ES. На самом деле браузер предоставляет WebGL. WebGL 1.0 основан на OpenGL ES 2, а WebGL 2.0 на OpenGL ES 3 очень похожи, но есть некоторые несовместимости. См. Различия между WebGL и OpenGL Существуют дополнительные различия между WebGL 1.0 и WebGL 2.0, которые описаны в: Спецификация WebGL 2.0
Многопоточность с QThread, QConcurrent, QFuture как часть QtBase
Многопоточность в Qt для WebAssembly пока не официально поддерживается и может работать не совсем так, как позиционные потоки. Обратитесь к Поддержке потоков Pthreads.
Qt в WebAssembly может запускать многопоточность, но поддержка по умолчанию отключена для совместимости с максимальным количеством браузеров. Поддержка многопоточности может быть включена путём сборки Qt из исходного кода и использования флага конфигурации "-feature-thread".
Бинарные релизы Qt для WebAssembly не поддерживают многопоточность.
Минимальная версия Emscripten SDK — 1.38.30. Документация Emscripten по потокам содержит соответствующую документацию по многопоточности.
Многопоточность поддерживается некоторыми (но не всеми) браузерами. Могут потребоваться изменения конфигурации. Демонстрация [1] может быть использована для определения доступности поддержки многопоточности. Обратите внимание, что именно режим сборки определяет, требуется ли поддержка многопоточности браузера, а не то, запускает ли приложение поток или нет.
В версиях Firefox до 79 откройте about:config и убедитесь, что включён следующий параметр:
- javascript.options.shared_memory = true
Поддержка многопоточности будет включена только при условии, что веб-сервер установит два дополнительных заголовка:
- Cross-Origin-Opener-Policy: same-origin
- Cross-Origin-Embedder-Policy: require-corp
(Это заголовки COOP и COEP соответственно)
Mozilla баг 1619649 отслеживает изменение значений по умолчанию в Firefox. Пока можно вручную обойти или включить проверку заголовка:
Firefox Nightly или Beta — обход проверки заголовка:
- dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled = true
Firefox Release — включение проверки заголовка:
- browser.tabs.remote.useCrossOriginEmbedderPolicy = true
- browser.tabs.remote.useCrossOriginOpenerPolicy = true
После включения проверки заголовков убедитесь, что ваш веб-сервер устанавливает необходимые заголовки. См. QTBUG-79087 для примера сервера разработки на Python.
https://bugreports.qt.io/browse/QTBUG-79087
Разработчикам приложений может потребоваться добавить две настройки в файл .pro или CMakeFiles.txt при включении потоков:
- Размер пула потоков: Приложения должны задать ожидаемое количество одновременных потоков во время сборки. Это можно сделать, установив QT_WASM_PTHREAD_POOL_SIZE в файлах .pro или CMakeFIles.txt (соответствует Emscripten PTHREAD_POOL_SIZE). Приложения могут превышать PTHREAD_POOL_SIZE, при условии, что они возвращают управление основному потоку браузера до ожидания нового потока, например, возвращаясь из обработчика событий, который запустил новый поток. Это позволяет браузеру запустить другого веб-рабочего поток. Немедленное ожидание нового потока в главном потоке (используя QThread::wait() или аналогично) приведет к тупику. Qt устанавливает PTHREAD_POOL_SIZE по умолчанию в 4.
- Размер памяти кучи: Приложения должны задать размер памяти кучи во время сборки, так как увеличение кучи не поддерживается при включенных pthreads. Это можно сделать, установив QT_WASM_INITIAL_MEMORY в файле CMakeFiles.txt (соответствует Emscripten INITIAL_MEMORY). Браузеры обычно ограничивают начальный размер выделения памяти WASM 1 ГБ. Qt устанавливает INITIAL_MEMORY по умолчанию в 1 ГБ (для сборки с включенным -feature-thread).
Используя CMake, например:
set(QT_WASM_PTHREAD_POOL_SIZE, 10)
Ограниченный доступ к сети из-за песочницы веб-приложений
Веб-песочница ограничивает доступ к сети подмножеством того, что доступно для нативных приложений.
- QNetworkAccessManager запросы HTTP к веб-странице серверу происхождения или к серверу, который поддерживает CORS.
- QWebSocket подключения к любому хосту.
- Туннелирование TCP и UDP-сокеттов через WebSocket с использованием веб-сервера websockify [3].
- Websockify v0.8.0 можно использовать для туннелирования TCP-соединений с QT5.12, но ОБЯЗАТЕЛЬНО указать базовые64 или бинарные подпротоколы перед вызовом QWebSocket::open().
Доступ к файлам и локальной файловой системе ограничен из-за веб-песочницы
Доступ к файловой системе ограничен в веб-песочнице, и это влияет на то, как приложение работает с файлами. Веб-платформа предоставляет API для доступа к локальной файловой системе под управлением пользователя, а также API для доступа к постоянному хранилищу. Emscripten и Qt оборачивают эти функции и предоставляют API, которые проще использовать из приложений на C++ и Qt.
Веб-платформа предоставляет функции для доступа к локальным файлам и постоянному хранилищу:
- <input type="file"> для отображения родного диалога выбора файла, где пользователь может выбрать файл.
- IndexedDB обеспечивает постоянное локальное хранилище (недоступно за пределами браузера).
Emscripten предоставляет несколько файловых систем с API, похожим на POSIX. К ним относятся:
- временная файловая система MEMFS, которая хранит файлы в оперативной памяти
- постоянная файловая система IDBFS, которая хранит файлы с использованием IndexedDB
Emscripten монтирует временную файловую систему MEMFS в "/" при запуске приложения. Это означает, что QFile может использоваться и будет по умолчанию читать и записывать файлы в оперативную память. Qt также предоставляет другие API:
- QSettings имеет бэкенд на основе IndexedDB; Обратите внимание, что QSettings асинхронны в WebAssembly. См. пример использования в [2]
- QFileDialog::getOpenFileContent() открывает родной диалог выбора файла, где пользователь может выбрать файл
- QFileDialog::saveFileContent() сохраняет файл в локальной файловой системе через загрузку файла
Буфер обмена только с текстовым содержимым
Qt поддерживает копирование и вставку текста в буфер обмена системы, но в вашем коде необходимо учитывать различия, специфичные для браузера.
- Предпочтите браузеры, которые поддерживают API буфера обмена. Требование для этого API заключается в том, что веб-страница предоставляется по защищенному соединению (например, https).
- Chrome поддерживает API буфера обмена
- Firefox поддерживает API буфера обмена через флаг: dom.events.asyncClipboard.dataTransfer
- Также поддерживаются браузеры, которые отправляют события буфера обмена на элемент холста Qt.
- Этот режим поддерживает только сочетания клавиш CTRL+x/c/v
- Проводится работа. Firefox работает хорошо, другие браузеры имеют некоторые сбои.
- В данный момент только текст
Другие известные ограничения QtBase
- Вложенные циклы событий не поддерживаются. Приложения не должны вызывать, например, QDialog::exec() или создавать новый объект QEventLoop.
- Перетаскивание не поддерживается
- Печать не поддерживается
- QDnsLookup запросы, QTcpSocket, QSsl не работают и не поддерживаются в Wasm из-за песочницы платформы
- Доступность: Wasm как платформа ограничивает доступ к холсту, и это нельзя обойти с помощью Qt. Qt отображает содержимое приложения на элемент холста и не использует другие родные элементы DOM. Это означает, что доступность (считыватели экрана) не поддерживается, и ввод текста не будет вызывать виртуальные клавиатуры.
- Шрифты: Песочница Wasm не позволяет получить доступ к системным шрифтам. Файлы шрифтов должны быть распространены с приложением, например, в ресурсах Qt или загружены. Сам Qt для WebAssembly встраивает один такой шрифт.
- Высокое разрешение и масштабирование: Поддерживается рендеринг высокого разрешения, а также установка общего размера отображения пользовательского интерфейса с помощью функции масштабирования браузера. Настройки размера (и типа) шрифта браузера не влияют на приложения Qt.
Известные проблемы и ограничения QtQuickControls2
Известные ограничения и проблемы с Qt Quick Controls 2:
- Возможны артефакты неинициализированной графической памяти в некоторых компонентах Qt Quick Controls 2, таких как флажки. Иногда это можно увидеть на дисплеях высокого разрешения.
- Родные стили для Windows и macOS не поддерживаются, так как Wasm как платформа не предоставляет эту возможность
Отладка и профилирование
Отладка Wasm выполняется в консоли JavaScript браузера, отладка приложений Wasm непосредственно в Qt Creator невозможна.
- Вывод отладки и регистрации Qt печатается в консоли JavaScript, к которой можно получить доступ через "Инструменты разработчика" браузера или аналогично.
- Карты исходных данных для пошагового выполнения кода могут быть созданы путем переконфигурации Qt с параметром --device-option QT_WASM_SOURCE_MAP=1 и построения сборки отладки.
- Браузеры мобильных устройств могут использовать удаленную отладку
- Чтобы остановить выполнение на определенной строке и вывести дебаггер браузера программно, можно добавить функцию emscripten_debugger(); в исходный код приложения.
- Профилирование можно выполнить с помощью сборки отладки и функций профилирования консоли JavaScript. Qt добавляет --profiling-funcs в аргументы компоновщика в сборках отладки, которые сохраняют имена функций при профилировании.
Занимаемое место и размер файла
Ожидаемое занимаемое место (размер загрузки): Модули Wasm, полученные от компилятора, могут быть большими, но хорошо сжимаются:
| Пример | gzip | brotli |
|---|---|---|
| helloglwindow (QtCore + QtGui) | 2.8M | 2.1M |
| wiggly widget (QtCore + QtGui + QtWidgets) | 4.3M | 3.2M |
| SensorTag (QtCore + QtGui + QtWidgets + QtQuick + QtCharts) | 8.6M | 6.3M |
Сжатие обычно выполняется на стороне веб-сервера, с использованием стандартных функций сжатия: сервер автоматически сжимает или использует предварительно сжатые версии файлов. Обычно нет необходимости в специальной обработке файлов wasm.
Другие известные проблемы, ограничения и общие замечания
- Поддерживается на всех системах разработки: Linux, MacOS и Windows (MinGW)
- Ошибка на этапе компоновки, например, "wasm-ld: ошибка: начальная память слишком мала", требует корректировки начального размера памяти. Используйте QT_WASM_TOTAL_MEMORY для установки начального размера в Кб, который должен быть кратен 64 Кб (65536). В CMakeFiles.txt: set(QT_WASM_TOTAL_MEMORY, xxxxx);
- Для целевой платформы WebAssembly в qmake используйте emscripten в качестве имени платформы, например: emscripten { message("Building for WebAssembly") }
- Известные ошибки и проблемы Wasm можно найти на странице отслеживания проблем Qt JIRA по этой ссылке: https://bugreports.qt.io/secure/RapidBoard.jspa?rapidView=258&quickFilter=2352
Некоторые примеры
- Демонстрация промышленной панели
- Приложение QMainWindow
- Галерея доступных элементов управления в Qt Quick Controls
- Веб-приложение для заказа пиццы
Внешние ресурсы
- Предварительный просмотр технологии Qt для WebAssembly
- Qt и WebAssembly
- Вики Qt для WebAssembly
- Начало работы с Qt для WebAssembly
- Удаленные пользовательские интерфейсы с WebGL и WebAssembly
- Сайт ресурсов WebAssembly
- Документация Emscripten
Лицензии
Qt для WebAssembly доступен по коммерческим лицензиям от The Qt Company. Кроме того, он доступен по GNU General Public License, версия 3. Подробнее см. Лицензирование Qt.
См. также Сайт ресурсов WebAssembly, Начало работы с Qt для WebAssembly и Удаленные пользовательские интерфейсы с WebGL и WebAssembly.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/wasm.html