mirror of
https://git.vladimir.cc/vladimir/lazopenglcontextex.git
synced 2026-08-25 17:37:31 +00:00
Форк штатного пакета LazOpenGLContext (Lazarus components/opengl). Qt5/Qt6-бэкенд (glqtnativecontext.pas) использует собственный контекст настоящего QOpenGLWidget вместо ручного GLX на winId(): EGL на Wayland, GLX на X11, WGL/CGL на Windows/macOS под ws=qt6. QOpenGLWidget-наследник с paintGL-хуком живёт в маленькой C++-либе csrc/qlclglwidget.cpp (QLCLOpenGLWidget из libQt6Pas — не QOpenGLWidget, а голый QWidget под внешний GLX). Сборка: csrc/Makefile (linux/macos/windows-кросс mingw-w64/windows-native MSYS2, install, install-app) + build-msvc.bat для MSVC-Qt. Либа грузится в рантайме рядом с бинарником приложения. Бэкенды gtk2/gtk3/win32/cocoa скопированы из стока без изменений (юниты переименованы с суффиксом Ex). Детали и нюансы — в README.md. Проверено: EWSDR, спектр/водопад через GL в нативном Wayland (KDE, qt6), живой эфир QO-100. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
111 lines
7.1 KiB
Markdown
111 lines
7.1 KiB
Markdown
# 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-заголовками).
|