/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006-2007 Rivo Laks Copyright (C) 2010, 2011 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kwinglutils.h" // need to call GLTexturePrivate::initStatic() #include "kwingltexture_p.h" #include "kwinglcolorcorrection.h" #include "kwineffects.h" #include "kwinglplatform.h" #include "logging_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_EPOXY_GLX #include #endif #define DEBUG_GLRENDERTARGET 0 #define MAKE_GL_VERSION(major, minor, release) ( ((major) << 16) | ((minor) << 8) | (release) ) #ifdef __GNUC__ # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) #else # define likely(x) (x) # define unlikely(x) (x) #endif namespace KWin { // Variables // GL version, use MAKE_GL_VERSION() macro for comparing with a specific version static int glVersion; // GLX version, use MAKE_GL_VERSION() macro for comparing with a specific version static int glXVersion; // EGL version, use MAKE_GL_VERSION() macro for comparing with a specific version static int eglVersion; // List of all supported GL, EGL and GLX extensions static QList glExtensions; static QList s_glxExtensions; static QList s_eglExtensions; int glTextureUnitsCount; // Functions void initGLX() { #if HAVE_EPOXY_GLX // Get GLX version int major, minor; glXQueryVersion(display(), &major, &minor); glXVersion = MAKE_GL_VERSION(major, minor, 0); // Get list of supported GLX extensions const QByteArray string = (const char *) glXQueryExtensionsString(display(), QX11Info::appScreen()); s_glxExtensions = string.split(' '); glxResolveFunctions(); #endif } void initEGL() { EGLDisplay dpy = eglGetCurrentDisplay(); if (dpy == EGL_NO_DISPLAY) dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); int major, minor; eglInitialize(dpy, &major, &minor); eglVersion = MAKE_GL_VERSION(major, minor, 0); const QByteArray string = eglQueryString(dpy, EGL_EXTENSIONS); s_eglExtensions = string.split(' '); eglResolveFunctions(); } void initGL(OpenGLPlatformInterface platformInterface) { // Get OpenGL version const char* glversioncstring = (const char*)glGetString(GL_VERSION); QByteArray glversionstring = QByteArray::fromRawData(glversioncstring, qstrlen(glversioncstring)); if (glversionstring.startsWith("OpenGL ES ")) { glversionstring = glversionstring.mid(10); } const int whiteSpaceIndex = glversionstring.indexOf(' '); if (whiteSpaceIndex != -1) { glversionstring.truncate(whiteSpaceIndex); } auto glversioninfo = glversionstring.split('.'); while (glversioninfo.count() < 3) glversioninfo << "0"; glVersion = MAKE_GL_VERSION(glversioninfo[0].toInt(), glversioninfo[1].toInt(), glversioninfo[2].toInt()); // Get list of supported OpenGL extensions if (hasGLVersion(3, 0)) { int count; glGetIntegerv(GL_NUM_EXTENSIONS, &count); for (int i = 0; i < count; i++) { const QByteArray name = (const char *) glGetStringi(GL_EXTENSIONS, i); glExtensions << name; } } else glExtensions = QByteArray((const char*)glGetString(GL_EXTENSIONS)).split(' '); // handle OpenGL extensions functions glResolveFunctions(platformInterface); GLTexturePrivate::initStatic(); GLRenderTarget::initStatic(); GLVertexBuffer::initStatic(); } void cleanupGL() { ShaderManager::cleanup(); GLTexturePrivate::cleanup(); GLRenderTarget::cleanup(); GLVertexBuffer::cleanup(); GLPlatform::cleanup(); glExtensions.clear(); s_glxExtensions.clear(); s_eglExtensions.clear(); glVersion = 0; glXVersion = 0; eglVersion = 0; glTextureUnitsCount = 0; } bool hasGLVersion(int major, int minor, int release) { return glVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasGLXVersion(int major, int minor, int release) { return glXVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasEGLVersion(int major, int minor, int release) { return eglVersion >= MAKE_GL_VERSION(major, minor, release); } bool hasGLExtension(const QByteArray &extension) { return glExtensions.contains(extension) || s_glxExtensions.contains(extension) || s_eglExtensions.contains(extension); } QList eglExtensions() { return s_eglExtensions; } QList glxExtensions() { return s_glxExtensions; } QList openGLExtensions() { return glExtensions; } static QString formatGLError(GLenum err) { switch(err) { case GL_NO_ERROR: return QStringLiteral("GL_NO_ERROR"); case GL_INVALID_ENUM: return QStringLiteral("GL_INVALID_ENUM"); case GL_INVALID_VALUE: return QStringLiteral("GL_INVALID_VALUE"); case GL_INVALID_OPERATION: return QStringLiteral("GL_INVALID_OPERATION"); case GL_STACK_OVERFLOW: return QStringLiteral("GL_STACK_OVERFLOW"); case GL_STACK_UNDERFLOW: return QStringLiteral("GL_STACK_UNDERFLOW"); case GL_OUT_OF_MEMORY: return QStringLiteral("GL_OUT_OF_MEMORY"); default: return QLatin1String("0x") + QString::number(err, 16); } } bool checkGLError(const char* txt) { GLenum err = glGetError(); bool hasError = false; while (err != GL_NO_ERROR) { qCWarning(LIBKWINGLUTILS) << "GL error (" << txt << "): " << formatGLError(err); hasError = true; err = glGetError(); } return hasError; } // TODO: Drop for Plasma 6, no longer needed after OpenGL 2.0 int nearestPowerOfTwo(int x) { unsigned y = static_cast(x); // From Hank Warren's "Hacker's Delight", clp2() method. // Works for up to 32-bit integers. y = y - 1; y = y | (y >> 1); y = y | (y >> 2); y = y | (y >> 4); y = y | (y >> 8); y = y | (y >> 16); return static_cast(y + 1); } //**************************************** // GLShader //**************************************** bool GLShader::sColorCorrect = false; GLShader::GLShader(unsigned int flags) : mValid(false) , mLocationsResolved(false) , mExplicitLinking(flags & ExplicitLinking) { mProgram = glCreateProgram(); } GLShader::GLShader(const QString& vertexfile, const QString& fragmentfile, unsigned int flags) : mValid(false) , mLocationsResolved(false) , mExplicitLinking(flags & ExplicitLinking) { mProgram = glCreateProgram(); loadFromFiles(vertexfile, fragmentfile); } GLShader::~GLShader() { if (mProgram) { glDeleteProgram(mProgram); } } bool GLShader::loadFromFiles(const QString &vertexFile, const QString &fragmentFile) { QFile vf(vertexFile); if (!vf.open(QIODevice::ReadOnly)) { qCCritical(LIBKWINGLUTILS) << "Couldn't open" << vertexFile << "for reading!"; return false; } const QByteArray vertexSource = vf.readAll(); QFile ff(fragmentFile); if (!ff.open(QIODevice::ReadOnly)) { qCCritical(LIBKWINGLUTILS) << "Couldn't open" << fragmentFile << "for reading!"; return false; } const QByteArray fragmentSource = ff.readAll(); return load(vertexSource, fragmentSource); } bool GLShader::link() { // Be optimistic mValid = true; glLinkProgram(mProgram); // Get the program info log int maxLength, length; glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &maxLength); QByteArray log(maxLength, 0); glGetProgramInfoLog(mProgram, maxLength, &length, log.data()); // Make sure the program linked successfully int status; glGetProgramiv(mProgram, GL_LINK_STATUS, &status); if (status == 0) { qCCritical(LIBKWINGLUTILS) << "Failed to link shader:" << endl << log; mValid = false; } else if (length > 0) { qCDebug(LIBKWINGLUTILS) << "Shader link log:" << log; } return mValid; } const QByteArray GLShader::prepareSource(GLenum shaderType, const QByteArray &source) const { // Prepare the source code QByteArray ba; if (GLPlatform::instance()->isGLES() && GLPlatform::instance()->glslVersion() < kVersionNumber(3, 0)) { ba.append("precision highp float;\n"); } if (ShaderManager::instance()->isShaderDebug()) { ba.append("#define KWIN_SHADER_DEBUG 1\n"); } ba.append(source); if (GLPlatform::instance()->isGLES() && GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0)) { ba.replace("#version 140", "#version 300 es\n\nprecision highp float;\n"); } // Inject color correction code for fragment shaders, if possible if (shaderType == GL_FRAGMENT_SHADER && sColorCorrect) ba = ColorCorrection::prepareFragmentShader(ba); return ba; } bool GLShader::compile(GLuint program, GLenum shaderType, const QByteArray &source) const { GLuint shader = glCreateShader(shaderType); QByteArray preparedSource = prepareSource(shaderType, source); const char* src = preparedSource.constData(); glShaderSource(shader, 1, &src, nullptr); // Compile the shader glCompileShader(shader); // Get the shader info log int maxLength, length; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); QByteArray log(maxLength, 0); glGetShaderInfoLog(shader, maxLength, &length, log.data()); // Check the status int status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == 0) { const char *typeName = (shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment"); qCCritical(LIBKWINGLUTILS) << "Failed to compile" << typeName << "shader:" << endl << log; } else if (length > 0) qCDebug(LIBKWINGLUTILS) << "Shader compile log:" << log; if (status != 0) glAttachShader(program, shader); glDeleteShader(shader); return status != 0; } bool GLShader::load(const QByteArray &vertexSource, const QByteArray &fragmentSource) { // Make sure shaders are actually supported if (!(GLPlatform::instance()->supports(GLSL) && // we lack shader branching for Texture2DRectangle everywhere - and it's probably not worth it GLPlatform::instance()->supports(TextureNPOT))) { qCCritical(LIBKWINGLUTILS) << "Shaders are not supported"; return false; } mValid = false; // Compile the vertex shader if (!vertexSource.isEmpty()) { bool success = compile(mProgram, GL_VERTEX_SHADER, vertexSource); if (!success) return false; } // Compile the fragment shader if (!fragmentSource.isEmpty()) { bool success = compile(mProgram, GL_FRAGMENT_SHADER, fragmentSource); if (!success) return false; } if (mExplicitLinking) return true; // link() sets mValid return link(); } void GLShader::bindAttributeLocation(const char *name, int index) { glBindAttribLocation(mProgram, index, name); } void GLShader::bindFragDataLocation(const char *name, int index) { if (!GLPlatform::instance()->isGLES() && (hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_gpu_shader4")))) glBindFragDataLocation(mProgram, index, name); } void GLShader::bind() { glUseProgram(mProgram); } void GLShader::unbind() { glUseProgram(0); } void GLShader::resolveLocations() { if (mLocationsResolved) return; mMatrixLocation[TextureMatrix] = uniformLocation("textureMatrix"); mMatrixLocation[ProjectionMatrix] = uniformLocation("projection"); mMatrixLocation[ModelViewMatrix] = uniformLocation("modelview"); mMatrixLocation[ModelViewProjectionMatrix] = uniformLocation("modelViewProjectionMatrix"); mMatrixLocation[WindowTransformation] = uniformLocation("windowTransformation"); mMatrixLocation[ScreenTransformation] = uniformLocation("screenTransformation"); mVec2Location[Offset] = uniformLocation("offset"); mVec4Location[ModulationConstant] = uniformLocation("modulation"); mFloatLocation[Saturation] = uniformLocation("saturation"); mIntLocation[ColorCorrectionLookupTextureUnit] = uniformLocation("u_ccLookupTexture"); mColorLocation[Color] = uniformLocation("geometryColor"); mLocationsResolved = true; } int GLShader::uniformLocation(const char *name) { const int location = glGetUniformLocation(mProgram, name); return location; } bool GLShader::setUniform(GLShader::MatrixUniform uniform, const QMatrix4x4 &matrix) { resolveLocations(); return setUniform(mMatrixLocation[uniform], matrix); } bool GLShader::setUniform(GLShader::Vec2Uniform uniform, const QVector2D &value) { resolveLocations(); return setUniform(mVec2Location[uniform], value); } bool GLShader::setUniform(GLShader::Vec4Uniform uniform, const QVector4D &value) { resolveLocations(); return setUniform(mVec4Location[uniform], value); } bool GLShader::setUniform(GLShader::FloatUniform uniform, float value) { resolveLocations(); return setUniform(mFloatLocation[uniform], value); } bool GLShader::setUniform(GLShader::IntUniform uniform, int value) { resolveLocations(); return setUniform(mIntLocation[uniform], value); } bool GLShader::setUniform(GLShader::ColorUniform uniform, const QVector4D &value) { resolveLocations(); return setUniform(mColorLocation[uniform], value); } bool GLShader::setUniform(GLShader::ColorUniform uniform, const QColor &value) { resolveLocations(); return setUniform(mColorLocation[uniform], value); } bool GLShader::setUniform(const char *name, float value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, int value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector2D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector3D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QVector4D& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QMatrix4x4& value) { const int location = uniformLocation(name); return setUniform(location, value); } bool GLShader::setUniform(const char *name, const QColor& color) { const int location = uniformLocation(name); return setUniform(location, color); } bool GLShader::setUniform(int location, float value) { if (location >= 0) { glUniform1f(location, value); } return (location >= 0); } bool GLShader::setUniform(int location, int value) { if (location >= 0) { glUniform1i(location, value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector2D &value) { if (location >= 0) { glUniform2fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector3D &value) { if (location >= 0) { glUniform3fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QVector4D &value) { if (location >= 0) { glUniform4fv(location, 1, (const GLfloat*)&value); } return (location >= 0); } bool GLShader::setUniform(int location, const QMatrix4x4 &value) { if (location >= 0) { GLfloat m[16]; const auto *data = value.constData(); // i is column, j is row for m for (int i = 0; i < 16; ++i) { m[i] = data[i]; } glUniformMatrix4fv(location, 1, GL_FALSE, m); } return (location >= 0); } bool GLShader::setUniform(int location, const QColor &color) { if (location >= 0) { glUniform4f(location, color.redF(), color.greenF(), color.blueF(), color.alphaF()); } return (location >= 0); } int GLShader::attributeLocation(const char* name) { int location = glGetAttribLocation(mProgram, name); return location; } bool GLShader::setAttribute(const char* name, float value) { int location = attributeLocation(name); if (location >= 0) { glVertexAttrib1f(location, value); } return (location >= 0); } QMatrix4x4 GLShader::getUniformMatrix4x4(const char* name) { int location = uniformLocation(name); if (location >= 0) { GLfloat m[16]; glGetnUniformfv(mProgram, location, sizeof(m), m); QMatrix4x4 matrix(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); matrix.optimize(); return matrix; } else { return QMatrix4x4(); } } //**************************************** // ShaderManager //**************************************** ShaderManager *ShaderManager::s_shaderManager = nullptr; ShaderManager *ShaderManager::instance() { if (!s_shaderManager) { s_shaderManager = new ShaderManager(); } return s_shaderManager; } void ShaderManager::cleanup() { delete s_shaderManager; s_shaderManager = nullptr; } ShaderManager::ShaderManager() { m_debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0; const qint64 coreVersionNumber = GLPlatform::instance()->isGLES() ? kVersionNumber(3, 0) : kVersionNumber(1, 40); if (GLPlatform::instance()->glslVersion() >= coreVersionNumber) { m_resourcePath = QStringLiteral(":/effect-shaders-1.40/"); } else { m_resourcePath = QStringLiteral(":/effect-shaders-1.10/"); } } ShaderManager::~ShaderManager() { while (!m_boundShaders.isEmpty()) { popShader(); } qDeleteAll(m_shaderHash); m_shaderHash.clear(); } static bool fuzzyCompare(const QVector4D &lhs, const QVector4D &rhs) { const float epsilon = 1.0f / 255.0f; return lhs[0] >= rhs[0] - epsilon && lhs[0] <= rhs[0] + epsilon && lhs[1] >= rhs[1] - epsilon && lhs[1] <= rhs[1] + epsilon && lhs[2] >= rhs[2] - epsilon && lhs[2] <= rhs[2] + epsilon && lhs[3] >= rhs[3] - epsilon && lhs[3] <= rhs[3] + epsilon; } static bool checkPixel(int x, int y, const QVector4D &expected, const char *file, int line) { uint8_t data[4]; glReadnPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 4, data); const QVector4D pixel{data[0] / 255.f, data[1] / 255.f, data[2] / 255.f, data[3] / 255.f}; if (fuzzyCompare(pixel, expected)) return true; QMessageLogger(file, line, nullptr).warning() << "Pixel was" << pixel << "expected" << expected; return false; } #define CHECK_PIXEL(x, y, expected) \ checkPixel(x, y, expected, __FILE__, __LINE__) static QVector4D adjustSaturation(const QVector4D &color, float saturation) { const float gray = QVector3D::dotProduct(color.toVector3D(), {0.2126, 0.7152, 0.0722}); return QVector4D{gray, gray, gray, color.w()} * (1.0f - saturation) + color * saturation; } bool ShaderManager::selfTest() { bool pass = true; if (!GLRenderTarget::supported()) { qCWarning(LIBKWINGLUTILS) << "Framebuffer objects not supported - skipping shader tests"; return true; } // Create the source texture QImage image(2, 2, QImage::Format_ARGB32_Premultiplied); image.setPixel(0, 0, 0xffff0000); // Red image.setPixel(1, 0, 0xff00ff00); // Green image.setPixel(0, 1, 0xff0000ff); // Blue image.setPixel(1, 1, 0xffffffff); // White GLTexture src(image); src.setFilter(GL_NEAREST); // Create the render target GLTexture dst(GL_RGBA8, 32, 32); GLRenderTarget fbo(dst); GLRenderTarget::pushRenderTarget(&fbo); // Set up the vertex buffer GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); const GLVertexAttrib attribs[] { { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) }, { VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) }, }; vbo->setAttribLayout(attribs, 2, sizeof(GLVertex2D)); GLVertex2D *verts = (GLVertex2D*) vbo->map(6 * sizeof(GLVertex2D)); verts[0] = GLVertex2D{{0, 0}, {0, 0}}; // Top left verts[1] = GLVertex2D{{0, 32}, {0, 1}}; // Bottom left verts[2] = GLVertex2D{{32, 0}, {1, 0}}; // Top right verts[3] = GLVertex2D{{32, 0}, {1, 0}}; // Top right verts[4] = GLVertex2D{{0, 32}, {0, 1}}; // Bottom left verts[5] = GLVertex2D{{32, 32}, {1, 1}}; // Bottom right vbo->unmap(); vbo->bindArrays(); glViewport(0, 0, 32, 32); glClearColor(0, 0, 0, 0); // Set up the projection matrix QMatrix4x4 matrix; matrix.ortho(QRect(0, 0, 32, 32)); // Bind the source texture src.bind(); const QVector4D red {1.0f, 0.0f, 0.0f, 1.0f}; const QVector4D green {0.0f, 1.0f, 0.0f, 1.0f}; const QVector4D blue {0.0f, 0.0f, 1.0f, 1.0f}; const QVector4D white {1.0f, 1.0f, 1.0f, 1.0f}; // Note: To see the line number in error messages, set // QT_MESSAGE_PATTERN="%{message} (%{file}:%{line})" // Test solid color GLShader *shader = pushShader(ShaderTrait::UniformColor); if (shader->isValid()) { glClear(GL_COLOR_BUFFER_BIT); shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); shader->setUniform(GLShader::Color, green); vbo->draw(GL_TRIANGLES, 0, 6); pass = CHECK_PIXEL(8, 24, green) && pass; pass = CHECK_PIXEL(24, 24, green) && pass; pass = CHECK_PIXEL(8, 8, green) && pass; pass = CHECK_PIXEL(24, 8, green) && pass; } else { pass = false; } popShader(); // Test texture mapping shader = pushShader(ShaderTrait::MapTexture); if (shader->isValid()) { glClear(GL_COLOR_BUFFER_BIT); shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); vbo->draw(GL_TRIANGLES, 0, 6); pass = CHECK_PIXEL(8, 24, red) && pass; pass = CHECK_PIXEL(24, 24, green) && pass; pass = CHECK_PIXEL(8, 8, blue) && pass; pass = CHECK_PIXEL(24, 8, white) && pass; } else { pass = false; } popShader(); // Test saturation filter shader = pushShader(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation); if (shader->isValid()) { glClear(GL_COLOR_BUFFER_BIT); const float saturation = .3; shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); shader->setUniform(GLShader::Saturation, saturation); vbo->draw(GL_TRIANGLES, 0, 6); pass = CHECK_PIXEL(8, 24, adjustSaturation(red, saturation)) && pass; pass = CHECK_PIXEL(24, 24, adjustSaturation(green, saturation)) && pass; pass = CHECK_PIXEL(8, 8, adjustSaturation(blue, saturation)) && pass; pass = CHECK_PIXEL(24, 8, adjustSaturation(white, saturation)) && pass; } else { pass = false; } popShader(); // Test modulation filter shader = pushShader(ShaderTrait::MapTexture | ShaderTrait::Modulate); if (shader->isValid()) { glClear(GL_COLOR_BUFFER_BIT); const QVector4D modulation{.3f, .4f, .5f, .6f}; shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); shader->setUniform(GLShader::ModulationConstant, modulation); vbo->draw(GL_TRIANGLES, 0, 6); pass = CHECK_PIXEL(8, 24, red * modulation) && pass; pass = CHECK_PIXEL(24, 24, green * modulation) && pass; pass = CHECK_PIXEL(8, 8, blue * modulation) && pass; pass = CHECK_PIXEL(24, 8, white * modulation) && pass; } else { pass = false; } popShader(); // Test saturation + modulation shader = pushShader(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation | ShaderTrait::Modulate); if (shader->isValid()) { glClear(GL_COLOR_BUFFER_BIT); const QVector4D modulation{.3f, .4f, .5f, .6f}; const float saturation = .3; shader->setUniform(GLShader::ModelViewProjectionMatrix, matrix); shader->setUniform(GLShader::ModulationConstant, modulation); shader->setUniform(GLShader::Saturation, saturation); vbo->draw(GL_TRIANGLES, 0, 6); pass = CHECK_PIXEL(8, 24, adjustSaturation(red * modulation, saturation)) && pass; pass = CHECK_PIXEL(24, 24, adjustSaturation(green * modulation, saturation)) && pass; pass = CHECK_PIXEL(8, 8, adjustSaturation(blue * modulation, saturation)) && pass; pass = CHECK_PIXEL(24, 8, adjustSaturation(white * modulation, saturation)) && pass; } else { pass = false; } popShader(); vbo->unbindArrays(); GLRenderTarget::popRenderTarget(); return pass; } QByteArray ShaderManager::generateVertexSource(ShaderTraits traits) const { QByteArray source; QTextStream stream(&source); GLPlatform * const gl = GLPlatform::instance(); QByteArray attribute, varying; if (!gl->isGLES()) { const bool glsl_140 = gl->glslVersion() >= kVersionNumber(1, 40); attribute = glsl_140 ? QByteArrayLiteral("in") : QByteArrayLiteral("attribute"); varying = glsl_140 ? QByteArrayLiteral("out") : QByteArrayLiteral("varying"); if (glsl_140) stream << "#version 140\n\n"; } else { const bool glsl_es_300 = gl->glslVersion() >= kVersionNumber(3, 0); attribute = glsl_es_300 ? QByteArrayLiteral("in") : QByteArrayLiteral("attribute"); varying = glsl_es_300 ? QByteArrayLiteral("out") : QByteArrayLiteral("varying"); if (glsl_es_300) stream << "#version 300 es\n\n"; } stream << attribute << " vec4 position;\n"; if (traits & ShaderTrait::MapTexture) { stream << attribute << " vec4 texcoord;\n\n"; stream << varying << " vec2 texcoord0;\n\n"; } else stream << "\n"; stream << "uniform mat4 modelViewProjectionMatrix;\n\n"; stream << "void main()\n{\n"; if (traits & ShaderTrait::MapTexture) stream << " texcoord0 = texcoord.st;\n"; stream << " gl_Position = modelViewProjectionMatrix * position;\n"; stream << "}\n"; stream.flush(); return source; } QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const { QByteArray source; QTextStream stream(&source); GLPlatform * const gl = GLPlatform::instance(); QByteArray varying, output, textureLookup; if (!gl->isGLES()) { const bool glsl_140 = gl->glslVersion() >= kVersionNumber(1, 40); if (glsl_140) stream << "#version 140\n\n"; varying = glsl_140 ? QByteArrayLiteral("in") : QByteArrayLiteral("varying"); textureLookup = glsl_140 ? QByteArrayLiteral("texture") : QByteArrayLiteral("texture2D"); output = glsl_140 ? QByteArrayLiteral("fragColor") : QByteArrayLiteral("gl_FragColor"); } else { const bool glsl_es_300 = GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0); if (glsl_es_300) stream << "#version 300 es\n\n"; // From the GLSL ES specification: // // "The fragment language has no default precision qualifier for floating point types." stream << "precision highp float;\n\n"; varying = glsl_es_300 ? QByteArrayLiteral("in") : QByteArrayLiteral("varying"); textureLookup = glsl_es_300 ? QByteArrayLiteral("texture") : QByteArrayLiteral("texture2D"); output = glsl_es_300 ? QByteArrayLiteral("fragColor") : QByteArrayLiteral("gl_FragColor"); } if (traits & ShaderTrait::MapTexture) { stream << "uniform sampler2D sampler;\n"; if (traits & ShaderTrait::Modulate) stream << "uniform vec4 modulation;\n"; if (traits & ShaderTrait::AdjustSaturation) stream << "uniform float saturation;\n"; stream << "\n" << varying << " vec2 texcoord0;\n"; } else if (traits & ShaderTrait::UniformColor) stream << "uniform vec4 geometryColor;\n"; if (output != QByteArrayLiteral("gl_FragColor")) stream << "\nout vec4 " << output << ";\n"; stream << "\nvoid main(void)\n{\n"; if (traits & ShaderTrait::MapTexture) { if (traits & (ShaderTrait::Modulate | ShaderTrait::AdjustSaturation)) { stream << " vec4 texel = " << textureLookup << "(sampler, texcoord0);\n"; if (traits & ShaderTrait::Modulate) stream << " texel *= modulation;\n"; if (traits & ShaderTrait::AdjustSaturation) stream << " texel.rgb = mix(vec3(dot(texel.rgb, vec3(0.2126, 0.7152, 0.0722))), texel.rgb, saturation);\n"; stream << " " << output << " = texel;\n"; } else { stream << " " << output << " = " << textureLookup << "(sampler, texcoord0);\n"; } } else if (traits & ShaderTrait::UniformColor) stream << " " << output << " = geometryColor;\n"; stream << "}"; stream.flush(); return source; } GLShader *ShaderManager::generateShader(ShaderTraits traits) { return generateCustomShader(traits); } GLShader *ShaderManager::generateCustomShader(ShaderTraits traits, const QByteArray &vertexSource, const QByteArray &fragmentSource) { const QByteArray vertex = vertexSource.isEmpty() ? generateVertexSource(traits) : vertexSource; const QByteArray fragment = fragmentSource.isEmpty() ? generateFragmentSource(traits) : fragmentSource; #if 0 qCDebug(LIBKWINGLUTILS) << "**************"; qCDebug(LIBKWINGLUTILS) << vertex; qCDebug(LIBKWINGLUTILS) << "**************"; qCDebug(LIBKWINGLUTILS) << fragment; qCDebug(LIBKWINGLUTILS) << "**************"; #endif GLShader *shader = new GLShader(GLShader::ExplicitLinking); shader->load(vertex, fragment); shader->bindAttributeLocation("position", VA_Position); shader->bindAttributeLocation("texcoord", VA_TexCoord); shader->bindFragDataLocation("fragColor", 0); shader->link(); return shader; } GLShader *ShaderManager::generateShaderFromResources(ShaderTraits traits, const QString &vertexFile, const QString &fragmentFile) { auto loadShaderFile = [this] (const QString &fileName) { QFile file(m_resourcePath + fileName); if (file.open(QIODevice::ReadOnly)) { return file.readAll(); } qCCritical(LIBKWINGLUTILS) << "Failed to read shader " << fileName; return QByteArray(); }; QByteArray vertexSource; QByteArray fragmentSource; if (!vertexFile.isEmpty()) { vertexSource = loadShaderFile(vertexFile); if (vertexSource.isEmpty()) { return new GLShader(); } } if (!fragmentFile.isEmpty()) { fragmentSource = loadShaderFile(fragmentFile); if (fragmentSource.isEmpty()) { return new GLShader(); } } return generateCustomShader(traits, vertexSource, fragmentSource); } GLShader *ShaderManager::shader(ShaderTraits traits) { GLShader *shader = m_shaderHash.value(traits); if (!shader) { shader = generateShader(traits); m_shaderHash.insert(traits, shader); } return shader; } GLShader *ShaderManager::getBoundShader() const { if (m_boundShaders.isEmpty()) { return nullptr; } else { return m_boundShaders.top(); } } bool ShaderManager::isShaderBound() const { return !m_boundShaders.isEmpty(); } bool ShaderManager::isShaderDebug() const { return m_debug; } GLShader *ShaderManager::pushShader(ShaderTraits traits) { GLShader *shader = this->shader(traits); pushShader(shader); return shader; } void ShaderManager::pushShader(GLShader *shader) { // only bind shader if it is not already bound if (shader != getBoundShader()) { shader->bind(); } m_boundShaders.push(shader); } void ShaderManager::popShader() { if (m_boundShaders.isEmpty()) { return; } GLShader *shader = m_boundShaders.pop(); if (m_boundShaders.isEmpty()) { // no more shader bound - unbind shader->unbind(); } else if (shader != m_boundShaders.top()) { // only rebind if a different shader is on top of stack m_boundShaders.top()->bind(); } } void ShaderManager::bindFragDataLocations(GLShader *shader) { shader->bindFragDataLocation("fragColor", 0); } void ShaderManager::bindAttributeLocations(GLShader *shader) const { shader->bindAttributeLocation("vertex", VA_Position); shader->bindAttributeLocation("texCoord", VA_TexCoord); } GLShader *ShaderManager::loadShaderFromCode(const QByteArray &vertexSource, const QByteArray &fragmentSource) { GLShader *shader = new GLShader(GLShader::ExplicitLinking); shader->load(vertexSource, fragmentSource); bindAttributeLocations(shader); bindFragDataLocations(shader); shader->link(); return shader; } /*** GLRenderTarget ***/ bool GLRenderTarget::sSupported = false; bool GLRenderTarget::s_blitSupported = false; QStack GLRenderTarget::s_renderTargets = QStack(); QSize GLRenderTarget::s_virtualScreenSize; void GLRenderTarget::initStatic() { if (GLPlatform::instance()->isGLES()) { sSupported = true; s_blitSupported = hasGLVersion(3, 0); } else { sSupported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object")); s_blitSupported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_blit")); } } void GLRenderTarget::cleanup() { Q_ASSERT(s_renderTargets.isEmpty()); sSupported = false; s_blitSupported = false; } bool GLRenderTarget::isRenderTargetBound() { return !s_renderTargets.isEmpty(); } bool GLRenderTarget::blitSupported() { return s_blitSupported; } void GLRenderTarget::pushRenderTarget(GLRenderTarget* target) { target->enable(); s_renderTargets.push(target); } GLRenderTarget* GLRenderTarget::popRenderTarget() { GLRenderTarget* ret = s_renderTargets.pop(); ret->disable(); if (!s_renderTargets.isEmpty()) { s_renderTargets.top()->enable(); } else { glViewport (0, 0, s_virtualScreenSize.width(), s_virtualScreenSize.height()); } return ret; } GLRenderTarget::GLRenderTarget(const GLTexture& color) { // Reset variables mValid = false; mTexture = color; // Make sure FBO is supported if (sSupported && !mTexture.isNull()) { initFBO(); } else qCCritical(LIBKWINGLUTILS) << "Render targets aren't supported!"; } GLRenderTarget::~GLRenderTarget() { if (mValid) { glDeleteFramebuffers(1, &mFramebuffer); } } bool GLRenderTarget::enable() { if (!valid()) { qCCritical(LIBKWINGLUTILS) << "Can't enable invalid render target!"; return false; } glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); glViewport(0, 0, mTexture.width(), mTexture.height()); mTexture.setDirty(); return true; } bool GLRenderTarget::disable() { if (!valid()) { qCCritical(LIBKWINGLUTILS) << "Can't disable invalid render target!"; return false; } glBindFramebuffer(GL_FRAMEBUFFER, 0); mTexture.setDirty(); return true; } static QString formatFramebufferStatus(GLenum status) { switch(status) { case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: // An attachment is the wrong type / is invalid / has 0 width or height return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: // There are no images attached to the framebuffer return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); case GL_FRAMEBUFFER_UNSUPPORTED: // A format or the combination of formats of the attachments is unsupported return QStringLiteral("GL_FRAMEBUFFER_UNSUPPORTED"); case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: // Not all attached images have the same width and height return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"); case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: // The color attachments don't have the same format return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"); case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: // The attachments don't have the same number of samples return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: // The draw buffer is missing return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: // The read buffer is missing return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); default: return QStringLiteral("Unknown (0x") + QString::number(status, 16) + QStringLiteral(")"); } } void GLRenderTarget::initFBO() { #if DEBUG_GLRENDERTARGET GLenum err = glGetError(); if (err != GL_NO_ERROR) qCCritical(LIBKWINGLUTILS) << "Error status when entering GLRenderTarget::initFBO: " << formatGLError(err); #endif glGenFramebuffers(1, &mFramebuffer); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { qCCritical(LIBKWINGLUTILS) << "glGenFramebuffers failed: " << formatGLError(err); return; } #endif glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { qCCritical(LIBKWINGLUTILS) << "glBindFramebuffer failed: " << formatGLError(err); glDeleteFramebuffers(1, &mFramebuffer); return; } #endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture.target(), mTexture.texture(), 0); #if DEBUG_GLRENDERTARGET if ((err = glGetError()) != GL_NO_ERROR) { qCCritical(LIBKWINGLUTILS) << "glFramebufferTexture2D failed: " << formatGLError(err); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &mFramebuffer); return; } #endif const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindFramebuffer(GL_FRAMEBUFFER, 0); if (status != GL_FRAMEBUFFER_COMPLETE) { // We have an incomplete framebuffer, consider it invalid if (status == 0) qCCritical(LIBKWINGLUTILS) << "glCheckFramebufferStatus failed: " << formatGLError(glGetError()); else qCCritical(LIBKWINGLUTILS) << "Invalid framebuffer status: " << formatFramebufferStatus(status); glDeleteFramebuffers(1, &mFramebuffer); return; } mValid = true; } void GLRenderTarget::blitFromFramebuffer(const QRect &source, const QRect &destination, GLenum filter) { if (!GLRenderTarget::blitSupported()) { return; } GLRenderTarget::pushRenderTarget(this); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); const QRect s = source.isNull() ? QRect(0, 0, s_virtualScreenSize.width(), s_virtualScreenSize.height()) : source; const QRect d = destination.isNull() ? QRect(0, 0, mTexture.width(), mTexture.height()) : destination; glBlitFramebuffer(s.x(), s_virtualScreenSize.height() - s.y() - s.height(), s.x() + s.width(), s_virtualScreenSize.height() - s.y(), d.x(), mTexture.height() - d.y() - d.height(), d.x() + d.width(), mTexture.height() - d.y(), GL_COLOR_BUFFER_BIT, filter); GLRenderTarget::popRenderTarget(); } void GLRenderTarget::attachTexture(const GLTexture& target) { if (!mValid || mTexture.texture() == target.texture()) { return; } pushRenderTarget(this); mTexture = target; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTexture.target(), mTexture.texture(), 0); popRenderTarget(); } // ------------------------------------------------------------------ static const uint16_t indices[] = { 1, 0, 3, 3, 2, 1, 5, 4, 7, 7, 6, 5, 9, 8, 11, 11, 10, 9, 13, 12, 15, 15, 14, 13, 17, 16, 19, 19, 18, 17, 21, 20, 23, 23, 22, 21, 25, 24, 27, 27, 26, 25, 29, 28, 31, 31, 30, 29, 33, 32, 35, 35, 34, 33, 37, 36, 39, 39, 38, 37, 41, 40, 43, 43, 42, 41, 45, 44, 47, 47, 46, 45, 49, 48, 51, 51, 50, 49, 53, 52, 55, 55, 54, 53, 57, 56, 59, 59, 58, 57, 61, 60, 63, 63, 62, 61, 65, 64, 67, 67, 66, 65, 69, 68, 71, 71, 70, 69, 73, 72, 75, 75, 74, 73, 77, 76, 79, 79, 78, 77, 81, 80, 83, 83, 82, 81, 85, 84, 87, 87, 86, 85, 89, 88, 91, 91, 90, 89, 93, 92, 95, 95, 94, 93, 97, 96, 99, 99, 98, 97, 101, 100, 103, 103, 102, 101, 105, 104, 107, 107, 106, 105, 109, 108, 111, 111, 110, 109, 113, 112, 115, 115, 114, 113, 117, 116, 119, 119, 118, 117, 121, 120, 123, 123, 122, 121, 125, 124, 127, 127, 126, 125, 129, 128, 131, 131, 130, 129, 133, 132, 135, 135, 134, 133, 137, 136, 139, 139, 138, 137, 141, 140, 143, 143, 142, 141, 145, 144, 147, 147, 146, 145, 149, 148, 151, 151, 150, 149, 153, 152, 155, 155, 154, 153, 157, 156, 159, 159, 158, 157, 161, 160, 163, 163, 162, 161, 165, 164, 167, 167, 166, 165, 169, 168, 171, 171, 170, 169, 173, 172, 175, 175, 174, 173, 177, 176, 179, 179, 178, 177, 181, 180, 183, 183, 182, 181, 185, 184, 187, 187, 186, 185, 189, 188, 191, 191, 190, 189, 193, 192, 195, 195, 194, 193, 197, 196, 199, 199, 198, 197, 201, 200, 203, 203, 202, 201, 205, 204, 207, 207, 206, 205, 209, 208, 211, 211, 210, 209, 213, 212, 215, 215, 214, 213, 217, 216, 219, 219, 218, 217, 221, 220, 223, 223, 222, 221, 225, 224, 227, 227, 226, 225, 229, 228, 231, 231, 230, 229, 233, 232, 235, 235, 234, 233, 237, 236, 239, 239, 238, 237, 241, 240, 243, 243, 242, 241, 245, 244, 247, 247, 246, 245, 249, 248, 251, 251, 250, 249, 253, 252, 255, 255, 254, 253, 257, 256, 259, 259, 258, 257, 261, 260, 263, 263, 262, 261, 265, 264, 267, 267, 266, 265, 269, 268, 271, 271, 270, 269, 273, 272, 275, 275, 274, 273, 277, 276, 279, 279, 278, 277, 281, 280, 283, 283, 282, 281, 285, 284, 287, 287, 286, 285, 289, 288, 291, 291, 290, 289, 293, 292, 295, 295, 294, 293, 297, 296, 299, 299, 298, 297, 301, 300, 303, 303, 302, 301, 305, 304, 307, 307, 306, 305, 309, 308, 311, 311, 310, 309, 313, 312, 315, 315, 314, 313, 317, 316, 319, 319, 318, 317, 321, 320, 323, 323, 322, 321, 325, 324, 327, 327, 326, 325, 329, 328, 331, 331, 330, 329, 333, 332, 335, 335, 334, 333, 337, 336, 339, 339, 338, 337, 341, 340, 343, 343, 342, 341, 345, 344, 347, 347, 346, 345, 349, 348, 351, 351, 350, 349, 353, 352, 355, 355, 354, 353, 357, 356, 359, 359, 358, 357, 361, 360, 363, 363, 362, 361, 365, 364, 367, 367, 366, 365, 369, 368, 371, 371, 370, 369, 373, 372, 375, 375, 374, 373, 377, 376, 379, 379, 378, 377, 381, 380, 383, 383, 382, 381, 385, 384, 387, 387, 386, 385, 389, 388, 391, 391, 390, 389, 393, 392, 395, 395, 394, 393, 397, 396, 399, 399, 398, 397, 401, 400, 403, 403, 402, 401, 405, 404, 407, 407, 406, 405, 409, 408, 411, 411, 410, 409, 413, 412, 415, 415, 414, 413, 417, 416, 419, 419, 418, 417, 421, 420, 423, 423, 422, 421, 425, 424, 427, 427, 426, 425, 429, 428, 431, 431, 430, 429, 433, 432, 435, 435, 434, 433, 437, 436, 439, 439, 438, 437, 441, 440, 443, 443, 442, 441, 445, 444, 447, 447, 446, 445, 449, 448, 451, 451, 450, 449, 453, 452, 455, 455, 454, 453, 457, 456, 459, 459, 458, 457, 461, 460, 463, 463, 462, 461, 465, 464, 467, 467, 466, 465, 469, 468, 471, 471, 470, 469, 473, 472, 475, 475, 474, 473, 477, 476, 479, 479, 478, 477, 481, 480, 483, 483, 482, 481, 485, 484, 487, 487, 486, 485, 489, 488, 491, 491, 490, 489, 493, 492, 495, 495, 494, 493, 497, 496, 499, 499, 498, 497, 501, 500, 503, 503, 502, 501, 505, 504, 507, 507, 506, 505, 509, 508, 511, 511, 510, 509, 513, 512, 515, 515, 514, 513, 517, 516, 519, 519, 518, 517, 521, 520, 523, 523, 522, 521, 525, 524, 527, 527, 526, 525, 529, 528, 531, 531, 530, 529, 533, 532, 535, 535, 534, 533, 537, 536, 539, 539, 538, 537, 541, 540, 543, 543, 542, 541, 545, 544, 547, 547, 546, 545, 549, 548, 551, 551, 550, 549, 553, 552, 555, 555, 554, 553, 557, 556, 559, 559, 558, 557, 561, 560, 563, 563, 562, 561, 565, 564, 567, 567, 566, 565, 569, 568, 571, 571, 570, 569, 573, 572, 575, 575, 574, 573, 577, 576, 579, 579, 578, 577, 581, 580, 583, 583, 582, 581, 585, 584, 587, 587, 586, 585, 589, 588, 591, 591, 590, 589, 593, 592, 595, 595, 594, 593, 597, 596, 599, 599, 598, 597, 601, 600, 603, 603, 602, 601, 605, 604, 607, 607, 606, 605, 609, 608, 611, 611, 610, 609, 613, 612, 615, 615, 614, 613, 617, 616, 619, 619, 618, 617, 621, 620, 623, 623, 622, 621, 625, 624, 627, 627, 626, 625, 629, 628, 631, 631, 630, 629, 633, 632, 635, 635, 634, 633, 637, 636, 639, 639, 638, 637, 641, 640, 643, 643, 642, 641, 645, 644, 647, 647, 646, 645, 649, 648, 651, 651, 650, 649, 653, 652, 655, 655, 654, 653, 657, 656, 659, 659, 658, 657, 661, 660, 663, 663, 662, 661, 665, 664, 667, 667, 666, 665, 669, 668, 671, 671, 670, 669, 673, 672, 675, 675, 674, 673, 677, 676, 679, 679, 678, 677, 681, 680, 683, 683, 682, 681, 685, 684, 687, 687, 686, 685, 689, 688, 691, 691, 690, 689, 693, 692, 695, 695, 694, 693, 697, 696, 699, 699, 698, 697, 701, 700, 703, 703, 702, 701, 705, 704, 707, 707, 706, 705, 709, 708, 711, 711, 710, 709, 713, 712, 715, 715, 714, 713, 717, 716, 719, 719, 718, 717, 721, 720, 723, 723, 722, 721, 725, 724, 727, 727, 726, 725, 729, 728, 731, 731, 730, 729, 733, 732, 735, 735, 734, 733, 737, 736, 739, 739, 738, 737, 741, 740, 743, 743, 742, 741, 745, 744, 747, 747, 746, 745, 749, 748, 751, 751, 750, 749, 753, 752, 755, 755, 754, 753, 757, 756, 759, 759, 758, 757, 761, 760, 763, 763, 762, 761, 765, 764, 767, 767, 766, 765, 769, 768, 771, 771, 770, 769, 773, 772, 775, 775, 774, 773, 777, 776, 779, 779, 778, 777, 781, 780, 783, 783, 782, 781, 785, 784, 787, 787, 786, 785, 789, 788, 791, 791, 790, 789, 793, 792, 795, 795, 794, 793, 797, 796, 799, 799, 798, 797, 801, 800, 803, 803, 802, 801, 805, 804, 807, 807, 806, 805, 809, 808, 811, 811, 810, 809, 813, 812, 815, 815, 814, 813, 817, 816, 819, 819, 818, 817, 821, 820, 823, 823, 822, 821, 825, 824, 827, 827, 826, 825, 829, 828, 831, 831, 830, 829, 833, 832, 835, 835, 834, 833, 837, 836, 839, 839, 838, 837, 841, 840, 843, 843, 842, 841, 845, 844, 847, 847, 846, 845, 849, 848, 851, 851, 850, 849, 853, 852, 855, 855, 854, 853, 857, 856, 859, 859, 858, 857, 861, 860, 863, 863, 862, 861, 865, 864, 867, 867, 866, 865, 869, 868, 871, 871, 870, 869, 873, 872, 875, 875, 874, 873, 877, 876, 879, 879, 878, 877, 881, 880, 883, 883, 882, 881, 885, 884, 887, 887, 886, 885, 889, 888, 891, 891, 890, 889, 893, 892, 895, 895, 894, 893, 897, 896, 899, 899, 898, 897, 901, 900, 903, 903, 902, 901, 905, 904, 907, 907, 906, 905, 909, 908, 911, 911, 910, 909, 913, 912, 915, 915, 914, 913, 917, 916, 919, 919, 918, 917, 921, 920, 923, 923, 922, 921, 925, 924, 927, 927, 926, 925, 929, 928, 931, 931, 930, 929, 933, 932, 935, 935, 934, 933, 937, 936, 939, 939, 938, 937, 941, 940, 943, 943, 942, 941, 945, 944, 947, 947, 946, 945, 949, 948, 951, 951, 950, 949, 953, 952, 955, 955, 954, 953, 957, 956, 959, 959, 958, 957, 961, 960, 963, 963, 962, 961, 965, 964, 967, 967, 966, 965, 969, 968, 971, 971, 970, 969, 973, 972, 975, 975, 974, 973, 977, 976, 979, 979, 978, 977, 981, 980, 983, 983, 982, 981, 985, 984, 987, 987, 986, 985, 989, 988, 991, 991, 990, 989, 993, 992, 995, 995, 994, 993, 997, 996, 999, 999, 998, 997, 1001, 1000, 1003, 1003, 1002, 1001, 1005, 1004, 1007, 1007, 1006, 1005, 1009, 1008, 1011, 1011, 1010, 1009, 1013, 1012, 1015, 1015, 1014, 1013, 1017, 1016, 1019, 1019, 1018, 1017, 1021, 1020, 1023, 1023, 1022, 1021, 1025, 1024, 1027, 1027, 1026, 1025, 1029, 1028, 1031, 1031, 1030, 1029, 1033, 1032, 1035, 1035, 1034, 1033, 1037, 1036, 1039, 1039, 1038, 1037, 1041, 1040, 1043, 1043, 1042, 1041, 1045, 1044, 1047, 1047, 1046, 1045, 1049, 1048, 1051, 1051, 1050, 1049, 1053, 1052, 1055, 1055, 1054, 1053, 1057, 1056, 1059, 1059, 1058, 1057, 1061, 1060, 1063, 1063, 1062, 1061, 1065, 1064, 1067, 1067, 1066, 1065, 1069, 1068, 1071, 1071, 1070, 1069, 1073, 1072, 1075, 1075, 1074, 1073, 1077, 1076, 1079, 1079, 1078, 1077, 1081, 1080, 1083, 1083, 1082, 1081, 1085, 1084, 1087, 1087, 1086, 1085, 1089, 1088, 1091, 1091, 1090, 1089, 1093, 1092, 1095, 1095, 1094, 1093, 1097, 1096, 1099, 1099, 1098, 1097, 1101, 1100, 1103, 1103, 1102, 1101, 1105, 1104, 1107, 1107, 1106, 1105, 1109, 1108, 1111, 1111, 1110, 1109, 1113, 1112, 1115, 1115, 1114, 1113, 1117, 1116, 1119, 1119, 1118, 1117, 1121, 1120, 1123, 1123, 1122, 1121, 1125, 1124, 1127, 1127, 1126, 1125, 1129, 1128, 1131, 1131, 1130, 1129, 1133, 1132, 1135, 1135, 1134, 1133, 1137, 1136, 1139, 1139, 1138, 1137, 1141, 1140, 1143, 1143, 1142, 1141, 1145, 1144, 1147, 1147, 1146, 1145, 1149, 1148, 1151, 1151, 1150, 1149, 1153, 1152, 1155, 1155, 1154, 1153, 1157, 1156, 1159, 1159, 1158, 1157, 1161, 1160, 1163, 1163, 1162, 1161, 1165, 1164, 1167, 1167, 1166, 1165, 1169, 1168, 1171, 1171, 1170, 1169, 1173, 1172, 1175, 1175, 1174, 1173, 1177, 1176, 1179, 1179, 1178, 1177, 1181, 1180, 1183, 1183, 1182, 1181, 1185, 1184, 1187, 1187, 1186, 1185, 1189, 1188, 1191, 1191, 1190, 1189, 1193, 1192, 1195, 1195, 1194, 1193, 1197, 1196, 1199, 1199, 1198, 1197, 1201, 1200, 1203, 1203, 1202, 1201, 1205, 1204, 1207, 1207, 1206, 1205, 1209, 1208, 1211, 1211, 1210, 1209, 1213, 1212, 1215, 1215, 1214, 1213, 1217, 1216, 1219, 1219, 1218, 1217, 1221, 1220, 1223, 1223, 1222, 1221, 1225, 1224, 1227, 1227, 1226, 1225, 1229, 1228, 1231, 1231, 1230, 1229, 1233, 1232, 1235, 1235, 1234, 1233, 1237, 1236, 1239, 1239, 1238, 1237, 1241, 1240, 1243, 1243, 1242, 1241, 1245, 1244, 1247, 1247, 1246, 1245, 1249, 1248, 1251, 1251, 1250, 1249, 1253, 1252, 1255, 1255, 1254, 1253, 1257, 1256, 1259, 1259, 1258, 1257, 1261, 1260, 1263, 1263, 1262, 1261, 1265, 1264, 1267, 1267, 1266, 1265, 1269, 1268, 1271, 1271, 1270, 1269, 1273, 1272, 1275, 1275, 1274, 1273, 1277, 1276, 1279, 1279, 1278, 1277, 1281, 1280, 1283, 1283, 1282, 1281, 1285, 1284, 1287, 1287, 1286, 1285, 1289, 1288, 1291, 1291, 1290, 1289, 1293, 1292, 1295, 1295, 1294, 1293, 1297, 1296, 1299, 1299, 1298, 1297, 1301, 1300, 1303, 1303, 1302, 1301, 1305, 1304, 1307, 1307, 1306, 1305, 1309, 1308, 1311, 1311, 1310, 1309, 1313, 1312, 1315, 1315, 1314, 1313, 1317, 1316, 1319, 1319, 1318, 1317, 1321, 1320, 1323, 1323, 1322, 1321, 1325, 1324, 1327, 1327, 1326, 1325, 1329, 1328, 1331, 1331, 1330, 1329, 1333, 1332, 1335, 1335, 1334, 1333, 1337, 1336, 1339, 1339, 1338, 1337, 1341, 1340, 1343, 1343, 1342, 1341, 1345, 1344, 1347, 1347, 1346, 1345, 1349, 1348, 1351, 1351, 1350, 1349, 1353, 1352, 1355, 1355, 1354, 1353, 1357, 1356, 1359, 1359, 1358, 1357, 1361, 1360, 1363, 1363, 1362, 1361, 1365, 1364, 1367, 1367, 1366, 1365, 1369, 1368, 1371, 1371, 1370, 1369, 1373, 1372, 1375, 1375, 1374, 1373, 1377, 1376, 1379, 1379, 1378, 1377, 1381, 1380, 1383, 1383, 1382, 1381, 1385, 1384, 1387, 1387, 1386, 1385, 1389, 1388, 1391, 1391, 1390, 1389, 1393, 1392, 1395, 1395, 1394, 1393, 1397, 1396, 1399, 1399, 1398, 1397, 1401, 1400, 1403, 1403, 1402, 1401, 1405, 1404, 1407, 1407, 1406, 1405, 1409, 1408, 1411, 1411, 1410, 1409, 1413, 1412, 1415, 1415, 1414, 1413, 1417, 1416, 1419, 1419, 1418, 1417, 1421, 1420, 1423, 1423, 1422, 1421, 1425, 1424, 1427, 1427, 1426, 1425, 1429, 1428, 1431, 1431, 1430, 1429, 1433, 1432, 1435, 1435, 1434, 1433, 1437, 1436, 1439, 1439, 1438, 1437, 1441, 1440, 1443, 1443, 1442, 1441, 1445, 1444, 1447, 1447, 1446, 1445, 1449, 1448, 1451, 1451, 1450, 1449, 1453, 1452, 1455, 1455, 1454, 1453, 1457, 1456, 1459, 1459, 1458, 1457, 1461, 1460, 1463, 1463, 1462, 1461, 1465, 1464, 1467, 1467, 1466, 1465, 1469, 1468, 1471, 1471, 1470, 1469, 1473, 1472, 1475, 1475, 1474, 1473, 1477, 1476, 1479, 1479, 1478, 1477, 1481, 1480, 1483, 1483, 1482, 1481, 1485, 1484, 1487, 1487, 1486, 1485, 1489, 1488, 1491, 1491, 1490, 1489, 1493, 1492, 1495, 1495, 1494, 1493, 1497, 1496, 1499, 1499, 1498, 1497, 1501, 1500, 1503, 1503, 1502, 1501, 1505, 1504, 1507, 1507, 1506, 1505, 1509, 1508, 1511, 1511, 1510, 1509, 1513, 1512, 1515, 1515, 1514, 1513, 1517, 1516, 1519, 1519, 1518, 1517, 1521, 1520, 1523, 1523, 1522, 1521, 1525, 1524, 1527, 1527, 1526, 1525, 1529, 1528, 1531, 1531, 1530, 1529, 1533, 1532, 1535, 1535, 1534, 1533, 1537, 1536, 1539, 1539, 1538, 1537, 1541, 1540, 1543, 1543, 1542, 1541, 1545, 1544, 1547, 1547, 1546, 1545, 1549, 1548, 1551, 1551, 1550, 1549, 1553, 1552, 1555, 1555, 1554, 1553, 1557, 1556, 1559, 1559, 1558, 1557, 1561, 1560, 1563, 1563, 1562, 1561, 1565, 1564, 1567, 1567, 1566, 1565, 1569, 1568, 1571, 1571, 1570, 1569, 1573, 1572, 1575, 1575, 1574, 1573, 1577, 1576, 1579, 1579, 1578, 1577, 1581, 1580, 1583, 1583, 1582, 1581, 1585, 1584, 1587, 1587, 1586, 1585, 1589, 1588, 1591, 1591, 1590, 1589, 1593, 1592, 1595, 1595, 1594, 1593, 1597, 1596, 1599, 1599, 1598, 1597, 1601, 1600, 1603, 1603, 1602, 1601, 1605, 1604, 1607, 1607, 1606, 1605, 1609, 1608, 1611, 1611, 1610, 1609, 1613, 1612, 1615, 1615, 1614, 1613, 1617, 1616, 1619, 1619, 1618, 1617, 1621, 1620, 1623, 1623, 1622, 1621, 1625, 1624, 1627, 1627, 1626, 1625, 1629, 1628, 1631, 1631, 1630, 1629, 1633, 1632, 1635, 1635, 1634, 1633, 1637, 1636, 1639, 1639, 1638, 1637, 1641, 1640, 1643, 1643, 1642, 1641, 1645, 1644, 1647, 1647, 1646, 1645, 1649, 1648, 1651, 1651, 1650, 1649, 1653, 1652, 1655, 1655, 1654, 1653, 1657, 1656, 1659, 1659, 1658, 1657, 1661, 1660, 1663, 1663, 1662, 1661, 1665, 1664, 1667, 1667, 1666, 1665, 1669, 1668, 1671, 1671, 1670, 1669, 1673, 1672, 1675, 1675, 1674, 1673, 1677, 1676, 1679, 1679, 1678, 1677, 1681, 1680, 1683, 1683, 1682, 1681, 1685, 1684, 1687, 1687, 1686, 1685, 1689, 1688, 1691, 1691, 1690, 1689, 1693, 1692, 1695, 1695, 1694, 1693, 1697, 1696, 1699, 1699, 1698, 1697, 1701, 1700, 1703, 1703, 1702, 1701, 1705, 1704, 1707, 1707, 1706, 1705, 1709, 1708, 1711, 1711, 1710, 1709, 1713, 1712, 1715, 1715, 1714, 1713, 1717, 1716, 1719, 1719, 1718, 1717, 1721, 1720, 1723, 1723, 1722, 1721, 1725, 1724, 1727, 1727, 1726, 1725, 1729, 1728, 1731, 1731, 1730, 1729, 1733, 1732, 1735, 1735, 1734, 1733, 1737, 1736, 1739, 1739, 1738, 1737, 1741, 1740, 1743, 1743, 1742, 1741, 1745, 1744, 1747, 1747, 1746, 1745, 1749, 1748, 1751, 1751, 1750, 1749, 1753, 1752, 1755, 1755, 1754, 1753, 1757, 1756, 1759, 1759, 1758, 1757, 1761, 1760, 1763, 1763, 1762, 1761, 1765, 1764, 1767, 1767, 1766, 1765, 1769, 1768, 1771, 1771, 1770, 1769, 1773, 1772, 1775, 1775, 1774, 1773, 1777, 1776, 1779, 1779, 1778, 1777, 1781, 1780, 1783, 1783, 1782, 1781, 1785, 1784, 1787, 1787, 1786, 1785, 1789, 1788, 1791, 1791, 1790, 1789, 1793, 1792, 1795, 1795, 1794, 1793, 1797, 1796, 1799, 1799, 1798, 1797, 1801, 1800, 1803, 1803, 1802, 1801, 1805, 1804, 1807, 1807, 1806, 1805, 1809, 1808, 1811, 1811, 1810, 1809, 1813, 1812, 1815, 1815, 1814, 1813, 1817, 1816, 1819, 1819, 1818, 1817, 1821, 1820, 1823, 1823, 1822, 1821, 1825, 1824, 1827, 1827, 1826, 1825, 1829, 1828, 1831, 1831, 1830, 1829, 1833, 1832, 1835, 1835, 1834, 1833, 1837, 1836, 1839, 1839, 1838, 1837, 1841, 1840, 1843, 1843, 1842, 1841, 1845, 1844, 1847, 1847, 1846, 1845, 1849, 1848, 1851, 1851, 1850, 1849, 1853, 1852, 1855, 1855, 1854, 1853, 1857, 1856, 1859, 1859, 1858, 1857, 1861, 1860, 1863, 1863, 1862, 1861, 1865, 1864, 1867, 1867, 1866, 1865, 1869, 1868, 1871, 1871, 1870, 1869, 1873, 1872, 1875, 1875, 1874, 1873, 1877, 1876, 1879, 1879, 1878, 1877, 1881, 1880, 1883, 1883, 1882, 1881, 1885, 1884, 1887, 1887, 1886, 1885, 1889, 1888, 1891, 1891, 1890, 1889, 1893, 1892, 1895, 1895, 1894, 1893, 1897, 1896, 1899, 1899, 1898, 1897, 1901, 1900, 1903, 1903, 1902, 1901, 1905, 1904, 1907, 1907, 1906, 1905, 1909, 1908, 1911, 1911, 1910, 1909, 1913, 1912, 1915, 1915, 1914, 1913, 1917, 1916, 1919, 1919, 1918, 1917, 1921, 1920, 1923, 1923, 1922, 1921, 1925, 1924, 1927, 1927, 1926, 1925, 1929, 1928, 1931, 1931, 1930, 1929, 1933, 1932, 1935, 1935, 1934, 1933, 1937, 1936, 1939, 1939, 1938, 1937, 1941, 1940, 1943, 1943, 1942, 1941, 1945, 1944, 1947, 1947, 1946, 1945, 1949, 1948, 1951, 1951, 1950, 1949, 1953, 1952, 1955, 1955, 1954, 1953, 1957, 1956, 1959, 1959, 1958, 1957, 1961, 1960, 1963, 1963, 1962, 1961, 1965, 1964, 1967, 1967, 1966, 1965, 1969, 1968, 1971, 1971, 1970, 1969, 1973, 1972, 1975, 1975, 1974, 1973, 1977, 1976, 1979, 1979, 1978, 1977, 1981, 1980, 1983, 1983, 1982, 1981, 1985, 1984, 1987, 1987, 1986, 1985, 1989, 1988, 1991, 1991, 1990, 1989, 1993, 1992, 1995, 1995, 1994, 1993, 1997, 1996, 1999, 1999, 1998, 1997, 2001, 2000, 2003, 2003, 2002, 2001, 2005, 2004, 2007, 2007, 2006, 2005, 2009, 2008, 2011, 2011, 2010, 2009, 2013, 2012, 2015, 2015, 2014, 2013, 2017, 2016, 2019, 2019, 2018, 2017, 2021, 2020, 2023, 2023, 2022, 2021, 2025, 2024, 2027, 2027, 2026, 2025, 2029, 2028, 2031, 2031, 2030, 2029, 2033, 2032, 2035, 2035, 2034, 2033, 2037, 2036, 2039, 2039, 2038, 2037, 2041, 2040, 2043, 2043, 2042, 2041, 2045, 2044, 2047, 2047, 2046, 2045 }; template T align(T value, int bytes) { return (value + bytes - 1) & ~T(bytes - 1); } class IndexBuffer { public: IndexBuffer(); ~IndexBuffer(); void accomodate(int count); void bind(); private: GLuint m_buffer; size_t m_size; int m_count; }; IndexBuffer::IndexBuffer() { // The maximum number of quads we can render with 16 bit indices is 16,384. // But we start with 512 and grow the buffer as needed. m_size = sizeof(indices); m_count = m_size / (6 * sizeof(uint16_t)); glGenBuffers(1, &m_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); } IndexBuffer::~IndexBuffer() { glDeleteBuffers(1, &m_buffer); } void IndexBuffer::accomodate(int count) { // Check if we need to grow the buffer. if (count <= m_count) return; count = align(count, 128); size_t size = 6 * sizeof(uint16_t) * count; // Create a new buffer object GLuint buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, GL_STATIC_DRAW); // Use the GPU to copy the data from the old object to the new object, glBindBuffer(GL_COPY_READ_BUFFER, m_buffer); glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_ELEMENT_ARRAY_BUFFER, 0, 0, m_size); glDeleteBuffers(1, &m_buffer); glFlush(); // Needed to work around what appears to be a CP DMA issue in r600g // Map the new object and fill in the uninitialized section const GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_RANGE_BIT; uint16_t *map = (uint16_t *) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, m_size, size - m_size, access); const uint16_t index[] = { 1, 0, 3, 3, 2, 1 }; for (int i = m_count; i < count; i++) { for (int j = 0; j < 6; j++) *(map++) = i * 4 + index[j]; } glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); m_buffer = buffer; m_count = count; m_size = size; } void IndexBuffer::bind() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer); } // ------------------------------------------------------------------ class BitRef { public: BitRef(uint32_t &bitfield, int bit) : m_bitfield(bitfield), m_mask(1 << bit) {} void operator = (bool val) { if (val) m_bitfield |= m_mask; else m_bitfield &= ~m_mask; } operator bool () const { return m_bitfield & m_mask; } private: uint32_t &m_bitfield; int const m_mask; }; // ------------------------------------------------------------------ class Bitfield { public: Bitfield() : m_bitfield(0) {} Bitfield(uint32_t bits) : m_bitfield(bits) {} void set(int i) { m_bitfield |= (1 << i); } void clear(int i) { m_bitfield &= ~(1 << i); } BitRef operator [] (int i) { return BitRef(m_bitfield, i); } operator uint32_t () const { return m_bitfield; } private: uint32_t m_bitfield; }; // ------------------------------------------------------------------ class BitfieldIterator { public: BitfieldIterator(uint32_t bitfield) : m_bitfield(bitfield) {} bool hasNext() const { return m_bitfield != 0; } int next() { const int bit = ffs(m_bitfield) - 1; m_bitfield ^= (1 << bit); return bit; } private: uint32_t m_bitfield; }; // ------------------------------------------------------------------ struct VertexAttrib { int size; GLenum type; int offset; }; // ------------------------------------------------------------------ struct BufferFence { GLsync sync; intptr_t nextEnd; bool signaled() const { GLint value; glGetSynciv(sync, GL_SYNC_STATUS, 1, nullptr, &value); return value == GL_SIGNALED; } }; static void deleteAll(std::deque &fences) { for (const BufferFence &fence : fences) glDeleteSync(fence.sync); fences.clear(); } // ------------------------------------------------------------------ template struct FrameSizesArray { public: FrameSizesArray() { m_array.fill(0); } void push(size_t size) { m_array[m_index] = size; m_index = (m_index + 1) % Count; } size_t average() const { size_t sum = 0; for (size_t size : m_array) sum += size; return sum / Count; } private: std::array m_array; int m_index = 0; }; //********************************* // GLVertexBufferPrivate //********************************* class GLVertexBufferPrivate { public: GLVertexBufferPrivate(GLVertexBuffer::UsageHint usageHint) : vertexCount(0) , persistent(false) , useColor(false) , color(0, 0, 0, 255) , bufferSize(0) , bufferEnd(0) , mappedSize(0) , frameSize(0) , nextOffset(0) , baseAddress(0) , map(nullptr) { glGenBuffers(1, &buffer); switch(usageHint) { case GLVertexBuffer::Dynamic: usage = GL_DYNAMIC_DRAW; break; case GLVertexBuffer::Static: usage = GL_STATIC_DRAW; break; default: usage = GL_STREAM_DRAW; break; } } ~GLVertexBufferPrivate() { deleteAll(fences); if (buffer != 0) { glDeleteBuffers(1, &buffer); map = nullptr; } } void interleaveArrays(float *array, int dim, const float *vertices, const float *texcoords, int count); void bindArrays(); void unbindArrays(); void reallocateBuffer(size_t size); GLvoid *mapNextFreeRange(size_t size); void reallocatePersistentBuffer(size_t size); bool awaitFence(intptr_t offset); GLvoid *getIdleRange(size_t size); GLuint buffer; GLenum usage; int stride; int vertexCount; static GLVertexBuffer *streamingBuffer; static bool haveBufferStorage; static bool haveSyncFences; static bool hasMapBufferRange; static bool supportsIndexedQuads; QByteArray dataStore; bool persistent; bool useColor; QVector4D color; size_t bufferSize; intptr_t bufferEnd; size_t mappedSize; size_t frameSize; intptr_t nextOffset; intptr_t baseAddress; uint8_t *map; std::deque fences; FrameSizesArray<4> frameSizes; VertexAttrib attrib[VertexAttributeCount]; Bitfield enabledArrays; static IndexBuffer *s_indexBuffer; }; bool GLVertexBufferPrivate::hasMapBufferRange = false; bool GLVertexBufferPrivate::supportsIndexedQuads = false; GLVertexBuffer *GLVertexBufferPrivate::streamingBuffer = nullptr; bool GLVertexBufferPrivate::haveBufferStorage = false; bool GLVertexBufferPrivate::haveSyncFences = false; IndexBuffer *GLVertexBufferPrivate::s_indexBuffer = nullptr; void GLVertexBufferPrivate::interleaveArrays(float *dst, int dim, const float *vertices, const float *texcoords, int count) { if (!texcoords) { memcpy((void *) dst, vertices, dim * sizeof(float) * count); return; } switch (dim) { case 2: for (int i = 0; i < count; i++) { *(dst++) = *(vertices++); *(dst++) = *(vertices++); *(dst++) = *(texcoords++); *(dst++) = *(texcoords++); } break; case 3: for (int i = 0; i < count; i++) { *(dst++) = *(vertices++); *(dst++) = *(vertices++); *(dst++) = *(vertices++); *(dst++) = *(texcoords++); *(dst++) = *(texcoords++); } break; default: for (int i = 0; i < count; i++) { for (int j = 0; j < dim; j++) *(dst++) = *(vertices++); *(dst++) = *(texcoords++); *(dst++) = *(texcoords++); } } } void GLVertexBufferPrivate::bindArrays() { if (useColor) { GLShader *shader = ShaderManager::instance()->getBoundShader(); shader->setUniform(GLShader::Color, color); } glBindBuffer(GL_ARRAY_BUFFER, buffer); BitfieldIterator it(enabledArrays); while (it.hasNext()) { const int index = it.next(); glVertexAttribPointer(index, attrib[index].size, attrib[index].type, GL_FALSE, stride, (const GLvoid *) (baseAddress + attrib[index].offset)); glEnableVertexAttribArray(index); } } void GLVertexBufferPrivate::unbindArrays() { BitfieldIterator it(enabledArrays); while (it.hasNext()) glDisableVertexAttribArray(it.next()); } void GLVertexBufferPrivate::reallocatePersistentBuffer(size_t size) { if (buffer != 0) { // This also unmaps and unbinds the buffer glDeleteBuffers(1, &buffer); buffer = 0; deleteAll(fences); } if (buffer == 0) glGenBuffers(1, &buffer); // Round the size up to 64 kb size_t minSize = qMax(frameSizes.average() * 3, 128 * 1024); bufferSize = align(qMax(size, minSize), 64 * 1024); const GLbitfield storage = GL_DYNAMIC_STORAGE_BIT; const GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferStorage(GL_ARRAY_BUFFER, bufferSize, nullptr, storage | access); map = (uint8_t *) glMapBufferRange(GL_ARRAY_BUFFER, 0, bufferSize, access); nextOffset = 0; bufferEnd = bufferSize; } bool GLVertexBufferPrivate::awaitFence(intptr_t end) { // Skip fences until we reach the end offset while (!fences.empty() && fences.front().nextEnd < end) { glDeleteSync(fences.front().sync); fences.pop_front(); } assert(!fences.empty()); // Wait on the next fence const BufferFence &fence = fences.front(); if (!fence.signaled()) { qCDebug(LIBKWINGLUTILS) << "Stalling on VBO fence"; const GLenum ret = glClientWaitSync(fence.sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000); if (ret == GL_TIMEOUT_EXPIRED || ret == GL_WAIT_FAILED) { qCCritical(LIBKWINGLUTILS) << "Wait failed"; return false; } } glDeleteSync(fence.sync); // Update the end pointer bufferEnd = fence.nextEnd; fences.pop_front(); return true; } GLvoid *GLVertexBufferPrivate::getIdleRange(size_t size) { if (unlikely(size > bufferSize)) reallocatePersistentBuffer(size * 2); // Handle wrap-around if (unlikely(nextOffset + size > bufferSize)) { nextOffset = 0; bufferEnd -= bufferSize; for (BufferFence &fence : fences) fence.nextEnd -= bufferSize; // Emit a fence now BufferFence fence; fence.sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); fence.nextEnd = bufferSize; fences.emplace_back(fence); } if (unlikely(nextOffset + intptr_t(size) > bufferEnd)) { if (!awaitFence(nextOffset + size)) return nullptr; } return map + nextOffset; } void GLVertexBufferPrivate::reallocateBuffer(size_t size) { // Round the size up to 4 Kb for streaming/dynamic buffers. const size_t minSize = 32768; // Minimum size for streaming buffers const size_t alloc = usage != GL_STATIC_DRAW ? align(qMax(size, minSize), 4096) : size; glBufferData(GL_ARRAY_BUFFER, alloc, 0, usage); bufferSize = alloc; } GLvoid *GLVertexBufferPrivate::mapNextFreeRange(size_t size) { GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; if ((nextOffset + size) > bufferSize) { // Reallocate the data store if it's too small. if (size > bufferSize) { reallocateBuffer(size); } else { access |= GL_MAP_INVALIDATE_BUFFER_BIT; access ^= GL_MAP_UNSYNCHRONIZED_BIT; } nextOffset = 0; } return glMapBufferRange(GL_ARRAY_BUFFER, nextOffset, size, access); } //********************************* // GLVertexBuffer //********************************* QSize GLVertexBuffer::s_virtualScreenSize; GLVertexBuffer::GLVertexBuffer(UsageHint hint) : d(new GLVertexBufferPrivate(hint)) { } GLVertexBuffer::~GLVertexBuffer() { delete d; } void GLVertexBuffer::setData(const void *data, size_t size) { GLvoid *ptr = map(size); memcpy(ptr, data, size); unmap(); } void GLVertexBuffer::setData(int vertexCount, int dim, const float* vertices, const float* texcoords) { const GLVertexAttrib layout[] = { { VA_Position, dim, GL_FLOAT, 0 }, { VA_TexCoord, 2, GL_FLOAT, int(dim * sizeof(float)) } }; int stride = (texcoords ? dim + 2 : dim) * sizeof(float); int attribCount = texcoords ? 2 : 1; setAttribLayout(layout, attribCount, stride); setVertexCount(vertexCount); GLvoid *ptr = map(vertexCount * stride); d->interleaveArrays((float *) ptr, dim, vertices, texcoords, vertexCount); unmap(); } GLvoid *GLVertexBuffer::map(size_t size) { d->mappedSize = size; d->frameSize += size; if (d->persistent) return d->getIdleRange(size); glBindBuffer(GL_ARRAY_BUFFER, d->buffer); bool preferBufferSubData = GLPlatform::instance()->preferBufferSubData(); if (GLVertexBufferPrivate::hasMapBufferRange && !preferBufferSubData) return (GLvoid *) d->mapNextFreeRange(size); // If we can't map the buffer we allocate local memory to hold the // buffer data and return a pointer to it. The data will be submitted // to the actual buffer object when the user calls unmap(). if (size_t(d->dataStore.size()) < size) d->dataStore.resize(size); return (GLvoid *) d->dataStore.data(); } void GLVertexBuffer::unmap() { if (d->persistent) { d->baseAddress = d->nextOffset; d->nextOffset += align(d->mappedSize, 16); // Align to 16 bytes for SSE d->mappedSize = 0; return; } bool preferBufferSubData = GLPlatform::instance()->preferBufferSubData(); if (GLVertexBufferPrivate::hasMapBufferRange && !preferBufferSubData) { glUnmapBuffer(GL_ARRAY_BUFFER); d->baseAddress = d->nextOffset; d->nextOffset += align(d->mappedSize, 16); // Align to 16 bytes for SSE } else { // Upload the data from local memory to the buffer object if (preferBufferSubData) { if ((d->nextOffset + d->mappedSize) > d->bufferSize) { d->reallocateBuffer(d->mappedSize); d->nextOffset = 0; } glBufferSubData(GL_ARRAY_BUFFER, d->nextOffset, d->mappedSize, d->dataStore.constData()); d->baseAddress = d->nextOffset; d->nextOffset += align(d->mappedSize, 16); // Align to 16 bytes for SSE } else { glBufferData(GL_ARRAY_BUFFER, d->mappedSize, d->dataStore.data(), d->usage); d->baseAddress = 0; } // Free the local memory buffer if it's unlikely to be used again if (d->usage == GL_STATIC_DRAW) d->dataStore = QByteArray(); } d->mappedSize = 0; } void GLVertexBuffer::setVertexCount(int count) { d->vertexCount = count; } void GLVertexBuffer::setAttribLayout(const GLVertexAttrib *attribs, int count, int stride) { // Start by disabling all arrays d->enabledArrays = 0; for (int i = 0; i < count; i++) { const int index = attribs[i].index; assert(index >= 0 && index < VertexAttributeCount); assert(!d->enabledArrays[index]); d->attrib[index].size = attribs[i].size; d->attrib[index].type = attribs[i].type; d->attrib[index].offset = attribs[i].relativeOffset; d->enabledArrays[index] = true; } d->stride = stride; } void GLVertexBuffer::render(GLenum primitiveMode) { render(infiniteRegion(), primitiveMode, false); } void GLVertexBuffer::render(const QRegion& region, GLenum primitiveMode, bool hardwareClipping) { d->bindArrays(); draw(region, primitiveMode, 0, d->vertexCount, hardwareClipping); d->unbindArrays(); } void GLVertexBuffer::bindArrays() { d->bindArrays(); } void GLVertexBuffer::unbindArrays() { d->unbindArrays(); } void GLVertexBuffer::draw(GLenum primitiveMode, int first, int count) { draw(infiniteRegion(), primitiveMode, first, count, false); } void GLVertexBuffer::draw(const QRegion ®ion, GLenum primitiveMode, int first, int count, bool hardwareClipping) { if (primitiveMode == GL_QUADS) { IndexBuffer *&indexBuffer = GLVertexBufferPrivate::s_indexBuffer; if (!indexBuffer) indexBuffer = new IndexBuffer; indexBuffer->bind(); indexBuffer->accomodate(count / 4); count = count * 6 / 4; if (!hardwareClipping) { glDrawElementsBaseVertex(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, nullptr, first); } else { // Clip using scissoring foreach (const QRect &r, region.rects()) { glScissor(r.x(), s_virtualScreenSize.height() - r.y() - r.height(), r.width(), r.height()); glDrawElementsBaseVertex(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, nullptr, first); } } return; } if (!hardwareClipping) { glDrawArrays(primitiveMode, first, count); } else { // Clip using scissoring foreach (const QRect &r, region.rects()) { glScissor(r.x(), s_virtualScreenSize.height() - r.y() - r.height(), r.width(), r.height()); glDrawArrays(primitiveMode, first, count); } } } bool GLVertexBuffer::supportsIndexedQuads() { return GLVertexBufferPrivate::supportsIndexedQuads; } bool GLVertexBuffer::isUseColor() const { return d->useColor; } void GLVertexBuffer::setUseColor(bool enable) { d->useColor = enable; } void GLVertexBuffer::setColor(const QColor& color, bool enable) { d->useColor = enable; d->color = QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } void GLVertexBuffer::reset() { d->useColor = false; d->color = QVector4D(0, 0, 0, 1); d->vertexCount = 0; } void GLVertexBuffer::endOfFrame() { if (!d->persistent) return; // Emit a fence if we have uploaded data if (d->frameSize > 0) { d->frameSizes.push(d->frameSize); d->frameSize = 0; // Force the buffer to be reallocated at the beginning of the next frame // if the average frame size is greater than half the size of the buffer if (unlikely(d->frameSizes.average() > d->bufferSize / 2)) { deleteAll(d->fences); glDeleteBuffers(1, &d->buffer); d->buffer = 0; d->bufferSize = 0; d->nextOffset = 0; d->map = nullptr; } else { BufferFence fence; fence.sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); fence.nextEnd = d->nextOffset + d->bufferSize; d->fences.emplace_back(fence); } } } void GLVertexBuffer::framePosted() { if (!d->persistent) return; // Remove finished fences from the list and update the bufferEnd offset while (d->fences.size() > 1 && d->fences.front().signaled()) { const BufferFence &fence = d->fences.front(); glDeleteSync(fence.sync); d->bufferEnd = fence.nextEnd; d->fences.pop_front(); } } void GLVertexBuffer::initStatic() { if (GLPlatform::instance()->isGLES()) { bool haveBaseVertex = hasGLExtension(QByteArrayLiteral("GL_OES_draw_elements_base_vertex")); bool haveCopyBuffer = hasGLVersion(3, 0); bool haveMapBufferRange = hasGLExtension(QByteArrayLiteral("GL_EXT_map_buffer_range")); GLVertexBufferPrivate::hasMapBufferRange = haveMapBufferRange; GLVertexBufferPrivate::supportsIndexedQuads = haveBaseVertex && haveCopyBuffer && haveMapBufferRange; GLVertexBufferPrivate::haveBufferStorage = hasGLExtension("GL_EXT_buffer_storage"); GLVertexBufferPrivate::haveSyncFences = hasGLVersion(3, 0); } else { bool haveBaseVertex = hasGLVersion(3, 2) || hasGLExtension(QByteArrayLiteral("GL_ARB_draw_elements_base_vertex")); bool haveCopyBuffer = hasGLVersion(3, 1) || hasGLExtension(QByteArrayLiteral("GL_ARB_copy_buffer")); bool haveMapBufferRange = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_map_buffer_range")); GLVertexBufferPrivate::hasMapBufferRange = haveMapBufferRange; GLVertexBufferPrivate::supportsIndexedQuads = haveBaseVertex && haveCopyBuffer && haveMapBufferRange; GLVertexBufferPrivate::haveBufferStorage = hasGLVersion(4, 4) || hasGLExtension("GL_ARB_buffer_storage"); GLVertexBufferPrivate::haveSyncFences = hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync"); } GLVertexBufferPrivate::s_indexBuffer = nullptr; GLVertexBufferPrivate::streamingBuffer = new GLVertexBuffer(GLVertexBuffer::Stream); if (GLVertexBufferPrivate::haveBufferStorage && GLVertexBufferPrivate::haveSyncFences) { if (qgetenv("KWIN_PERSISTENT_VBO") != QByteArrayLiteral("0")) { GLVertexBufferPrivate::streamingBuffer->d->persistent = true; } } } void GLVertexBuffer::cleanup() { delete GLVertexBufferPrivate::s_indexBuffer; GLVertexBufferPrivate::s_indexBuffer = nullptr; GLVertexBufferPrivate::hasMapBufferRange = false; GLVertexBufferPrivate::supportsIndexedQuads = false; delete GLVertexBufferPrivate::streamingBuffer; GLVertexBufferPrivate::streamingBuffer = nullptr; } GLVertexBuffer *GLVertexBuffer::streamingBuffer() { return GLVertexBufferPrivate::streamingBuffer; } } // namespace