Changeset View
Changeset View
Standalone View
Standalone View
plugins/scenes/opengl/scene_opengl.cpp
Show First 20 Lines • Show All 1071 Lines • ▼ Show 20 Line(s) | 1070 | OpenGLWindow::OpenGLWindow(Toplevel *toplevel, SceneOpenGL *scene) | |||
---|---|---|---|---|---|
1072 | , m_scene(scene) | 1072 | , m_scene(scene) | ||
1073 | { | 1073 | { | ||
1074 | } | 1074 | } | ||
1075 | 1075 | | |||
1076 | OpenGLWindow::~OpenGLWindow() | 1076 | OpenGLWindow::~OpenGLWindow() | ||
1077 | { | 1077 | { | ||
1078 | } | 1078 | } | ||
1079 | 1079 | | |||
1080 | static SceneOpenGLTexture *s_frameTexture = nullptr; | | |||
1081 | // Bind the window pixmap to an OpenGL texture. | 1080 | // Bind the window pixmap to an OpenGL texture. | ||
1082 | bool OpenGLWindow::bindTexture() | 1081 | bool OpenGLWindow::bindTexture() | ||
1083 | { | 1082 | { | ||
1084 | s_frameTexture = nullptr; | | |||
1085 | OpenGLWindowPixmap *pixmap = windowPixmap<OpenGLWindowPixmap>(); | 1083 | OpenGLWindowPixmap *pixmap = windowPixmap<OpenGLWindowPixmap>(); | ||
1086 | if (!pixmap) { | 1084 | if (!pixmap) { | ||
1087 | return false; | 1085 | return false; | ||
1088 | } | 1086 | } | ||
1089 | s_frameTexture = pixmap->texture(); | | |||
1090 | if (pixmap->isDiscarded()) { | 1087 | if (pixmap->isDiscarded()) { | ||
1091 | return !pixmap->texture()->isNull(); | 1088 | return !pixmap->texture()->isNull(); | ||
1092 | } | 1089 | } | ||
1093 | 1090 | | |||
1094 | if (!window()->damage().isEmpty()) | 1091 | if (!window()->damage().isEmpty()) | ||
1095 | m_scene->insertWait(); | 1092 | m_scene->insertWait(); | ||
1096 | 1093 | | |||
1097 | return pixmap->bind(); | 1094 | return pixmap->bind(); | ||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Line(s) | 1133 | foreach (const WindowQuad &quad, data.quads) { | |||
1150 | } | 1147 | } | ||
1151 | } | 1148 | } | ||
1152 | data.quads = quads; | 1149 | data.quads = quads; | ||
1153 | } | 1150 | } | ||
1154 | 1151 | | |||
1155 | if (data.quads.isEmpty()) | 1152 | if (data.quads.isEmpty()) | ||
1156 | return false; | 1153 | return false; | ||
1157 | 1154 | | |||
1158 | if (!bindTexture() || !s_frameTexture) { | 1155 | if (!bindTexture()) { | ||
1159 | return false; | 1156 | return false; | ||
1160 | } | 1157 | } | ||
1161 | 1158 | | |||
1162 | if (m_hardwareClipping) { | 1159 | if (m_hardwareClipping) { | ||
1163 | glEnable(GL_SCISSOR_TEST); | 1160 | glEnable(GL_SCISSOR_TEST); | ||
1164 | } | 1161 | } | ||
1165 | 1162 | | |||
1166 | // Update the texture filter | | |||
1167 | if (waylandServer()) { | | |||
1168 | filter = Scene::ImageFilterGood; | | |||
1169 | s_frameTexture->setFilter(GL_LINEAR); | | |||
1170 | } else { | | |||
1171 | if (options->glSmoothScale() != 0 && | | |||
1172 | (mask & (Scene::PAINT_WINDOW_TRANSFORMED | Scene::PAINT_SCREEN_TRANSFORMED))) | | |||
1173 | filter = Scene::ImageFilterGood; | | |||
1174 | else | | |||
1175 | filter = Scene::ImageFilterFast; | | |||
1176 | | ||||
1177 | s_frameTexture->setFilter(filter == Scene::ImageFilterGood ? GL_LINEAR : GL_NEAREST); | | |||
1178 | } | | |||
1179 | | ||||
1180 | const GLVertexAttrib attribs[] = { | 1163 | const GLVertexAttrib attribs[] = { | ||
1181 | { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) }, | 1164 | { VA_Position, 2, GL_FLOAT, offsetof(GLVertex2D, position) }, | ||
1182 | { VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) }, | 1165 | { VA_TexCoord, 2, GL_FLOAT, offsetof(GLVertex2D, texcoord) }, | ||
1183 | }; | 1166 | }; | ||
1184 | 1167 | | |||
1185 | GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); | 1168 | GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); | ||
1186 | vbo->reset(); | 1169 | vbo->reset(); | ||
1187 | vbo->setAttribLayout(attribs, 2, sizeof(GLVertex2D)); | 1170 | vbo->setAttribLayout(attribs, 2, sizeof(GLVertex2D)); | ||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Line(s) | 1222 | { | |||
1240 | if (enabled && !m_blendingEnabled) | 1223 | if (enabled && !m_blendingEnabled) | ||
1241 | glEnable(GL_BLEND); | 1224 | glEnable(GL_BLEND); | ||
1242 | else if (!enabled && m_blendingEnabled) | 1225 | else if (!enabled && m_blendingEnabled) | ||
1243 | glDisable(GL_BLEND); | 1226 | glDisable(GL_BLEND); | ||
1244 | 1227 | | |||
1245 | m_blendingEnabled = enabled; | 1228 | m_blendingEnabled = enabled; | ||
1246 | } | 1229 | } | ||
1247 | 1230 | | |||
1248 | void OpenGLWindow::setupLeafNodes(LeafNode *nodes, const WindowQuadList *quads, const WindowPaintData &data) | 1231 | void OpenGLWindow::initializeRenderContext(RenderContext &context, const WindowPaintData &data) | ||
1249 | { | 1232 | { | ||
1250 | if (!quads[ShadowLeaf].isEmpty()) { | 1233 | QVector<RenderNode> &renderNodes = context.renderNodes; | ||
1251 | nodes[ShadowLeaf].texture = static_cast<SceneOpenGLShadow *>(m_shadow)->shadowTexture(); | 1234 | | ||
1252 | nodes[ShadowLeaf].opacity = data.opacity(); | 1235 | int contentsNodeCount = 0; | ||
1253 | nodes[ShadowLeaf].hasAlpha = true; | 1236 | for (const WindowQuad &quad : data.quads) { | ||
1254 | nodes[ShadowLeaf].coordinateType = NormalizedCoordinates; | 1237 | if (quad.type() != WindowQuadContents) | ||
1255 | } | 1238 | continue; | ||
1256 | 1239 | contentsNodeCount = std::max(contentsNodeCount, quad.id() + 1); // Cheating. | |||
1257 | if (!quads[DecorationLeaf].isEmpty()) { | 1240 | } | ||
1258 | nodes[DecorationLeaf].texture = getDecorationTexture(); | 1241 | | ||
1259 | nodes[DecorationLeaf].opacity = data.opacity(); | 1242 | context.shadowOffset = 0; | ||
1260 | nodes[DecorationLeaf].hasAlpha = true; | 1243 | context.decorationOffset = 1; | ||
1261 | nodes[DecorationLeaf].coordinateType = UnnormalizedCoordinates; | 1244 | context.contentOffset = 2; | ||
1262 | } | 1245 | context.previousContentOffset = contentsNodeCount + 2; | ||
1263 | 1246 | context.quadCount = data.quads.count(); | |||
1264 | nodes[ContentLeaf].texture = s_frameTexture; | 1247 | | ||
1265 | nodes[ContentLeaf].hasAlpha = !isOpaque(); | 1248 | const int nodeCount = context.previousContentOffset + 1; | ||
1266 | // TODO: ARGB crsoofading is atm. a hack, playing on opacities for two dumb SrcOver operations | 1249 | renderNodes.resize(nodeCount); | ||
1267 | // Should be a shader | 1250 | | ||
1251 | for (const WindowQuad &quad : data.quads) { | ||||
1252 | switch (quad.type()) { | ||||
1253 | case WindowQuadShadow: | ||||
1254 | renderNodes[context.shadowOffset].quads << quad; | ||||
1255 | break; | ||||
1256 | | ||||
1257 | case WindowQuadDecoration: | ||||
1258 | renderNodes[context.decorationOffset].quads << quad; | ||||
1259 | break; | ||||
1260 | | ||||
1261 | case WindowQuadContents: | ||||
1262 | renderNodes[context.contentOffset + quad.id()].quads << quad; | ||||
1263 | break; | ||||
1264 | | ||||
1265 | default: | ||||
1266 | // Ignore window quad generated by effects. | ||||
1267 | break; | ||||
1268 | } | ||||
1269 | } | ||||
1270 | | ||||
1271 | RenderNode &shadowRenderNode = renderNodes[context.shadowOffset]; | ||||
apol: For readability it would make sense to keep a reference of the objects we're interacting with. | |||||
1272 | if (!shadowRenderNode.quads.isEmpty()) { | ||||
1273 | SceneOpenGLShadow *shadow = static_cast<SceneOpenGLShadow *>(m_shadow); | ||||
1274 | shadowRenderNode.texture = shadow->shadowTexture(); | ||||
1275 | shadowRenderNode.opacity = data.opacity(); | ||||
1276 | shadowRenderNode.hasAlpha = true; | ||||
1277 | shadowRenderNode.coordinateType = NormalizedCoordinates; | ||||
1278 | shadowRenderNode.leafType = ShadowLeaf; | ||||
1279 | } | ||||
1280 | | ||||
1281 | RenderNode &decorationRenderNode = renderNodes[context.decorationOffset]; | ||||
1282 | if (!decorationRenderNode.quads.isEmpty()) { | ||||
1283 | decorationRenderNode.texture = getDecorationTexture(); | ||||
1284 | decorationRenderNode.opacity = data.opacity(); | ||||
1285 | decorationRenderNode.hasAlpha = true; | ||||
1286 | decorationRenderNode.coordinateType = UnnormalizedCoordinates; | ||||
1287 | decorationRenderNode.leafType = DecorationLeaf; | ||||
1288 | } | ||||
1289 | | ||||
1290 | // FIXME: Cross-fading must be implemented in a shader. | ||||
1291 | float contentOpacity = data.opacity(); | ||||
Doesn't look like a fixme, more of a phabricator task to tackle in the future? Reading this makes others feel sad but doesn't make one implement anything. apol: Doesn't look like a fixme, more of a phabricator task to tackle in the future?
Reading this… | |||||
zzag: Ok, I'll create a task. | |||||
1268 | if (data.crossFadeProgress() != 1.0 && (data.opacity() < 0.95 || toplevel->hasAlpha())) { | 1292 | if (data.crossFadeProgress() != 1.0 && (data.opacity() < 0.95 || toplevel->hasAlpha())) { | ||
1269 | const float opacity = 1.0 - data.crossFadeProgress(); | 1293 | const float opacity = 1.0 - data.crossFadeProgress(); | ||
1270 | nodes[ContentLeaf].opacity = data.opacity() * (1 - pow(opacity, 1.0f + 2.0f * data.opacity())); | 1294 | contentOpacity *= 1 - pow(opacity, 1.0f + 2.0f * data.opacity()); | ||
1271 | } else { | | |||
1272 | nodes[ContentLeaf].opacity = data.opacity(); | | |||
1273 | } | 1295 | } | ||
1274 | nodes[ContentLeaf].coordinateType = UnnormalizedCoordinates; | | |||
1275 | 1296 | | |||
1297 | // The main surface and all of its sub-surfaces form a tree. In order to initialize | ||||
1298 | // the render nodes for the window pixmaps we need to traverse the tree in the | ||||
1299 | // depth-first search manner. The id of content window quads corresponds to the time | ||||
1300 | // when we visited the corresponding window pixmap. The DFS traversal probably doesn't | ||||
1301 | // have a significant impact on performance. However, if that's the case, we could | ||||
1302 | // keep a cache of window pixmaps in the order in which they'll be rendered. | ||||
1303 | QStack<WindowPixmap *> stack; | ||||
1304 | stack.push(windowPixmap<OpenGLWindowPixmap>()); | ||||
1305 | | ||||
1306 | int i = 0; | ||||
1307 | | ||||
1308 | while (!stack.isEmpty()) { | ||||
1309 | OpenGLWindowPixmap *windowPixmap = static_cast<OpenGLWindowPixmap *>(stack.pop()); | ||||
1310 | | ||||
1311 | // If it's an unmapped sub-surface, don't render it and all of its children. | ||||
1312 | if (!windowPixmap->isValid()) | ||||
1313 | continue; | ||||
1314 | | ||||
1315 | RenderNode &contentRenderNode = renderNodes[context.contentOffset + i++]; | ||||
1316 | contentRenderNode.texture = windowPixmap->texture(); | ||||
1317 | contentRenderNode.hasAlpha = windowPixmap->hasAlphaChannel(); | ||||
1318 | contentRenderNode.opacity = contentOpacity; | ||||
1319 | contentRenderNode.coordinateType = UnnormalizedCoordinates; | ||||
1320 | contentRenderNode.leafType = ContentLeaf; | ||||
1321 | | ||||
1322 | const QVector<WindowPixmap *> children = windowPixmap->children(); | ||||
1323 | for (WindowPixmap *child : children) | ||||
1324 | stack.push(child); | ||||
1325 | } | ||||
1326 | | ||||
1327 | // Note that cross-fading is currently working properly only on X11. In order to make it | ||||
1328 | // work on Wayland, we have to render the current and the previous window pixmap trees in | ||||
1329 | // offscreen render targets, then use a cross-fading shader to blend those two layers. | ||||
1276 | if (data.crossFadeProgress() != 1.0) { | 1330 | if (data.crossFadeProgress() != 1.0) { | ||
1277 | OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>(); | 1331 | OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>(); | ||
1278 | nodes[PreviousContentLeaf].texture = previous ? previous->texture() : nullptr; | 1332 | if (previous) { // TODO(vlad): Should cross-fading be disabled on Wayland? | ||
1279 | nodes[PreviousContentLeaf].hasAlpha = !isOpaque(); | 1333 | const QRect &oldGeometry = previous->contentsRect(); | ||
1280 | nodes[PreviousContentLeaf].opacity = data.opacity() * (1.0 - data.crossFadeProgress()); | 1334 | RenderNode &previousContentRenderNode = renderNodes[context.previousContentOffset]; | ||
1281 | nodes[PreviousContentLeaf].coordinateType = NormalizedCoordinates; | 1335 | for (const WindowQuad &quad : qAsConst(renderNodes[context.contentOffset].quads)) { | ||
1336 | // We need to create new window quads with normalized texture coordinates. | ||||
1337 | // Normal quads divide the x/y position by width/height. This would not work | ||||
1338 | // as the texture is larger than the visible content in case of a decorated | ||||
1339 | // Client resulting in garbage being shown. So we calculate the normalized | ||||
1340 | // texture coordinate in the Client's new content space and map it to the | ||||
1341 | // previous Client's content space. | ||||
1342 | WindowQuad newQuad(WindowQuadContents); | ||||
1343 | for (int i = 0; i < 4; ++i) { | ||||
1344 | const qreal xFactor = (quad[i].textureX() - toplevel->clientPos().x()) | ||||
1345 | / qreal(toplevel->clientSize().width()); | ||||
1346 | const qreal yFactor = (quad[i].textureY() - toplevel->clientPos().y()) | ||||
1347 | / qreal(toplevel->clientSize().height()); | ||||
1348 | const qreal u = (xFactor * oldGeometry.width() + oldGeometry.x()) | ||||
1349 | / qreal(previous->size().width()); | ||||
1350 | const qreal v = (yFactor * oldGeometry.height() + oldGeometry.y()) | ||||
1351 | / qreal(previous->size().height()); | ||||
1352 | newQuad[i] = WindowVertex(quad[i].x(), quad[i].y(), u, v); | ||||
1353 | } | ||||
1354 | previousContentRenderNode.quads.append(newQuad); | ||||
1355 | } | ||||
1356 | | ||||
1357 | previousContentRenderNode.texture = previous->texture(); | ||||
1358 | previousContentRenderNode.hasAlpha = previous->hasAlphaChannel(); | ||||
1359 | previousContentRenderNode.opacity = data.opacity() * (1.0 - data.crossFadeProgress()); | ||||
1360 | previousContentRenderNode.coordinateType = NormalizedCoordinates; | ||||
1361 | previousContentRenderNode.leafType = PreviousContentLeaf; | ||||
1362 | | ||||
1363 | context.quadCount += previousContentRenderNode.quads.count(); | ||||
1364 | } | ||||
IMHO no, i'tll be fine for the majority cases. Whilst we're doing the opacity blend we do have another option here. Instead of one tree with an old and new pixmap in each node we can reference an old and new tree where each node has one pixmap. In any case, that's a task for another day. davidedmundson: IMHO no, i'tll be fine for the majority cases.
Whilst we're doing the opacity blend we do have… | |||||
1282 | } | 1365 | } | ||
1283 | } | 1366 | } | ||
1284 | 1367 | | |||
1285 | QMatrix4x4 OpenGLWindow::modelViewProjectionMatrix(int mask, const WindowPaintData &data) const | 1368 | QMatrix4x4 OpenGLWindow::modelViewProjectionMatrix(int mask, const WindowPaintData &data) const | ||
1286 | { | 1369 | { | ||
1287 | SceneOpenGL2 *scene = static_cast<SceneOpenGL2 *>(m_scene); | 1370 | SceneOpenGL2 *scene = static_cast<SceneOpenGL2 *>(m_scene); | ||
1288 | 1371 | | |||
1289 | const QMatrix4x4 pMatrix = data.projectionMatrix(); | 1372 | const QMatrix4x4 pMatrix = data.projectionMatrix(); | ||
Show All 11 Lines | |||||
1301 | // with the default projection matrix. If the effect hasn't specified a | 1384 | // with the default projection matrix. If the effect hasn't specified a | ||
1302 | // model-view matrix, mvMatrix will be the identity matrix. | 1385 | // model-view matrix, mvMatrix will be the identity matrix. | ||
1303 | if (mask & Scene::PAINT_SCREEN_TRANSFORMED) | 1386 | if (mask & Scene::PAINT_SCREEN_TRANSFORMED) | ||
1304 | return scene->screenProjectionMatrix() * mvMatrix; | 1387 | return scene->screenProjectionMatrix() * mvMatrix; | ||
1305 | 1388 | | |||
1306 | return scene->projectionMatrix() * mvMatrix; | 1389 | return scene->projectionMatrix() * mvMatrix; | ||
1307 | } | 1390 | } | ||
1308 | 1391 | | |||
1309 | void OpenGLWindow::renderSubSurface(GLShader *shader, const QMatrix4x4 &mvp, const QMatrix4x4 &windowMatrix, OpenGLWindowPixmap *pixmap, const QRegion ®ion, bool hardwareClipping) | | |||
1310 | { | | |||
1311 | QMatrix4x4 newWindowMatrix = windowMatrix; | | |||
1312 | newWindowMatrix.translate(pixmap->subSurface()->position().x(), pixmap->subSurface()->position().y()); | | |||
1313 | | ||||
1314 | qreal scale = 1.0; | | |||
1315 | if (pixmap->surface()) { | | |||
1316 | scale = pixmap->surface()->scale(); | | |||
1317 | } | | |||
1318 | | ||||
1319 | if (!pixmap->texture()->isNull()) { | | |||
1320 | setBlendEnabled(pixmap->buffer() && pixmap->buffer()->hasAlphaChannel()); | | |||
1321 | // render this texture | | |||
1322 | shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp * newWindowMatrix); | | |||
1323 | auto texture = pixmap->texture(); | | |||
1324 | texture->bind(); | | |||
1325 | texture->render(region, QRect(0, 0, texture->width() / scale, texture->height() / scale), hardwareClipping); | | |||
1326 | texture->unbind(); | | |||
1327 | } | | |||
1328 | | ||||
1329 | const auto &children = pixmap->children(); | | |||
1330 | for (auto pixmap : children) { | | |||
1331 | if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) { | | |||
1332 | continue; | | |||
1333 | } | | |||
1334 | renderSubSurface(shader, mvp, newWindowMatrix, static_cast<OpenGLWindowPixmap*>(pixmap), region, hardwareClipping); | | |||
1335 | } | | |||
1336 | } | | |||
1337 | | ||||
1338 | void OpenGLWindow::performPaint(int mask, const QRegion ®ion, const WindowPaintData &_data) | 1392 | void OpenGLWindow::performPaint(int mask, const QRegion ®ion, const WindowPaintData &_data) | ||
1339 | { | 1393 | { | ||
1340 | WindowPaintData data = _data; | 1394 | WindowPaintData data = _data; | ||
1341 | if (!beginRenderWindow(mask, region, data)) | 1395 | if (!beginRenderWindow(mask, region, data)) | ||
1342 | return; | 1396 | return; | ||
1343 | 1397 | | |||
1344 | QMatrix4x4 windowMatrix = transformation(mask, data); | 1398 | QMatrix4x4 windowMatrix = transformation(mask, data); | ||
1345 | const QMatrix4x4 modelViewProjection = modelViewProjectionMatrix(mask, data); | 1399 | const QMatrix4x4 modelViewProjection = modelViewProjectionMatrix(mask, data); | ||
1346 | const QMatrix4x4 mvpMatrix = modelViewProjection * windowMatrix; | 1400 | const QMatrix4x4 mvpMatrix = modelViewProjection * windowMatrix; | ||
1347 | 1401 | | |||
1348 | | ||||
1349 | bool useX11TextureClamp = false; | 1402 | bool useX11TextureClamp = false; | ||
1350 | 1403 | | |||
1351 | GLShader *shader = data.shader; | 1404 | GLShader *shader = data.shader; | ||
1352 | GLenum filter; | 1405 | GLenum filter; | ||
1353 | 1406 | | |||
1354 | if (waylandServer()) { | 1407 | if (waylandServer()) { | ||
1355 | filter = GL_LINEAR; | 1408 | filter = GL_LINEAR; | ||
1356 | } else { | 1409 | } else { | ||
Show All 20 Lines | 1429 | if (data.saturation() != 1.0) | |||
1377 | traits |= ShaderTrait::AdjustSaturation; | 1430 | traits |= ShaderTrait::AdjustSaturation; | ||
1378 | 1431 | | |||
1379 | shader = ShaderManager::instance()->pushShader(traits); | 1432 | shader = ShaderManager::instance()->pushShader(traits); | ||
1380 | } | 1433 | } | ||
1381 | shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix); | 1434 | shader->setUniform(GLShader::ModelViewProjectionMatrix, mvpMatrix); | ||
1382 | 1435 | | |||
1383 | shader->setUniform(GLShader::Saturation, data.saturation()); | 1436 | shader->setUniform(GLShader::Saturation, data.saturation()); | ||
1384 | 1437 | | |||
1385 | WindowQuadList quads[LeafCount]; | 1438 | RenderContext renderContext; | ||
1386 | 1439 | initializeRenderContext(renderContext, data); | |||
1387 | // Split the quads into separate lists for each type | | |||
1388 | foreach (const WindowQuad &quad, data.quads) { | | |||
1389 | switch (quad.type()) { | | |||
1390 | case WindowQuadDecoration: | | |||
1391 | quads[DecorationLeaf].append(quad); | | |||
1392 | continue; | | |||
1393 | | ||||
1394 | case WindowQuadContents: | | |||
1395 | quads[ContentLeaf].append(quad); | | |||
1396 | continue; | | |||
1397 | | ||||
1398 | case WindowQuadShadow: | | |||
1399 | quads[ShadowLeaf].append(quad); | | |||
1400 | continue; | | |||
1401 | | ||||
1402 | default: | | |||
1403 | continue; | | |||
1404 | } | | |||
1405 | } | | |||
1406 | | ||||
1407 | if (data.crossFadeProgress() != 1.0) { | | |||
1408 | OpenGLWindowPixmap *previous = previousWindowPixmap<OpenGLWindowPixmap>(); | | |||
1409 | if (previous) { | | |||
1410 | const QRect &oldGeometry = previous->contentsRect(); | | |||
1411 | for (const WindowQuad &quad : quads[ContentLeaf]) { | | |||
1412 | // we need to create new window quads with normalize texture coordinates | | |||
1413 | // normal quads divide the x/y position by width/height. This would not work as the texture | | |||
1414 | // is larger than the visible content in case of a decorated Client resulting in garbage being shown. | | |||
1415 | // So we calculate the normalized texture coordinate in the Client's new content space and map it to | | |||
1416 | // the previous Client's content space. | | |||
1417 | WindowQuad newQuad(WindowQuadContents); | | |||
1418 | for (int i = 0; i < 4; ++i) { | | |||
1419 | const qreal xFactor = qreal(quad[i].textureX() - toplevel->clientPos().x())/qreal(toplevel->clientSize().width()); | | |||
1420 | const qreal yFactor = qreal(quad[i].textureY() - toplevel->clientPos().y())/qreal(toplevel->clientSize().height()); | | |||
1421 | WindowVertex vertex(quad[i].x(), quad[i].y(), | | |||
1422 | (xFactor * oldGeometry.width() + oldGeometry.x())/qreal(previous->size().width()), | | |||
1423 | (yFactor * oldGeometry.height() + oldGeometry.y())/qreal(previous->size().height())); | | |||
1424 | newQuad[i] = vertex; | | |||
1425 | } | | |||
1426 | quads[PreviousContentLeaf].append(newQuad); | | |||
1427 | } | | |||
1428 | } | | |||
1429 | } | | |||
1430 | 1440 | | |||
1431 | const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads(); | 1441 | const bool indexedQuads = GLVertexBuffer::supportsIndexedQuads(); | ||
1432 | const GLenum primitiveType = indexedQuads ? GL_QUADS : GL_TRIANGLES; | 1442 | const GLenum primitiveType = indexedQuads ? GL_QUADS : GL_TRIANGLES; | ||
1433 | const int verticesPerQuad = indexedQuads ? 4 : 6; | 1443 | const int verticesPerQuad = indexedQuads ? 4 : 6; | ||
1434 | 1444 | | |||
1435 | const size_t size = verticesPerQuad * | 1445 | const size_t size = verticesPerQuad * renderContext.quadCount * sizeof(GLVertex2D); | ||
1436 | (quads[0].count() + quads[1].count() + quads[2].count() + quads[3].count()) * sizeof(GLVertex2D); | | |||
1437 | 1446 | | |||
1438 | GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); | 1447 | GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); | ||
1439 | GLVertex2D *map = (GLVertex2D *) vbo->map(size); | 1448 | GLVertex2D *map = (GLVertex2D *) vbo->map(size); | ||
1440 | 1449 | | |||
1441 | LeafNode nodes[LeafCount]; | 1450 | for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) { | ||
1442 | setupLeafNodes(nodes, quads, data); | 1451 | RenderNode &renderNode = renderContext.renderNodes[i]; | ||
1443 | 1452 | if (renderNode.quads.isEmpty() || !renderNode.texture) | |||
1444 | for (int i = 0, v = 0; i < LeafCount; i++) { | | |||
1445 | if (quads[i].isEmpty() || !nodes[i].texture) | | |||
1446 | continue; | 1453 | continue; | ||
1447 | 1454 | | |||
1448 | nodes[i].firstVertex = v; | 1455 | renderNode.firstVertex = v; | ||
1449 | nodes[i].vertexCount = quads[i].count() * verticesPerQuad; | 1456 | renderNode.vertexCount = renderNode.quads.count() * verticesPerQuad; | ||
1450 | 1457 | | |||
1451 | const QMatrix4x4 matrix = nodes[i].texture->matrix(nodes[i].coordinateType); | 1458 | const QMatrix4x4 matrix = renderNode.texture->matrix(renderNode.coordinateType); | ||
1452 | 1459 | | |||
1453 | quads[i].makeInterleavedArrays(primitiveType, &map[v], matrix); | 1460 | renderNode.quads.makeInterleavedArrays(primitiveType, &map[v], matrix); | ||
1454 | v += quads[i].count() * verticesPerQuad; | 1461 | v += renderNode.quads.count() * verticesPerQuad; | ||
1455 | } | 1462 | } | ||
1456 | 1463 | | |||
1457 | vbo->unmap(); | 1464 | vbo->unmap(); | ||
1458 | vbo->bindArrays(); | 1465 | vbo->bindArrays(); | ||
1459 | 1466 | | |||
1460 | // Make sure the blend function is set up correctly in case we will be doing blending | 1467 | // Make sure the blend function is set up correctly in case we will be doing blending | ||
1461 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | 1468 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | ||
1462 | 1469 | | |||
1463 | float opacity = -1.0; | 1470 | float opacity = -1.0; | ||
1464 | 1471 | | |||
1465 | for (int i = 0; i < LeafCount; i++) { | 1472 | for (int i = 0; i < renderContext.renderNodes.count(); i++) { | ||
1466 | if (nodes[i].vertexCount == 0) | 1473 | const RenderNode &renderNode = renderContext.renderNodes[i]; | ||
1474 | if (renderNode.vertexCount == 0) | ||||
1467 | continue; | 1475 | continue; | ||
1468 | 1476 | | |||
1469 | setBlendEnabled(nodes[i].hasAlpha || nodes[i].opacity < 1.0); | 1477 | setBlendEnabled(renderNode.hasAlpha || renderNode.opacity < 1.0); | ||
1470 | 1478 | | |||
1471 | if (opacity != nodes[i].opacity) { | 1479 | if (opacity != renderNode.opacity) { | ||
1472 | shader->setUniform(GLShader::ModulationConstant, | 1480 | shader->setUniform(GLShader::ModulationConstant, | ||
1473 | modulate(nodes[i].opacity, data.brightness())); | 1481 | modulate(renderNode.opacity, data.brightness())); | ||
1474 | opacity = nodes[i].opacity; | 1482 | opacity = renderNode.opacity; | ||
1475 | } | 1483 | } | ||
1476 | 1484 | | |||
1477 | nodes[i].texture->setFilter(filter); | 1485 | renderNode.texture->setFilter(filter); | ||
1478 | nodes[i].texture->setWrapMode(GL_CLAMP_TO_EDGE); | 1486 | renderNode.texture->setWrapMode(GL_CLAMP_TO_EDGE); | ||
1479 | nodes[i].texture->bind(); | 1487 | renderNode.texture->bind(); | ||
1480 | 1488 | | |||
1481 | if (i == ContentLeaf && useX11TextureClamp) { | 1489 | if (renderNode.leafType == ContentLeaf && useX11TextureClamp) { | ||
1482 | // X11 windows are reparented to have their buffer in the middle of a larger texture | 1490 | // X11 windows are reparented to have their buffer in the middle of a larger texture | ||
1483 | // holding the frame window. | 1491 | // holding the frame window. | ||
1484 | // This code passes the texture geometry to the fragment shader | 1492 | // This code passes the texture geometry to the fragment shader | ||
1485 | // any samples near the edge of the texture will be constrained to be | 1493 | // any samples near the edge of the texture will be constrained to be | ||
1486 | // at least half a pixel in bounds, meaning we don't bleed the transparent border | 1494 | // at least half a pixel in bounds, meaning we don't bleed the transparent border | ||
1487 | QRectF bufferContentRect = clientShape().boundingRect(); | 1495 | QRectF bufferContentRect = clientShape().boundingRect(); | ||
1488 | bufferContentRect.adjust(0.5, 0.5, -0.5, -0.5); | 1496 | bufferContentRect.adjust(0.5, 0.5, -0.5, -0.5); | ||
1489 | const QRect bufferGeometry = toplevel->bufferGeometry(); | 1497 | const QRect bufferGeometry = toplevel->bufferGeometry(); | ||
1490 | 1498 | | |||
1491 | float leftClamp = bufferContentRect.left() / bufferGeometry.width(); | 1499 | float leftClamp = bufferContentRect.left() / bufferGeometry.width(); | ||
1492 | float topClamp = bufferContentRect.top() / bufferGeometry.height(); | 1500 | float topClamp = bufferContentRect.top() / bufferGeometry.height(); | ||
1493 | float rightClamp = bufferContentRect.right() / bufferGeometry.width(); | 1501 | float rightClamp = bufferContentRect.right() / bufferGeometry.width(); | ||
1494 | float bottomClamp = bufferContentRect.bottom() / bufferGeometry.height(); | 1502 | float bottomClamp = bufferContentRect.bottom() / bufferGeometry.height(); | ||
1495 | shader->setUniform(GLShader::TextureClamp, QVector4D({leftClamp, topClamp, rightClamp, bottomClamp})); | 1503 | shader->setUniform(GLShader::TextureClamp, QVector4D({leftClamp, topClamp, rightClamp, bottomClamp})); | ||
1496 | } else { | 1504 | } else { | ||
1497 | shader->setUniform(GLShader::TextureClamp, QVector4D({0, 0, 1, 1})); | 1505 | shader->setUniform(GLShader::TextureClamp, QVector4D({0, 0, 1, 1})); | ||
1498 | } | 1506 | } | ||
1499 | 1507 | | |||
1500 | vbo->draw(region, primitiveType, nodes[i].firstVertex, nodes[i].vertexCount, m_hardwareClipping); | 1508 | vbo->draw(region, primitiveType, renderNode.firstVertex, | ||
1509 | renderNode.vertexCount, m_hardwareClipping); | ||||
1501 | } | 1510 | } | ||
1502 | 1511 | | |||
1503 | vbo->unbindArrays(); | 1512 | vbo->unbindArrays(); | ||
1504 | 1513 | | |||
1505 | // render sub-surfaces | | |||
1506 | auto wp = windowPixmap<OpenGLWindowPixmap>(); | | |||
1507 | const auto &children = wp ? wp->children() : QVector<WindowPixmap*>(); | | |||
1508 | const QPoint mainSurfaceOffset = bufferOffset(); | | |||
1509 | windowMatrix.translate(mainSurfaceOffset.x(), mainSurfaceOffset.y()); | | |||
1510 | for (auto pixmap : children) { | | |||
1511 | if (pixmap->subSurface().isNull() || pixmap->subSurface()->surface().isNull() || !pixmap->subSurface()->surface()->isMapped()) { | | |||
1512 | continue; | | |||
1513 | } | | |||
1514 | renderSubSurface(shader, modelViewProjection, windowMatrix, static_cast<OpenGLWindowPixmap*>(pixmap), region, m_hardwareClipping); | | |||
1515 | } | | |||
1516 | | ||||
1517 | setBlendEnabled(false); | 1514 | setBlendEnabled(false); | ||
1518 | 1515 | | |||
1519 | if (!data.shader) | 1516 | if (!data.shader) | ||
1520 | ShaderManager::instance()->popShader(); | 1517 | ShaderManager::instance()->popShader(); | ||
1521 | 1518 | | |||
1522 | endRenderWindow(); | 1519 | endRenderWindow(); | ||
1523 | } | 1520 | } | ||
1524 | 1521 | | |||
Show All 39 Lines | 1546 | { | |||
1564 | 1561 | | |||
1565 | // That's an X11 client. | 1562 | // That's an X11 client. | ||
1566 | return false; | 1563 | return false; | ||
1567 | } | 1564 | } | ||
1568 | 1565 | | |||
1569 | bool OpenGLWindowPixmap::bind() | 1566 | bool OpenGLWindowPixmap::bind() | ||
1570 | { | 1567 | { | ||
1571 | if (!m_texture->isNull()) { | 1568 | if (!m_texture->isNull()) { | ||
1572 | // always call updateBuffer to get the sub-surface tree updated | | |||
1573 | if (subSurface().isNull() && !toplevel()->damage().isEmpty()) { | | |||
1574 | updateBuffer(); | | |||
1575 | } | | |||
1576 | if (needsPixmapUpdate(this)) { | 1569 | if (needsPixmapUpdate(this)) { | ||
1577 | m_texture->updateFromPixmap(this); | 1570 | m_texture->updateFromPixmap(this); | ||
1578 | // mipmaps need to be updated | 1571 | // mipmaps need to be updated | ||
1579 | m_texture->setDirty(); | 1572 | m_texture->setDirty(); | ||
1580 | } | 1573 | } | ||
1581 | if (subSurface().isNull()) { | 1574 | if (subSurface().isNull()) { | ||
1582 | toplevel()->resetDamage(); | 1575 | toplevel()->resetDamage(); | ||
1583 | } | 1576 | } | ||
1584 | // also bind all children | 1577 | // also bind all children | ||
1585 | for (auto it = children().constBegin(); it != children().constEnd(); ++it) { | 1578 | for (auto it = children().constBegin(); it != children().constEnd(); ++it) { | ||
1586 | static_cast<OpenGLWindowPixmap*>(*it)->bind(); | 1579 | static_cast<OpenGLWindowPixmap*>(*it)->bind(); | ||
1587 | } | 1580 | } | ||
1588 | return true; | 1581 | return true; | ||
1589 | } | 1582 | } | ||
1590 | // also bind all children, needs to be done before checking isValid | | |||
1591 | // as there might be valid children to render, see https://bugreports.qt.io/browse/QTBUG-52192 | | |||
1592 | if (subSurface().isNull()) { | | |||
1593 | updateBuffer(); | | |||
1594 | } | | |||
1595 | for (auto it = children().constBegin(); it != children().constEnd(); ++it) { | 1583 | for (auto it = children().constBegin(); it != children().constEnd(); ++it) { | ||
1596 | static_cast<OpenGLWindowPixmap*>(*it)->bind(); | 1584 | static_cast<OpenGLWindowPixmap*>(*it)->bind(); | ||
1597 | } | 1585 | } | ||
1598 | if (!isValid()) { | 1586 | if (!isValid()) { | ||
1599 | return false; | 1587 | return false; | ||
1600 | } | 1588 | } | ||
1601 | 1589 | | |||
1602 | bool success = m_texture->load(this); | 1590 | bool success = m_texture->load(this); | ||
▲ Show 20 Lines • Show All 1120 Lines • Show Last 20 Lines |
For readability it would make sense to keep a reference of the objects we're interacting with.