From 828accc91d60af0fe693425e93f4f693232663ff Mon Sep 17 00:00:00 2001 From: ts-korhonen Date: Sat, 5 Mar 2022 02:57:57 +0200 Subject: [PATCH] qt: More error handling and tweaking to opengl 3 renderer --- src/qt/qt_opengloptions.cpp | 2 +- src/qt/qt_openglrenderer.cpp | 149 ++++++++++++++++++++--------------- src/qt/qt_openglrenderer.hpp | 13 ++- src/qt/qt_rendererstack.cpp | 3 +- 4 files changed, 98 insertions(+), 69 deletions(-) diff --git a/src/qt/qt_opengloptions.cpp b/src/qt/qt_opengloptions.cpp index 73eaad98e..513f05f02 100644 --- a/src/qt/qt_opengloptions.cpp +++ b/src/qt/qt_opengloptions.cpp @@ -172,7 +172,7 @@ OpenGLOptions::addShader(const QString &path) if (!shader->addShaderFromSourceCode(QOpenGLShader::Vertex, version_line % "\n#extension GL_ARB_shading_language_420pack : enable\n" % "\n#define VERTEX\n" % shader_text)) throw_shader_error(tr("Error compiling vertex shader in file \"%1\"")); - if (!shader->addShaderFromSourceCode(QOpenGLShader::Fragment, version_line % "\n#extension GL_ARB_shading_language_420pack : enable\n" "\n#define FRAGMENT\n" % shader_text)) + if (!shader->addShaderFromSourceCode(QOpenGLShader::Fragment, version_line % "\n#extension GL_ARB_shading_language_420pack : enable\n" % "\n#define FRAGMENT\n" % shader_text)) throw_shader_error(tr("Error compiling fragment shader in file \"%1\"")); if (!shader->link()) diff --git a/src/qt/qt_openglrenderer.cpp b/src/qt/qt_openglrenderer.cpp index 9240b2604..2557b3ba1 100644 --- a/src/qt/qt_openglrenderer.cpp +++ b/src/qt/qt_openglrenderer.cpp @@ -18,8 +18,8 @@ #include #include #include +#include #include -#include #include @@ -44,18 +44,13 @@ OpenGLRenderer::OpenGLRenderer(QWidget *parent) format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); format.setMajorVersion(3); - format.setMinorVersion(0); + format.setMinorVersion(2); + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) format.setRenderableType(QSurfaceFormat::OpenGLES); setFormat(format); - context = new QOpenGLContext(this); - - context->setFormat(format); - - context->create(); - parentWidget = parent; source.setRect(0, 0, INIT_WIDTH, INIT_HEIGHT); @@ -108,64 +103,83 @@ OpenGLRenderer::event(QEvent *event) void OpenGLRenderer::initialize() { - if (!context->makeCurrent(this)) { - /* TODO: This could be done much better */ - QMessageBox::critical((QWidget *) qApp->findChild(), tr("Error initializing OpenGL"), tr("Error setting OpenGL context. Falling back to software rendering.")); + try { + context = new QOpenGLContext(this); + + context->setFormat(format()); + + if (!context->create()) + throw opengl_init_error(tr("Couldn't create OpenGL context.")); + + if (!context->makeCurrent(this)) + throw opengl_init_error(tr("Couldn't switch to OpenGL context.")); + + initializeOpenGLFunctions(); + + initializeExtensions(); + + initializeBuffers(); + + /* Vertex, texture 2d coordinates and color (white) making a quad as triangle strip */ + const GLfloat surface[] = { + -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, + 1.f, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f, + -1.f, -1.f, 0.f, 1.f, 1.f, 1.f, 1.f, 1.f, + 1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f + }; + + glGenVertexArrays(1, &vertexArrayID); + + glBindVertexArray(vertexArrayID); + + glGenBuffers(1, &vertexBufferID); + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); + glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW); + + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + + const GLfloat border_color[] = { 0.f, 0.f, 0.f, 1.f }; + + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glTexImage2D(GL_TEXTURE_2D, 0, QOpenGLTexture::RGBA8_UNorm, INIT_WIDTH, INIT_HEIGHT, 0, QOpenGLTexture::BGRA, QOpenGLTexture::UInt32_RGBA8_Rev, NULL); + + options = new OpenGLOptions(this, true); + + applyOptions(); + + glClearColor(0.f, 0.f, 0.f, 1.f); + + glViewport( + destination.x(), + destination.y(), + destination.width() * devicePixelRatio(), + destination.height() * devicePixelRatio()); + + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + throw opengl_init_error(tr("OpenGL initialization failed. Error %1.").arg(error)); + + isInitialized = true; + + emit initialized(); + + } catch (const opengl_init_error &e) { + /* Mark all buffers as in use */ + for (auto &flag : buf_usage) + flag.test_and_set(); + + QMessageBox::critical((QWidget *) qApp->findChild(), tr("Error initializing OpenGL"), e.what() % tr("\nFalling back to software rendering.")); + context->doneCurrent(); isFinalized = true; isInitialized = true; + emit errorInitializing(); - return; } - - initializeOpenGLFunctions(); - - setupExtensions(); - - setupBuffers(); - - /* Vertex, texture 2d coordinates and color (white) making a quad as triangle strip */ - const GLfloat surface[] = { - -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, - 1.f, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f, - -1.f, -1.f, 0.f, 1.f, 1.f, 1.f, 1.f, 1.f, - 1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f - }; - - glGenVertexArrays(1, &vertexArrayID); - - glBindVertexArray(vertexArrayID); - - glGenBuffers(1, &vertexBufferID); - glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID); - glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW); - - glGenTextures(1, &textureID); - glBindTexture(GL_TEXTURE_2D, textureID); - - const GLfloat border_color[] = { 0.f, 0.f, 0.f, 1.f }; - - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - - glTexImage2D(GL_TEXTURE_2D, 0, QOpenGLTexture::RGBA8_UNorm, INIT_WIDTH, INIT_HEIGHT, 0, QOpenGLTexture::BGRA, QOpenGLTexture::UInt32_RGBA8_Rev, NULL); - - options = new OpenGLOptions(this, true); - - applyOptions(); - - glClearColor(0.f, 0.f, 0.f, 1.f); - - glViewport( - destination.x(), - destination.y(), - destination.width(), - destination.height()); - - isInitialized = true; - - emit initialized(); } void @@ -174,6 +188,8 @@ OpenGLRenderer::finalize() if (isFinalized) return; + renderTimer->stop(); + context->makeCurrent(this); if (hasBufferStorage) @@ -203,7 +219,7 @@ OpenGLRenderer::getOptions(QWidget *parent) } void -OpenGLRenderer::setupExtensions() +OpenGLRenderer::initializeExtensions() { #ifndef NO_BUFFER_STORAGE if (context->hasExtension("GL_ARB_buffer_storage")) { @@ -215,7 +231,7 @@ OpenGLRenderer::setupExtensions() } void -OpenGLRenderer::setupBuffers() +OpenGLRenderer::initializeBuffers() { glGenBuffers(1, &unpackBufferID); @@ -232,6 +248,9 @@ OpenGLRenderer::setupBuffers() /* Fallback; create our own buffer. */ unpackBuffer = malloc(BUFFERBYTES * BUFFERCOUNT); + if (unpackBuffer == nullptr) + throw opengl_init_error(tr("Allocating memory for unpack buffer failed.")); + glBufferData(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * BUFFERCOUNT, NULL, GL_STREAM_DRAW); } } @@ -375,7 +394,7 @@ OpenGLRenderer::onBlit(int buf_idx, int x, int y, int w, int h) /* Resize the texture */ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)QOpenGLTexture::RGBA8_UNorm, source.width(), source.height(), 0, (GLenum)QOpenGLTexture::BGRA, (GLenum)QOpenGLTexture::UInt32_RGBA8_Rev, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, (GLenum) QOpenGLTexture::RGBA8_UNorm, source.width(), source.height(), 0, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, NULL); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBufferID); } @@ -384,7 +403,7 @@ OpenGLRenderer::onBlit(int buf_idx, int x, int y, int w, int h) glPixelStorei(GL_UNPACK_SKIP_PIXELS, BUFFERPIXELS * buf_idx + y * ROW_LENGTH + x); glPixelStorei(GL_UNPACK_ROW_LENGTH, ROW_LENGTH); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, (GLenum)QOpenGLTexture::BGRA, (GLenum)QOpenGLTexture::UInt32_RGBA8_Rev, NULL); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, NULL); /* TODO: check if fence sync is implementable here and still has any benefit. */ glFinish(); diff --git a/src/qt/qt_openglrenderer.hpp b/src/qt/qt_openglrenderer.hpp index 47bc82c22..db154d950 100644 --- a/src/qt/qt_openglrenderer.hpp +++ b/src/qt/qt_openglrenderer.hpp @@ -32,6 +32,7 @@ #endif #include +#include #include #include @@ -90,8 +91,8 @@ private: void *unpackBuffer = nullptr; void initialize(); - void setupExtensions(); - void setupBuffers(); + void initializeExtensions(); + void initializeBuffers(); void applyOptions(); void applyShader(const OpenGLShaderPass &shader); bool notReady() const { return !isInitialized || isFinalized; } @@ -107,4 +108,12 @@ private slots: void updateOptions(OpenGLOptions *newOptions); }; +class opengl_init_error : public std::runtime_error { +public: + opengl_init_error(const QString &what) + : std::runtime_error(what.toStdString()) + { + } +}; + #endif diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index be783864d..4fd1cf972 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -272,6 +272,7 @@ RendererStack::createRenderer(Renderer renderer) }); connect(hw, &OpenGLRenderer::errorInitializing, [=]() { /* Renderer could initialize, fallback to software. */ + imagebufs = {}; endblit(); QTimer::singleShot(0, this, [this]() { switchRenderer(Renderer::Software); }); }); @@ -299,7 +300,7 @@ RendererStack::createRenderer(Renderer renderer) void RendererStack::blit(int x, int y, int w, int h) { - if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || std::get(imagebufs[currentBuf])->test_and_set()) { + if ((w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || imagebufs.empty() || std::get(imagebufs[currentBuf])->test_and_set()) { video_blit_complete(); return; }