2513 lines
84 KiB
C++
2513 lines
84 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>
|
|
Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
|
|
#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 <QPixmap>
|
|
#include <QImage>
|
|
#include <QHash>
|
|
#include <QFile>
|
|
#include <QVector2D>
|
|
#include <QVector3D>
|
|
#include <QVector4D>
|
|
#include <QMatrix4x4>
|
|
#include <QVarLengthArray>
|
|
|
|
#include <array>
|
|
#include <deque>
|
|
|
|
#include <math.h>
|
|
|
|
#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<QByteArray> glExtensions;
|
|
static QList<QByteArray> glxExtensions;
|
|
static QList<QByteArray> eglExtensions;
|
|
|
|
int glTextureUnitsCount;
|
|
|
|
|
|
// Functions
|
|
void initGLX()
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
// 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());
|
|
glxExtensions = string.split(' ');
|
|
glxResolveFunctions();
|
|
#endif
|
|
}
|
|
|
|
void initEGL()
|
|
{
|
|
EGLDisplay dpy = eglGetCurrentDisplay();
|
|
int major, minor;
|
|
eglInitialize(dpy, &major, &minor);
|
|
eglVersion = MAKE_GL_VERSION(major, minor, 0);
|
|
const QByteArray string = eglQueryString(dpy, EGL_EXTENSIONS);
|
|
eglExtensions = string.split(' ');
|
|
eglResolveFunctions();
|
|
}
|
|
|
|
void initGL(OpenGLPlatformInterface platformInterface)
|
|
{
|
|
// Get OpenGL version
|
|
QString glversionstring = QString::fromUtf8((const char*)glGetString(GL_VERSION));
|
|
if (glversionstring.startsWith(QLatin1String("OpenGL ES "))) {
|
|
glversionstring = glversionstring.mid(10);
|
|
}
|
|
QStringList glversioninfo = glversionstring.left(glversionstring.indexOf(QStringLiteral(" "))).split(QStringLiteral("."));
|
|
while (glversioninfo.count() < 3)
|
|
glversioninfo << QStringLiteral("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();
|
|
glxExtensions.clear();
|
|
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) || glxExtensions.contains(extension) || eglExtensions.contains(extension);
|
|
}
|
|
|
|
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");
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
case GL_STACK_OVERFLOW: return QStringLiteral("GL_STACK_OVERFLOW");
|
|
case GL_STACK_UNDERFLOW: return QStringLiteral("GL_STACK_UNDERFLOW");
|
|
#endif
|
|
case GL_OUT_OF_MEMORY: return QStringLiteral("GL_OUT_OF_MEMORY");
|
|
default: return QStringLiteral("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;
|
|
}
|
|
|
|
int nearestPowerOfTwo(int x)
|
|
{
|
|
// This method had been copied from Qt's nearest_gl_texture_size()
|
|
int n = 0, last = 0;
|
|
for (int s = 0; s < 32; ++s) {
|
|
if (((x >> s) & 1) == 1) {
|
|
++n;
|
|
last = s;
|
|
}
|
|
}
|
|
if (n > 1)
|
|
return 1 << (last + 1);
|
|
return 1 << last;
|
|
}
|
|
|
|
//****************************************
|
|
// 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;
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
if (GLPlatform::instance()->glslVersion() < kVersionNumber(3, 0)) {
|
|
ba.append("precision highp float;\n");
|
|
}
|
|
#endif
|
|
if (ShaderManager::instance()->isShaderDebug()) {
|
|
ba.append("#define KWIN_SHADER_DEBUG 1\n");
|
|
}
|
|
ba.append(source);
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
if (GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0)) {
|
|
ba.replace("#version 140", "#version 300 es\n\nprecision highp float;\n");
|
|
}
|
|
#endif
|
|
|
|
// 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)
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
// Make sure shaders are actually supported
|
|
if (!GLPlatform::instance()->supports(GLSL) || GLPlatform::instance()->supports(LimitedNPOT)) {
|
|
qCCritical(LIBKWINGLUTILS) << "Shaders are not supported";
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
if (hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_EXT_gpu_shader4")))
|
|
glBindFragDataLocation(mProgram, index, name);
|
|
#else
|
|
Q_UNUSED(name)
|
|
Q_UNUSED(index)
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
QSize ShaderManager::s_virtualScreenSize;
|
|
|
|
ShaderManager *ShaderManager::instance()
|
|
{
|
|
if (!s_shaderManager) {
|
|
s_shaderManager = new ShaderManager();
|
|
s_shaderManager->initShaders();
|
|
s_shaderManager->m_inited = true;
|
|
}
|
|
return s_shaderManager;
|
|
}
|
|
|
|
void ShaderManager::cleanup()
|
|
{
|
|
delete s_shaderManager;
|
|
s_shaderManager = nullptr;
|
|
}
|
|
|
|
ShaderManager::ShaderManager()
|
|
: m_inited(false)
|
|
, m_valid(false)
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
m_shader[i] = 0;
|
|
|
|
m_debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
|
|
}
|
|
|
|
ShaderManager::~ShaderManager()
|
|
{
|
|
while (!m_boundShaders.isEmpty()) {
|
|
popShader();
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
delete m_shader[i];
|
|
|
|
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)
|
|
{
|
|
const QByteArray vertex = generateVertexSource(traits);
|
|
const QByteArray fragment = generateFragmentSource(traits);
|
|
|
|
#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::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::isValid() const
|
|
{
|
|
return m_valid;
|
|
}
|
|
|
|
bool ShaderManager::isShaderDebug() const
|
|
{
|
|
return m_debug;
|
|
}
|
|
|
|
GLShader *ShaderManager::pushShader(ShaderTraits traits)
|
|
{
|
|
GLShader *shader = this->shader(traits);
|
|
pushShader(shader);
|
|
return shader;
|
|
}
|
|
|
|
GLShader *ShaderManager::pushShader(ShaderType type, bool reset)
|
|
{
|
|
if (m_inited && !m_valid) {
|
|
return nullptr;
|
|
}
|
|
|
|
pushShader(m_shader[type]);
|
|
if (reset) {
|
|
resetShader(type);
|
|
}
|
|
|
|
return m_shader[type];
|
|
}
|
|
|
|
void ShaderManager::resetAllShaders()
|
|
{
|
|
if (!m_inited || !m_valid) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
pushShader(ShaderType(i), true);
|
|
popShader();
|
|
}
|
|
}
|
|
|
|
void ShaderManager::resetShader(GLShader *shader, ShaderType type)
|
|
{
|
|
if (!(shader && shader->isValid()))
|
|
return;
|
|
|
|
pushShader(shader);
|
|
resetShader(type);
|
|
popShader();
|
|
}
|
|
|
|
|
|
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::loadFragmentShader(ShaderType vertex, const QString &fragmentFile)
|
|
{
|
|
const char *vertexFile[] = {
|
|
"scene-vertex.glsl",
|
|
"scene-generic-vertex.glsl",
|
|
"scene-color-vertex.glsl"
|
|
};
|
|
|
|
GLShader *shader = new GLShader(QString::fromUtf8(m_shaderDir + vertexFile[vertex]), fragmentFile, GLShader::ExplicitLinking);
|
|
bindAttributeLocations(shader);
|
|
bindFragDataLocations(shader);
|
|
shader->link();
|
|
|
|
if (shader->isValid()) {
|
|
pushShader(shader);
|
|
resetShader(vertex);
|
|
popShader();
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
GLShader *ShaderManager::loadVertexShader(ShaderType fragment, const QString &vertexFile)
|
|
{
|
|
// The Simple and Generic shaders use same fragment shader
|
|
const char *fragmentFile[] = {
|
|
"scene-fragment.glsl",
|
|
"scene-fragment.glsl",
|
|
"scene-color-fragment.glsl"
|
|
};
|
|
|
|
GLShader *shader = new GLShader(vertexFile, QString::fromUtf8(m_shaderDir + fragmentFile[fragment]), GLShader::ExplicitLinking);
|
|
bindAttributeLocations(shader);
|
|
bindFragDataLocations(shader);
|
|
shader->link();
|
|
|
|
if (shader->isValid()) {
|
|
pushShader(shader);
|
|
resetShader(fragment);
|
|
popShader();
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void ShaderManager::initShaders()
|
|
{
|
|
const char *vertexFile[] = {
|
|
"scene-vertex.glsl",
|
|
"scene-generic-vertex.glsl",
|
|
"scene-color-vertex.glsl",
|
|
};
|
|
|
|
const char *fragmentFile[] = {
|
|
"scene-fragment.glsl",
|
|
"scene-fragment.glsl",
|
|
"scene-color-fragment.glsl",
|
|
};
|
|
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
const qint64 coreVersionNumber = kVersionNumber(3, 0);
|
|
#else
|
|
const qint64 coreVersionNumber = kVersionNumber(1, 40);
|
|
#endif
|
|
if (GLPlatform::instance()->glslVersion() >= coreVersionNumber)
|
|
m_shaderDir = ":/resources/shaders/1.40/";
|
|
else
|
|
m_shaderDir = ":/resources/shaders/1.10/";
|
|
|
|
// Be optimistic
|
|
m_valid = true;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
m_shader[i] = new GLShader(QString::fromUtf8(m_shaderDir + vertexFile[i]),
|
|
QString::fromUtf8(m_shaderDir + fragmentFile[i]),
|
|
GLShader::ExplicitLinking);
|
|
bindAttributeLocations(m_shader[i]);
|
|
bindFragDataLocations(m_shader[i]);
|
|
m_shader[i]->link();
|
|
|
|
if (!m_shader[i]->isValid()) {
|
|
m_valid = false;
|
|
break;
|
|
}
|
|
|
|
pushShader(m_shader[i]);
|
|
resetShader(ShaderType(i));
|
|
popShader();
|
|
}
|
|
|
|
if (!m_valid) {
|
|
for (int i = 0; i < 3; i++) {
|
|
delete m_shader[i];
|
|
m_shader[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderManager::resetShader(ShaderType type)
|
|
{
|
|
// resetShader is either called from init or from push, we know that a built-in shader is bound
|
|
const QMatrix4x4 identity;
|
|
|
|
QMatrix4x4 projection;
|
|
QMatrix4x4 modelView;
|
|
|
|
GLShader *shader = getBoundShader();
|
|
|
|
switch(type) {
|
|
case SimpleShader:
|
|
projection.ortho(0, s_virtualScreenSize.width(), s_virtualScreenSize.height(), 0, 0, 65535);
|
|
break;
|
|
|
|
case GenericShader: {
|
|
// Set up the projection matrix
|
|
float fovy = 60.0f;
|
|
float aspect = 1.0f;
|
|
float zNear = 0.1f;
|
|
float zFar = 100.0f;
|
|
float ymax = zNear * tan(fovy * M_PI / 360.0f);
|
|
float ymin = -ymax;
|
|
float xmin = ymin * aspect;
|
|
float xmax = ymax * aspect;
|
|
projection.frustum(xmin, xmax, ymin, ymax, zNear, zFar);
|
|
|
|
// Set up the model-view matrix
|
|
float scaleFactor = 1.1 * tan(fovy * M_PI / 360.0f) / ymax;
|
|
modelView.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1);
|
|
modelView.scale((xmax - xmin)*scaleFactor / s_virtualScreenSize.width(), -(ymax - ymin)*scaleFactor / s_virtualScreenSize.height(), 0.001);
|
|
break;
|
|
}
|
|
|
|
case ColorShader:
|
|
projection.ortho(0, s_virtualScreenSize.width(), s_virtualScreenSize.height(), 0, 0, 65535);
|
|
shader->setUniform("geometryColor", QVector4D(0, 0, 0, 1));
|
|
break;
|
|
}
|
|
|
|
shader->setUniform("sampler", 0);
|
|
|
|
shader->setUniform(GLShader::ProjectionMatrix, projection);
|
|
shader->setUniform(GLShader::ModelViewMatrix, modelView);
|
|
shader->setUniform(GLShader::ScreenTransformation, identity);
|
|
shader->setUniform(GLShader::WindowTransformation, identity);
|
|
|
|
shader->setUniform(GLShader::Offset, QVector2D(0, 0));
|
|
shader->setUniform(GLShader::ModulationConstant, QVector4D(1.0, 1.0, 1.0, 1.0));
|
|
|
|
shader->setUniform(GLShader::Saturation, 1.0f);
|
|
}
|
|
|
|
/*** GLRenderTarget ***/
|
|
bool GLRenderTarget::sSupported = false;
|
|
bool GLRenderTarget::s_blitSupported = false;
|
|
QStack<GLRenderTarget*> GLRenderTarget::s_renderTargets = QStack<GLRenderTarget*>();
|
|
QSize GLRenderTarget::s_virtualScreenSize;
|
|
|
|
void GLRenderTarget::initStatic()
|
|
{
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
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"));
|
|
#endif
|
|
}
|
|
|
|
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");
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
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");
|
|
#endif
|
|
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();
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
|
|
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
|
|
};
|
|
|
|
#endif // KWIN_HAVE_OPENGLES
|
|
|
|
template <typename T>
|
|
T align(T value, int bytes)
|
|
{
|
|
return (value + bytes - 1) & ~T(bytes - 1);
|
|
}
|
|
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
|
|
// This class is not be used with OpenGL ES for now, since we need
|
|
// GL_ARB_draw_elements_base_vertex and GL_ARB_copy_buffer.
|
|
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);
|
|
}
|
|
|
|
#endif // KWIN_HAVE_OPENGLES
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
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<BufferFence> &fences)
|
|
{
|
|
for (const BufferFence &fence : fences)
|
|
glDeleteSync(fence.sync);
|
|
|
|
fences.clear();
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
|
|
template <size_t Count>
|
|
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<size_t, Count> 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<BufferFence> fences;
|
|
FrameSizesArray<4> frameSizes;
|
|
VertexAttrib attrib[VertexAttributeCount];
|
|
Bitfield enabledArrays;
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
static IndexBuffer *s_indexBuffer;
|
|
#endif
|
|
};
|
|
|
|
bool GLVertexBufferPrivate::hasMapBufferRange = false;
|
|
bool GLVertexBufferPrivate::supportsIndexedQuads = false;
|
|
GLVertexBuffer *GLVertexBufferPrivate::streamingBuffer = nullptr;
|
|
bool GLVertexBufferPrivate::haveBufferStorage = false;
|
|
bool GLVertexBufferPrivate::haveSyncFences = false;
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
IndexBuffer *GLVertexBufferPrivate::s_indexBuffer = nullptr;
|
|
#endif
|
|
|
|
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<size_t>(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)
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
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()
|
|
{
|
|
#ifdef KWIN_HAVE_OPENGLES
|
|
GLVertexBufferPrivate::hasMapBufferRange = hasGLExtension(QByteArrayLiteral("GL_EXT_map_buffer_range"));
|
|
GLVertexBufferPrivate::supportsIndexedQuads = false;
|
|
GLVertexBufferPrivate::haveBufferStorage = false;
|
|
GLVertexBufferPrivate::haveSyncFences = false;
|
|
#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::s_indexBuffer = nullptr;
|
|
GLVertexBufferPrivate::haveBufferStorage = hasGLVersion(4, 4) || hasGLExtension("GL_ARB_buffer_storage");
|
|
GLVertexBufferPrivate::haveSyncFences = hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync");
|
|
#endif
|
|
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()
|
|
{
|
|
#ifndef KWIN_HAVE_OPENGLES
|
|
delete GLVertexBufferPrivate::s_indexBuffer;
|
|
GLVertexBufferPrivate::s_indexBuffer = nullptr;
|
|
#endif
|
|
GLVertexBufferPrivate::hasMapBufferRange = false;
|
|
GLVertexBufferPrivate::supportsIndexedQuads = false;
|
|
delete GLVertexBufferPrivate::streamingBuffer;
|
|
GLVertexBufferPrivate::streamingBuffer = nullptr;
|
|
}
|
|
|
|
GLVertexBuffer *GLVertexBuffer::streamingBuffer()
|
|
{
|
|
return GLVertexBufferPrivate::streamingBuffer;
|
|
}
|
|
|
|
} // namespace
|