LOpenGLPixelScale в Qt-бэкенде (QLCLGLWidget_devicePixelRatioF), на прочих бэкендах возвращает 1.0. Нужен коду с явными glViewport и текстурами в физических пикселях. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LazOpenGLContextEx
Форк штатного пакета LazOpenGLContext (Lazarus components/opengl), в котором
Qt5/Qt6-бэкенд построен на настоящем QOpenGLWidget вместо GLX.
Зачем
Штатный TOpenGLControl под --ws=qt5/qt6 работает только в X11-сессии и
вообще не собирается с Qt-виджетсетом на Windows/macOS: он руками создаёт
GLX-контекст на winId(). Важно: QLCLOpenGLWidget из libQt6Pas — это не
QOpenGLWidget, а голый QWidget с WA_NativeWindow и paintEngine()=nullptr,
т.е. просто мишень для внешнего GLX-рендера (вызов на нём методов
QOpenGLWidget = access violation — проверено).
Здесь контекст создаёт сам Qt через платформенный API:
| Платформа | Контекст |
|---|---|
| Linux Wayland | EGL |
| Linux X11 | GLX |
| Windows (ws=qt6) | WGL |
| macOS (ws=qt6) | CGL |
Бэкенды gtk2/gtk3/win32/cocoa скопированы из штатного пакета без изменений
(юниты переименованы с суффиксом Ex, чтобы не конфликтовать со стоковым пакетом).
Состав
openglcontextex.pas—TOpenGLControl(класс называется так же, юнитOpenGLContextEx).glqtnativecontext.pas— Qt-бэкенд поверх QOpenGLWidget.csrc/qlclglwidget.cpp— вспомогательная C++-библиотекаlibqlclglwidget.so: наследник QOpenGLWidget с paintGL-хуком в Pascal + плоские экспорты (makeCurrent,doneCurrent,isValid,setFormat,defaultFramebufferObject,devicePixelRatioF). Грузится в рантайме: сначала рядом с исполняемым файлом, затем по системным путям.
Сборка вспомогательной библиотеки (csrc/)
| Где собираем | Команда | Результат |
|---|---|---|
| Linux | make -C csrc |
libqlclglwidget.so |
| macOS | make -C csrc (авто) или make -C csrc macos |
libqlclglwidget.dylib |
| Linux → Windows (кросс) | make -C csrc windows |
qlclglwidget.dll (MinGW) |
| Windows, MSYS2 MinGW64 shell | make -C csrc (авто) или make -C csrc windows-native |
qlclglwidget.dll (MinGW) |
| Windows, Qt MinGW-kit без pkg-config | make -C csrc windows MINGW_CXX=g++ WIN_QT_INC=C:/Qt/6.x/mingw_64/include WIN_QT_LIB=C:/Qt/6.x/mingw_64/lib |
qlclglwidget.dll (MinGW) |
| Windows, MSVC | csrc\build-msvc.bat из «x64 Native Tools Command Prompt» с выставленным QTDIR |
qlclglwidget.dll (MSVC) |
Плюс:
make -C csrc install # установить в /usr/local/lib (PREFIX/DESTDIR поддерживаются)
make -C csrc install-app APPDIR=/путь/к/бинарнику # положить рядом с приложением
Для кросс-сборки нужен тулчейн mingw-w64 и MinGW-сборка Qt6 (AUR
mingw-w64-qt6-base, MXE, либо Qt MinGW-kit; пути переопределяются
MINGW_PREFIX/WIN_QT_INC/WIN_QT_LIB). Для MSYS2:
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-qt6-base pkgconf make.
⚠ ABI: MinGW-DLL работает только с MinGW-сборкой Qt6 на целевой машине, MSVC-DLL — только с MSVC-Qt (официальные бинарники Qt из online-инсталлятора — MSVC). Выбирайте вариант под ту Qt6, с которой собрана libQt6Pas приложения.
Использование
В проекте: зависимость LazOpenGLContext → LazOpenGLContextEx,
в uses: OpenGLContext → OpenGLContextEx.
make -C csrc
lazbuild --ws=qt6 lazopenglcontextex.lpk
make -C csrc install-app APPDIR=<каталог с бинарником приложения>
Как устроен Qt-бэкенд
- Qt сам делает контекст текущим и биндит FBO виджета перед вызовом
paintGL(); оттуда доставляетсяLM_PAINT, так что весь рендер вOnPaintработает как раньше. SwapBuffersвнутри paint — no-op (композитингом FBO занимается Qt после возврата изpaintGL); вне paint — планирует перерисовку (QWidget::update).MakeCurrent/ReleaseContextвне paint →QOpenGLWidget::makeCurrent/doneCurrent. До первого показа виджета контекста ещё нет —MakeCurrentвернёт False.LOpenGLViewport(AutoResizeViewport) умножает координаты на devicePixelRatio.
Ограничения / нюансы
- Не вызывать
glBindFramebuffer(..., 0)— QOpenGLWidget рендерит в свой FBO. - Альфа фреймбуфера = прозрачность виджета. Qt композитит FBO с учётом
альфы; легаси-GL-код после блендинга оставляет alpha<1, и виджет просвечивал
бы насквозь. Поэтому обёртка после каждого paintGL принудительно заливает
альфу единицей (
QLCLGLWidget_setForceOpaque, включено по умолчанию). Отключается автоматически, если контрол запросилAlphaBits > 0— тогда альфа остаётся под контролем приложения. - SharedControl игнорируется: Qt автоматически шарит контексты всех
QOpenGLWidget внутри одного top-level окна. Для шаринга между окнами нужно
выставить
Qt::AA_ShareOpenGLContextsдо создания QApplication. - Windows + qt6: чтобы Qt не выбрал ANGLE/GLES (где нет immediate mode),
выставить
QT_OPENGL=desktopили атрибутAA_UseDesktopOpenGL. - HiDPI (devicePixelRatio > 1): FBO больше логического размера виджета.
Явные вызовы
glViewportв коде приложения должны умножать на dpr сами. - AUXBuffers не поддерживаются QSurfaceFormat (игнорируются).
- Qt4 (LCLQT), gtk1 и carbon из форка выброшены.
Статус проверки
- Linux Wayland (KDE, qt6): спектр EWSDR рендерится через GL, стартует без ошибок, скриншот подтверждён (2026-07-03).
- Linux XWayland/xcb: стартует без ошибок.
- Windows/macOS: не проверялось (build.sh под них ещё нет — нужен аналог с MSVC/clang и Qt-заголовками).