I created a basic application with a single Skia canvas using QT. When using QOpenGLWindow
as base class it works fine. But using QOpenGLWidget
, the text disappears when resizing the window. The main difference is that QOpenGLWidget
recreates its framebuffer after each resize.
Before Resizing:
After Resizing:
The skia version used is chrome/m90 (git tag) on Arch Linux (tested with wayland and X11). The same issue occurs for ealier versions. For chrome/m80, the text does not disappear but letters become black blocks. So I suspect a memory corruption issue, but what is the fix?
My code is below. If necessary, I can also provide the build files.
#include <QSurfaceFormat>
#include <QOpenGLContext>
#include <QApplication>
#include <QOpenGLWindow>
#include <QOpenGLWidget>
#include <QMainWindow>
#include <gpu/gl/GrGLAssembleInterface.h>
#include <core/SkSurfaceProps.h>
#include <gpu/GrDirectContext.h>
#include <core/SkSurface.h>
#include <QOpenGLFunctions>
#include <core/SkCanvas.h>
#include <core/SkFont.h>
bool AllowEGL;
void initGL() {
QSurfaceFormat fmt;
fmt.setDepthBufferSize(0);
fmt.setRedBufferSize(8);
fmt.setGreenBufferSize(8);
fmt.setBlueBufferSize(8);
fmt.setStencilBufferSize(8);
fmt.setSamples(0);
fmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CoreProfile);
} else {
fmt.setVersion(3, 0);
}
QSurfaceFormat::setDefaultFormat(fmt);
AllowEGL = QApplication::platformName() != "xcb";
}
sk_sp<GrDirectContext> makeContext(QOpenGLContext *ctx) {
auto interface = GrGLMakeAssembledInterface(ctx, [](auto ctx, auto name) {
return AllowEGL || strncmp(name, "egl", 3) ? static_cast<QOpenGLContext *>(ctx)->getProcAddress(name) : nullptr;
});
return GrDirectContext::MakeGL(interface);
}
sk_sp<SkSurface> createSurface(GrRecordingContext *ctx, int w, int h, GrGLuint fbo) {
GrGLFramebufferInfo info;
info.fFBOID = fbo;
info.fFormat = GL_RGBA8;
GrBackendRenderTarget target(w, h, 0, 8, info);
const SkSurfaceProps props(0, SkPixelGeometry::kBGR_H_SkPixelGeometry);
return SkSurface::MakeFromBackendRenderTarget(ctx, target, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
nullptr, &props, [](auto r) { qDebug("release"); });
}
void draw(SkCanvas *canvas) {
canvas->clear(SK_ColorWHITE);
SkFont font;
SkPaint paint;
canvas->drawString("Hello World!", 10, 30, font, paint);
paint.setAntiAlias(true);
canvas->drawCircle(100, 50, 10, paint);
canvas->flush();
}
class SkiaWindow : public QOpenGLWindow {
protected:
void initializeGL() override {
ctx = makeContext(context());
f = context()->functions();
}
void resizeGL(int w, int h) override {
surface = createSurface(ctx.get(), w, h, defaultFramebufferObject());
f->glViewport(0, 0, w, h);
}
void paintGL() override {
f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw(surface->getCanvas());
}
private:
sk_sp<GrDirectContext> ctx{};
sk_sp<SkSurface> surface{};
QOpenGLFunctions *f{};
};
class SkiaWidget : public QOpenGLWidget {
using QOpenGLWidget::QOpenGLWidget;
protected:
void initializeGL() override {
ctx = makeContext(context());
f = context()->functions();
}
void resizeGL(int w, int h) override {
f->glViewport(0, 0, w, h);
surface = createSurface(ctx.get(), w, h, defaultFramebufferObject());
}
void paintGL() override {
draw(surface->getCanvas());
}
private:
sk_sp<GrDirectContext> ctx{};
sk_sp<SkSurface> surface{};
QOpenGLFunctions *f{};
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
app.setApplicationName("qtskia");
initGL();
// Works fine
//SkiaWindow window;
// Text disappears after resizing window
QMainWindow window;
SkiaWidget widget (&window);
window.setCentralWidget(&widget);
window.show();
return QApplication::exec();
}