summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Rødal <sroedal@trolltech.com>2009-10-06 13:38:54 (GMT)
committerSamuel Rødal <sroedal@trolltech.com>2009-10-06 13:38:55 (GMT)
commit6b623c04060d274c048c0d4c6dbc5a90d1c31604 (patch)
tree5cfbc3cd1c3520302d01e9c12a4355317da41b2a
parent6d17e09c274803d324e8a8db579aaafaefaab33f (diff)
downloadQt-6b623c04060d274c048c0d4c6dbc5a90d1c31604.zip
Qt-6b623c04060d274c048c0d4c6dbc5a90d1c31604.tar.gz
Qt-6b623c04060d274c048c0d4c6dbc5a90d1c31604.tar.bz2
Switched to using stencil instead of depth buffer for clipping.
Based on Aaron Kennedy's patch. All tests are green, but when enabling scissoring UniteClip seems to be broken atm.
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp277
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h17
2 files changed, 173 insertions, 121 deletions
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 13f0079..0af8e71 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -87,6 +87,7 @@ QT_BEGIN_NAMESPACE
//#define QT_GL_NO_SCISSOR_TEST
+static const GLuint GL_STENCIL_HIGH_BIT = 0x80;
static const GLuint QT_BRUSH_TEXTURE_UNIT = 0;
static const GLuint QT_IMAGE_TEXTURE_UNIT = 0; //Can be the same as brush texture unit
static const GLuint QT_MASK_TEXTURE_UNIT = 1;
@@ -227,6 +228,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height)
glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_SCISSOR_BIT);
#endif
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
@@ -402,11 +404,6 @@ void QGL2PaintEngineExPrivate::useSimpleShader()
shaderManager->simpleProgram()->setUniformValue("pmvMatrix", pmvMatrix);
simpleShaderMatrixUniformDirty = false;
}
-
- if (simpleShaderDepthUniformDirty) {
- shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(q->state()->currentClip));
- simpleShaderDepthUniformDirty = false;
- }
}
void QGL2PaintEngineExPrivate::updateBrushTexture()
@@ -771,6 +768,7 @@ void QGL2PaintEngineExPrivate::resetGLState()
{
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDepthMask(true);
@@ -895,10 +893,10 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
// Stencil the brush onto the dest buffer
- glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); // Pass if stencil buff value != 0
+ glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
- glEnable(GL_STENCIL_TEST);
prepareForDraw(currentBrush->isOpaque());
#ifndef QT_OPENGL_ES_2
@@ -906,58 +904,137 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path)
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText());
#endif
composite(vertexCoordinateArray.boundingRect());
- glDisable(GL_STENCIL_TEST);
-
glStencilMask(0);
+
+ updateClipScissorTest();
}
}
void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill)
{
+#ifndef QT_OPENGL_ES_2
+ if (inRenderText) {
+ glPushAttrib(GL_ENABLE_BIT);
+ glDisable(GL_DEPTH_TEST);
+ }
+#endif
+
// qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()");
- glStencilMask(0xFFFF); // Enable stencil writes
+ glStencilMask(0xffff); // Enable stencil writes
if (dirtyStencilRegion.intersects(currentScissorBounds)) {
- // Clear the stencil buffer to zeros
- glDisable(GL_STENCIL_TEST);
+ QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects();
glClearStencil(0); // Clear to zero
- glClear(GL_STENCIL_BUFFER_BIT);
+ for (int i = 0; i < clearRegion.size(); ++i) {
+#ifndef QT_GL_NO_SCISSOR_TEST
+ setScissor(clearRegion.at(i));
+#endif
+ glClear(GL_STENCIL_BUFFER_BIT);
+ }
+
dirtyStencilRegion -= currentScissorBounds;
+
+#ifndef QT_GL_NO_SCISSOR_TEST
+ updateClipScissorTest();
+#endif
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes
- glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test
+ useSimpleShader();
+ glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
- // Setup the stencil op:
if (useWindingFill) {
- glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); // Inc. for front-facing triangle
- glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); //Dec. for back-facing "holes"
- } else
- glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
+ if (q->state()->clipTestEnabled) {
+ glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ composite(vertexArray.boundingRect());
- // No point in using a fancy gradient shader for writing into the stencil buffer!
- useSimpleShader();
-
- glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d
- glDisable(GL_BLEND);
+ glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT);
+ } else {
+ glStencilFunc(GL_ALWAYS, 0, 0xffff);
+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
+ composite(vertexArray.boundingRect());
+ }
-#ifndef QT_OPENGL_ES_2
- if (inRenderText) {
- glPushAttrib(GL_ENABLE_BIT);
- glDisable(GL_DEPTH_TEST);
+ // Inc. for front-facing triangle
+ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP);
+ //Dec. for back-facing "holes"
+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP);
+ glStencilMask(~GL_STENCIL_HIGH_BIT);
+ drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+
+ if (q->state()->clipTestEnabled) {
+ // clear high bit of stencil outside of path
+ glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ composite(vertexArray.boundingRect());
+ // reset lower bits of stencil inside path to current clip
+ glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ glStencilMask(~GL_STENCIL_HIGH_BIT);
+ composite(vertexArray.boundingRect());
+ } else {
+ // set high bit of stencil inside path
+ glStencilFunc(GL_NOTEQUAL, 0, 0xffff);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ composite(vertexArray.boundingRect());
+ }
+ } else {
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit
+ drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
}
-#endif
- // Draw the vertecies into the stencil buffer:
- drawVertexArrays(vertexArray, GL_TRIANGLE_FAN);
+ // Enable color writes & disable stencil writes
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
#ifndef QT_OPENGL_ES_2
if (inRenderText)
glPopAttrib();
#endif
- // Enable color writes & disable stencil writes
+}
+
+/*
+ If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1,
+ restore the stencil buffer to a pristine state. The current clip region
+ is set to 1, and the rest to 0.
+*/
+void QGL2PaintEngineExPrivate::resetClipIfNeeded()
+{
+ if (maxClip != (GL_STENCIL_HIGH_BIT - 1))
+ return;
+
+ Q_Q(QGL2PaintEngineEx);
+
+ useSimpleShader();
+ glEnable(GL_STENCIL_TEST);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+ QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height));
+ QGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+
+ // Set high bit on clip region
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xffff);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(GL_STENCIL_HIGH_BIT);
+ composite(rect);
+
+ // Reset clipping to 1 and everything else to zero
+ glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE);
+ glStencilMask(0xFFFF);
+ composite(rect);
+
+ q->state()->currentClip = 1;
+ q->state()->canRestoreClip = false;
+
+ maxClip = 1;
+
+ glStencilMask(0x0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
@@ -1005,7 +1082,6 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
// The shader program has changed so mark all uniforms as dirty:
brushUniformsDirty = true;
shaderMatrixUniformDirty = true;
- depthUniformDirty = true;
opacityUniformDirty = true;
}
@@ -1017,11 +1093,6 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque)
shaderMatrixUniformDirty = false;
}
- if (depthUniformDirty) {
- shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), normalizedDeviceDepth(q->state()->currentClip));
- depthUniformDirty = false;
- }
-
if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) {
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity);
opacityUniformDirty = false;
@@ -1542,8 +1613,6 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
d->brushUniformsDirty = true;
d->matrixDirty = true;
d->compositionModeDirty = true;
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
d->opacityUniformDirty = true;
d->needsSync = true;
d->use_system_clip = !systemClip().isEmpty();
@@ -1564,6 +1633,7 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
d->shaderManager = new QGLEngineShaderManager(d->ctx);
if (!d->inRenderText) {
+ glDisable(GL_STENCIL_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
}
@@ -1637,10 +1707,13 @@ void QGL2PaintEngineEx::ensureActive()
void QGL2PaintEngineExPrivate::updateClipScissorTest()
{
Q_Q(QGL2PaintEngineEx);
- if (q->state()->clipTestEnabled)
- glEnable(GL_DEPTH_TEST);
- else
- glDisable(GL_DEPTH_TEST);
+ if (q->state()->clipTestEnabled) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ glStencilFunc(GL_ALWAYS, 0, 0xffff);
+ }
#ifdef QT_GL_NO_SCISSOR_TEST
currentScissorBounds = QRect(0, 0, width, height);
@@ -1683,28 +1756,20 @@ void QGL2PaintEngineEx::clipEnabledChanged()
{
Q_D(QGL2PaintEngineEx);
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
-
- if (painter()->hasClipping()) {
+ if (painter()->hasClipping())
d->regenerateClip();
- } else {
- if (d->use_system_clip) {
- state()->currentClip = 1;
- } else {
- state()->clipTestEnabled = false;
- }
-
- d->updateClipScissorTest();
- }
+ else
+ d->systemStateChanged();
}
void QGL2PaintEngineExPrivate::clearClip(uint value)
{
- glDepthMask(true);
- glClearDepth(rawDepth(value));
- glClear(GL_DEPTH_BUFFER_BIT);
- glDepthMask(false);
+ dirtyStencilRegion -= currentScissorBounds;
+
+ glStencilMask(0xffff);
+ glClearStencil(value);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glStencilMask(0x0);
q->state()->needsClipBufferClear = false;
}
@@ -1715,48 +1780,57 @@ void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value)
if (matrixDirty)
updateMatrix();
+
+ const bool singlePass = !path.hasWindingFill()
+ && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled)
+ || q->state()->needsClipBufferClear);
+ const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip;
+
if (q->state()->needsClipBufferClear)
clearClip(1);
- if (path.isEmpty())
+ if (path.isEmpty()) {
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
return;
+ }
- glDisable(GL_BLEND);
- glDepthMask(false);
+ if (q->state()->clipTestEnabled)
+ glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT);
+ else
+ glStencilFunc(GL_ALWAYS, 0, 0xffff);
vertexCoordinateArray.clear();
vertexCoordinateArray.addPath(path, inverseScale);
- if (q->state()->clipTestEnabled)
- glDepthFunc(GL_LEQUAL);
- else
- glDepthFunc(GL_ALWAYS);
-
- glDepthMask(GL_FALSE);
- fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
+ if (!singlePass)
+ fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill());
- // Stencil the clip onto the clip buffer
glColorMask(false, false, false, false);
- glDepthMask(true);
+ glEnable(GL_STENCIL_TEST);
+ useSimpleShader();
- shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(value));
- simpleShaderDepthUniformDirty = true;
+ if (singlePass) {
+ // Under these conditions we can set the new stencil value in a single
+ // pass, by using the current value and the "new value" as the toggles
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_ALWAYS);
+ glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT);
+ glStencilMask(value ^ referenceClipValue);
- glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0
- glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN);
+ } else {
+ glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
+ glStencilMask(0xffff);
- glEnable(GL_STENCIL_TEST);
- composite(vertexCoordinateArray.boundingRect());
- glDisable(GL_STENCIL_TEST);
+ composite(vertexCoordinateArray.boundingRect());
+ }
- glDepthFunc(GL_LEQUAL);
+ glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT);
glStencilMask(0);
glColorMask(true, true, true, true);
- glDepthMask(false);
}
void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
@@ -1804,12 +1878,14 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
case Qt::IntersectClip:
state()->rectangleClip = state()->rectangleClip.intersected(pathRect);
d->updateClipScissorTest();
+ d->resetClipIfNeeded();
++d->maxClip;
d->writeClip(path, d->maxClip);
state()->currentClip = d->maxClip;
state()->clipTestEnabled = true;
break;
case Qt::UniteClip: {
+ d->resetClipIfNeeded();
++d->maxClip;
if (state()->rectangleClip.isValid()) {
QPainterPath path;
@@ -1846,11 +1922,6 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op)
default:
break;
}
-
- if (state()->clipTestEnabled) {
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
- }
}
void QGL2PaintEngineExPrivate::regenerateClip()
@@ -1883,29 +1954,25 @@ void QGL2PaintEngineExPrivate::systemStateChanged()
q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height);
updateClipScissorTest();
- if (use_system_clip) {
+ if (systemClip.numRects() == 1) {
+ if (systemClip.boundingRect() == QRect(0, 0, width, height))
+ use_system_clip = false;
#ifndef QT_GL_NO_SCISSOR_TEST
- if (systemClip.numRects() == 1) {
- if (q->state()->rectangleClip == QRect(0, 0, width, height)) {
- use_system_clip = false;
- } else {
- simpleShaderDepthUniformDirty = true;
- depthUniformDirty = true;
- }
- return;
- }
+ // scissoring takes care of the system clip
+ return;
#endif
+ }
+
+ if (use_system_clip) {
clearClip(0);
QPainterPath path;
path.addRegion(systemClip);
+ q->state()->currentClip = 0;
writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1);
-
+ q->state()->currentClip = 1;
q->state()->clipTestEnabled = true;
-
- simpleShaderDepthUniformDirty = true;
- depthUniformDirty = true;
}
}
@@ -1929,8 +1996,6 @@ void QGL2PaintEngineEx::setState(QPainterState *new_state)
d->matrixDirty = true;
d->compositionModeDirty = true;
- d->simpleShaderDepthUniformDirty = true;
- d->depthUniformDirty = true;
d->simpleShaderMatrixUniformDirty = true;
d->shaderMatrixUniformDirty = true;
d->opacityUniformDirty = true;
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index dd5f4fc..bfc6a3f 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -217,8 +217,6 @@ public:
bool brushUniformsDirty;
bool simpleShaderMatrixUniformDirty;
bool shaderMatrixUniformDirty;
- bool depthUniformDirty;
- bool simpleShaderDepthUniformDirty;
bool opacityUniformDirty;
QRegion dirtyStencilRegion;
@@ -242,25 +240,14 @@ public:
void clearClip(uint value);
void writeClip(const QVectorPath &path, uint value);
+ void resetClipIfNeeded();
+
void updateClipScissorTest();
void setScissor(const QRect &rect);
void regenerateClip();
void systemStateChanged();
uint use_system_clip : 1;
- static inline GLfloat rawDepth(uint depth)
- {
- // assume at least 16 bits in the depth buffer, and
- // use 2^15 depth levels to be safe with regard to
- // rounding issues etc
- return depth * (1.0f / GLfloat((1 << 15) - 1));
- }
-
- static inline GLfloat normalizedDeviceDepth(uint depth)
- {
- return 2.0f * rawDepth(depth) - 1.0f;
- }
-
uint location(QGLEngineShaderManager::Uniform uniform)
{
return shaderManager->getUniformLocation(uniform);