1

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:

enter image description here

After 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();
}
D0r1an
  • 36
  • 5

1 Answers1

0

To fix the issue, just add GrDirectContext::resetContext() to the resizeGL function.

From the Skia documentation:

The context normally assumes that no outsider is setting state within the underlying 3D API's context/device/whatever.

This call informs the context that the state was modified and it should resend. Shouldn't be called frequently for good performance. The flag bits, state, is dependent on which backend is used by the context, either GL or D3D (possible in future).

resetGLTextureBindings() also seems to work and might have a lower overhead.

So the issue is was that Qt modifies the OpenGL state but Skia assumes that the state is not modified.

D0r1an
  • 36
  • 5