kwin: Use one buffer object in GLVertexBuffer
Store the vertex positions and texture coordinates in the same buffer object. This saves one buffer allocation in every setData() call. The attributes are also interleaved as they are uploaded into the buffer to maximize locality of reference.
This commit is contained in:
parent
e65f717b47
commit
0475559120
1 changed files with 124 additions and 26 deletions
|
@ -1184,10 +1184,13 @@ public:
|
|||
, dimension(2)
|
||||
, useColor(false)
|
||||
, useTexCoords(true)
|
||||
, color(0, 0, 0, 255) {
|
||||
if (GLVertexBufferPrivate::supported) {
|
||||
glGenBuffers(2, buffers);
|
||||
}
|
||||
, color(0, 0, 0, 255)
|
||||
, bufferSize(0)
|
||||
, vertexAddress(0)
|
||||
, texCoordAddress(0)
|
||||
{
|
||||
if (GLVertexBufferPrivate::supported)
|
||||
glGenBuffers(1, &buffer);
|
||||
|
||||
switch(usageHint) {
|
||||
case GLVertexBuffer::Dynamic:
|
||||
|
@ -1203,22 +1206,29 @@ public:
|
|||
}
|
||||
|
||||
~GLVertexBufferPrivate() {
|
||||
if (GLVertexBufferPrivate::supported) {
|
||||
glDeleteBuffers(2, buffers);
|
||||
}
|
||||
if (GLVertexBufferPrivate::supported)
|
||||
glDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
GLuint buffers[2];
|
||||
void interleaveArrays(float *array, int dim, const float *vertices, const float *texcoords, int count);
|
||||
GLvoid *mapBufferRange(size_t size);
|
||||
|
||||
GLuint buffer;
|
||||
GLenum usage;
|
||||
int vertexCount;
|
||||
int dimension;
|
||||
int stride;
|
||||
static bool supported;
|
||||
static GLVertexBuffer *streamingBuffer;
|
||||
static bool hasMapBufferRange;
|
||||
QVector<float> legacyVertices;
|
||||
QVector<float> legacyTexCoords;
|
||||
bool useColor;
|
||||
bool useTexCoords;
|
||||
QColor color;
|
||||
size_t bufferSize;
|
||||
intptr_t vertexAddress;
|
||||
intptr_t texCoordAddress;
|
||||
|
||||
//! VBO is not supported
|
||||
void legacyPainting(QRegion region, GLenum primitiveMode, bool hardwareClipping);
|
||||
|
@ -1227,9 +1237,52 @@ public:
|
|||
//! VBO is supported, but shaders are not supported
|
||||
void fallbackPainting(const QRegion& region, GLenum primitiveMode, bool hardwareClipping);
|
||||
};
|
||||
|
||||
bool GLVertexBufferPrivate::supported = false;
|
||||
bool GLVertexBufferPrivate::hasMapBufferRange = false;
|
||||
GLVertexBuffer *GLVertexBufferPrivate::streamingBuffer = NULL;
|
||||
|
||||
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::legacyPainting(QRegion region, GLenum primitiveMode, bool hardwareClipping)
|
||||
{
|
||||
#ifndef KWIN_HAVE_OPENGL_1
|
||||
|
@ -1267,25 +1320,25 @@ void GLVertexBufferPrivate::legacyPainting(QRegion region, GLenum primitiveMode,
|
|||
|
||||
void GLVertexBufferPrivate::corePainting(const QRegion& region, GLenum primitiveMode, bool hardwareClipping)
|
||||
{
|
||||
// FIXME Use glBindAttribLocation() to bind "vertex" and "texCoord" to the same
|
||||
// attribute index in all shaders.
|
||||
GLShader *shader = ShaderManager::instance()->getBoundShader();
|
||||
GLint vertexAttrib = shader->attributeLocation("vertex");
|
||||
GLint texAttrib = shader->attributeLocation("texCoord");
|
||||
|
||||
glEnableVertexAttribArray(vertexAttrib);
|
||||
if (useTexCoords) {
|
||||
glEnableVertexAttribArray(texAttrib);
|
||||
}
|
||||
|
||||
if (useColor) {
|
||||
// FIXME Store the uniform location in the shader so we don't have to look it up every time.
|
||||
shader->setUniform("geometryColor", color);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[ 0 ]);
|
||||
glVertexAttribPointer(vertexAttrib, dimension, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
|
||||
glVertexAttribPointer(vertexAttrib, dimension, GL_FLOAT, GL_FALSE, stride, (const GLvoid *) vertexAddress);
|
||||
glEnableVertexAttribArray(vertexAttrib);
|
||||
|
||||
if (texAttrib != -1 && useTexCoords) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[ 1 ]);
|
||||
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, stride, (const GLvoid *) texCoordAddress);
|
||||
glEnableVertexAttribArray(texAttrib);
|
||||
}
|
||||
|
||||
if (!hardwareClipping) {
|
||||
|
@ -1314,11 +1367,10 @@ void GLVertexBufferPrivate::fallbackPainting(const QRegion& region, GLenum primi
|
|||
#else
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[ 0 ]);
|
||||
glVertexPointer(dimension, GL_FLOAT, 0, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffers[ 1 ]);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glVertexPointer(dimension, GL_FLOAT, stride, (const GLvoid *) vertexAddress);
|
||||
glTexCoordPointer(2, GL_FLOAT, stride, (const GLvoid *) texCoordAddress);
|
||||
|
||||
if (useColor) {
|
||||
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
|
||||
|
@ -1341,6 +1393,32 @@ void GLVertexBufferPrivate::fallbackPainting(const QRegion& region, GLenum primi
|
|||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T align(T value, int bytes)
|
||||
{
|
||||
return (value + bytes - 1) & ~T(bytes - 1);
|
||||
}
|
||||
|
||||
GLvoid *GLVertexBufferPrivate::mapBufferRange(size_t size)
|
||||
{
|
||||
GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT;
|
||||
|
||||
if (size > bufferSize) {
|
||||
// Reallocate the data store if it's too small.
|
||||
// Round the size up to 4 Kb for streaming/dynamic buffers.
|
||||
const size_t alloc = usage != GL_STATIC_DRAW ? align(size, 4096) : size;
|
||||
glBufferData(GL_ARRAY_BUFFER, alloc, 0, usage);
|
||||
bufferSize = alloc;
|
||||
} else {
|
||||
// Hint the GL that it may discard the whole resource if there is contention.
|
||||
// This will pretty much always be the case.
|
||||
access |= GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
}
|
||||
|
||||
return glMapBufferRange(GL_ARRAY_BUFFER, 0, size, access);
|
||||
}
|
||||
|
||||
|
||||
//*********************************
|
||||
// GLVertexBuffer
|
||||
//*********************************
|
||||
|
@ -1377,12 +1455,30 @@ void GLVertexBuffer::setData(int vertexCount, int dim, const float* vertices, co
|
|||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, d->buffers[ 0 ]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertexCount * d->dimension, vertices, d->usage);
|
||||
d->vertexAddress = 0;
|
||||
d->texCoordAddress = dim * sizeof(float);
|
||||
d->stride = (dim + (texcoords ? 2 : 0)) * sizeof(float);
|
||||
|
||||
if (d->useTexCoords) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, d->buffers[ 1 ]);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*vertexCount * 2, texcoords, d->usage);
|
||||
size_t size = vertexCount * d->stride;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, d->buffer);
|
||||
|
||||
if (d->hasMapBufferRange) {
|
||||
float *map = (float *) d->mapBufferRange(size);
|
||||
d->interleaveArrays(map, dim, vertices, texcoords, vertexCount);
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
} else {
|
||||
// We always reallocate the data store when we can't map the buffer.
|
||||
// The rationale is that there will almost always be contention
|
||||
// in a glBufferSubData() call.
|
||||
if (d->useTexCoords) {
|
||||
QByteArray array;
|
||||
array.resize(size);
|
||||
|
||||
d->interleaveArrays((float *) array.data(), dim, vertices, texcoords, vertexCount);
|
||||
glBufferData(GL_ARRAY_BUFFER, size, array.data(), d->usage);
|
||||
} else
|
||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, d->usage);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
@ -1438,8 +1534,10 @@ void GLVertexBuffer::initStatic()
|
|||
{
|
||||
#ifdef KWIN_HAVE_OPENGLES
|
||||
GLVertexBufferPrivate::supported = true;
|
||||
GLVertexBufferPrivate::hasMapBufferRange = hasGLExtension("GL_EXT_map_buffer_range");
|
||||
#else
|
||||
GLVertexBufferPrivate::supported = hasGLVersion(1, 5) || hasGLExtension("GL_ARB_vertex_buffer_object");
|
||||
GLVertexBufferPrivate::hasMapBufferRange = hasGLVersion(3, 0) || hasGLExtension("GL_ARB_map_buffer_range");
|
||||
#endif
|
||||
GLVertexBufferPrivate::streamingBuffer = new GLVertexBuffer(GLVertexBuffer::Stream);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue