From 5e66892e640729293b995a76f6e260a8b0076d6f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 3 Sep 2021 13:30:22 -0700 Subject: [PATCH] rewrite shader compilation code Fixes #1737 --- app/render/opengl/openglrenderer.cpp | 145 ++++++++++++++++++--------- app/render/opengl/openglrenderer.h | 2 + app/render/shadercode.h | 7 -- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 6d9e8d640..b12f0f028 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -280,44 +280,38 @@ QVariant OpenGLRenderer::CreateNativeShader(ShaderCode code) PRINT_GL_ERRORS; - QOpenGLShaderProgram* program = new QOpenGLShaderProgram(context_); + GLuint vert = CompileShader(GL_VERTEX_SHADER, code.vert_code()); + GLuint frag = CompileShader(GL_FRAGMENT_SHADER, code.frag_code()); - QString vert_code = code.vert_code(); - QString frag_code = code.frag_code(); + GLuint program = 0; - QString shader_preamble = QStringLiteral("#version 110\n" - "\n"); + if (frag && vert) { + program = functions_->glCreateProgram(); + functions_->glAttachShader(program, frag); + functions_->glAttachShader(program, vert); + functions_->glLinkProgram(program); - vert_code.prepend(shader_preamble); - frag_code.prepend(shader_preamble); - - if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_code)) { - qCritical() << "Failed to add vertex code to shader"; - goto error; + GLint success; + functions_->glGetProgramiv(program, GL_LINK_STATUS, &success); + if (!success) { + qWarning() << "Failed to link OpenGL shader program"; + functions_->glDeleteProgram(program); + program = 0; + } } - if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_code)) { - qCritical() << "Failed to add fragment code to shader"; - goto error; - } + functions_->glDeleteShader(frag); + functions_->glDeleteShader(vert); - if (!program->link()) { - qCritical() << "Failed to link shader"; - goto error; - } - - return Node::PtrToValue(program); - -error: - delete program; - return QVariant(); + return program; } void OpenGLRenderer::DestroyNativeShader(QVariant shader) { GL_PREAMBLE; - delete Node::ValueToPtr(shader); + GLuint program = shader.value(); + functions_->glDeleteProgram(program); } void OpenGLRenderer::UploadToTexture(Texture *texture, const void *data, int linesize) @@ -431,14 +425,14 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video GLuint iterative_input = 0; QVector textures_to_bind; - QOpenGLShaderProgram* shader = Node::ValueToPtr(s); + GLuint shader = s.value(); - shader->bind(); + functions_->glUseProgram(shader); NodeValueMap::const_iterator it; for (it=job.GetValues().constBegin(); it!=job.GetValues().constEnd(); it++) { // See if the shader has takes this parameter as an input - int variable_location = shader->uniformLocation(it.key()); + GLint variable_location = functions_->glGetUniformLocation(shader, it.key().toUtf8().constData()); if (variable_location == -1) { continue; @@ -455,43 +449,51 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video case NodeValue::kInt: // kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to // over/underflows if the number is large enough, but the likelihood of that is quite low. - shader->setUniformValue(variable_location, value.data().toInt()); + functions_->glUniform1i(variable_location, value.data().toInt()); break; case NodeValue::kFloat: // kFloat technically specifies a double but as above, OpenGL doesn't support those. - shader->setUniformValue(variable_location, value.data().toFloat()); + functions_->glUniform1f(variable_location, value.data().toFloat()); break; case NodeValue::kVec2: - shader->setUniformValue(variable_location, value.data().value()); + { + QVector2D v = value.data().value(); + functions_->glUniform2fv(variable_location, 1, reinterpret_cast(&v)); break; + } case NodeValue::kVec3: - shader->setUniformValue(variable_location, value.data().value()); + { + QVector3D v = value.data().value(); + functions_->glUniform3fv(variable_location, 1, reinterpret_cast(&v)); break; + } case NodeValue::kVec4: - shader->setUniformValue(variable_location, value.data().value()); + { + QVector4D v = value.data().value(); + functions_->glUniform4fv(variable_location, 1, reinterpret_cast(&v)); break; + } case NodeValue::kMatrix: - shader->setUniformValue(variable_location, value.data().value()); + functions_->glUniformMatrix4fv(variable_location, 1, false, value.data().value().constData()); break; case NodeValue::kCombo: - shader->setUniformValue(variable_location, value.data().value()); + functions_->glUniform1i(variable_location, value.data().value()); break; case NodeValue::kColor: { Color color = value.data().value(); - shader->setUniformValue(variable_location, - color.red(), color.green(), color.blue(), color.alpha()); + functions_->glUniform4f(variable_location, color.red(), color.green(), color.blue(), color.alpha()); break; } case NodeValue::kBoolean: - shader->setUniformValue(variable_location, value.data().toBool()); + functions_->glUniform1i(variable_location, value.data().toBool()); break; case NodeValue::kTexture: { TexturePtr texture = value.data().value(); // Set value to bound texture - shader->setUniformValue(variable_location, textures_to_bind.size()); + functions_->glUniform1i(variable_location, textures_to_bind.size()); // If this texture binding is the iterative input, set it here if (it.key() == job.GetIterativeInput()) { @@ -503,10 +505,9 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video // Set enable flag if shader wants it GLuint tex_id = texture ? texture->id().value() : 0; - int enable_param_location = shader->uniformLocation(QStringLiteral("%1_enabled").arg(it.key())); + int enable_param_location = functions_->glGetUniformLocation(shader, QStringLiteral("%1_enabled").arg(it.key()).toUtf8().constData()); if (enable_param_location > -1) { - shader->setUniformValue(enable_param_location, - tex_id > 0); + functions_->glUniform1i(enable_param_location, tex_id > 0); } break; } @@ -542,8 +543,10 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } // Ensure matrix is set, at least to identity - shader->setUniformValue("ove_mvpmat", - job.GetValue(QStringLiteral("ove_mvpmat")).data().value()); + GLint mvpmat_location = functions_->glGetUniformLocation(shader, "ove_mvpmat"); + if (mvpmat_location > -1) { + functions_->glUniformMatrix4fv(mvpmat_location, 1, false, job.GetValue(QStringLiteral("ove_mvpmat")).data().value().constData()); + } // Set the viewport to the "physical" resolution of the destination functions_->glViewport(0, 0, @@ -568,7 +571,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video frag_vbo_.allocate(blit_texcoords.constData(), blit_texcoords.size() * sizeof(GLfloat)); frag_vbo_.release(); - int vertex_location = shader->attributeLocation("a_position"); + GLint vertex_location = functions_->glGetAttribLocation(shader, "a_position"); if (vertex_location != -1) { vert_vbo_.bind(); functions_->glEnableVertexAttribArray(vertex_location); @@ -576,7 +579,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video vert_vbo_.release(); } - int tex_location = shader->attributeLocation("a_texcoord"); + GLint tex_location = functions_->glGetAttribLocation(shader, "a_texcoord"); if (tex_location != -1) { frag_vbo_.bind(); functions_->glEnableVertexAttribArray(tex_location); @@ -608,9 +611,12 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } } + GLint iteration_location = functions_->glGetUniformLocation(shader, "ove_iteration"); for (int iteration=0; iterationsetUniformValue("ove_iteration", iteration); + if (iteration_location > -1) { + functions_->glUniform1i(iteration_location, iteration); + } // Replace iterative input if (iteration == real_iteration_count-1) { @@ -666,7 +672,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } // Release shader - shader->release(); + functions_->glUseProgram(0); // Release vertex array object frag_vbo_.destroy(); @@ -861,6 +867,47 @@ GLuint OpenGLRenderer::GetCachedTexture(int width, int height, int depth, VideoP return 0; } +GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code) +{ + static const QString shader_preamble = QStringLiteral("#version 110\n\n" + "precision highp float;\n\n"); + + QString complete_code = shader_preamble; + + if (code.isEmpty()) { + // Use default code + if (type == GL_FRAGMENT_SHADER) { + complete_code.append(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.frag"))); + } else if (type == GL_VERTEX_SHADER) { + complete_code.append(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.vert"))); + } + } else { + complete_code.append(code); + } + + QByteArray code_utf8 = complete_code.toUtf8(); + const char *code_cstr = code_utf8.constData(); + + GLuint shader = functions_->glCreateShader(type); + functions_->glShaderSource(shader, 1, &code_cstr, nullptr); + functions_->glCompileShader(shader); + + GLint success; + functions_->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + qWarning() << "Failed to compile OpenGL shader"; + + QByteArray error_log(10240, Qt::Uninitialized); + functions_->glGetShaderInfoLog(shader, error_log.size(), nullptr, error_log.data()); + std::cout << error_log.constData() << std::endl << code_cstr << std::endl; + + functions_->glDeleteShader(shader); + shader = 0; + } + + return shader; +} + void OpenGLRenderer::GarbageCollectTextureCache() { qint64 max_age = QDateTime::currentMSecsSinceEpoch() - kTextureCacheMaxSize; diff --git a/app/render/opengl/openglrenderer.h b/app/render/opengl/openglrenderer.h index 6b1906cac..decc1d20b 100644 --- a/app/render/opengl/openglrenderer.h +++ b/app/render/opengl/openglrenderer.h @@ -98,6 +98,8 @@ private: GLuint GetCachedTexture(int width, int height, int depth, VideoParams::Format format, int channel_count); + GLuint CompileShader(GLenum type, const QString &code); + QTimer cache_timer_; QOpenGLContext* context_; diff --git a/app/render/shadercode.h b/app/render/shadercode.h index 5b8c6a313..c41422122 100644 --- a/app/render/shadercode.h +++ b/app/render/shadercode.h @@ -31,13 +31,6 @@ public: frag_code_(frag_code), vert_code_(vert_code) { - if (frag_code_.isEmpty()) { - frag_code_ = FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.frag")); - } - - if (vert_code_.isEmpty()) { - vert_code_ = FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.vert")); - } } const QString& frag_code() const