File indexing completed on 2024-11-10 04:57:15
0001 /* 0002 SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "scene/itemrenderer_opengl.h" 0008 #include "core/pixelgrid.h" 0009 #include "core/rendertarget.h" 0010 #include "core/renderviewport.h" 0011 #include "effect/effect.h" 0012 #include "platformsupport/scenes/opengl/openglsurfacetexture.h" 0013 #include "scene/decorationitem.h" 0014 #include "scene/imageitem.h" 0015 #include "scene/shadowitem.h" 0016 #include "scene/surfaceitem.h" 0017 #include "scene/workspacescene_opengl.h" 0018 #include "utils/common.h" 0019 0020 namespace KWin 0021 { 0022 0023 ItemRendererOpenGL::ItemRendererOpenGL() 0024 { 0025 const QString visualizeOptionsString = qEnvironmentVariable("KWIN_SCENE_VISUALIZE"); 0026 if (!visualizeOptionsString.isEmpty()) { 0027 const QStringList visualtizeOptions = visualizeOptionsString.split(';'); 0028 m_debug.fractionalEnabled = visualtizeOptions.contains(QLatin1StringView("fractional")); 0029 } 0030 } 0031 0032 std::unique_ptr<ImageItem> ItemRendererOpenGL::createImageItem(Scene *scene, Item *parent) 0033 { 0034 return std::make_unique<ImageItemOpenGL>(scene, parent); 0035 } 0036 0037 void ItemRendererOpenGL::beginFrame(const RenderTarget &renderTarget, const RenderViewport &viewport) 0038 { 0039 GLFramebuffer *fbo = renderTarget.framebuffer(); 0040 GLFramebuffer::pushFramebuffer(fbo); 0041 0042 GLVertexBuffer::streamingBuffer()->beginFrame(); 0043 } 0044 0045 void ItemRendererOpenGL::endFrame() 0046 { 0047 GLVertexBuffer::streamingBuffer()->endOfFrame(); 0048 GLFramebuffer::popFramebuffer(); 0049 } 0050 0051 QVector4D ItemRendererOpenGL::modulate(float opacity, float brightness) const 0052 { 0053 const float a = opacity; 0054 const float rgb = opacity * brightness; 0055 0056 return QVector4D(rgb, rgb, rgb, a); 0057 } 0058 0059 void ItemRendererOpenGL::setBlendEnabled(bool enabled) 0060 { 0061 if (enabled && !m_blendingEnabled) { 0062 glEnable(GL_BLEND); 0063 } else if (!enabled && m_blendingEnabled) { 0064 glDisable(GL_BLEND); 0065 } 0066 0067 m_blendingEnabled = enabled; 0068 } 0069 0070 static OpenGLSurfaceContents bindSurfaceTexture(SurfaceItem *surfaceItem) 0071 { 0072 SurfacePixmap *surfacePixmap = surfaceItem->pixmap(); 0073 auto platformSurfaceTexture = 0074 static_cast<OpenGLSurfaceTexture *>(surfacePixmap->texture()); 0075 if (surfacePixmap->isDiscarded()) { 0076 return platformSurfaceTexture->texture(); 0077 } 0078 0079 if (platformSurfaceTexture->texture().isValid()) { 0080 const QRegion region = surfaceItem->damage(); 0081 if (!region.isEmpty()) { 0082 platformSurfaceTexture->update(region); 0083 surfaceItem->resetDamage(); 0084 } 0085 } else { 0086 if (!surfacePixmap->isValid()) { 0087 return {}; 0088 } 0089 if (!platformSurfaceTexture->create()) { 0090 qCDebug(KWIN_OPENGL) << "Failed to bind window"; 0091 return {}; 0092 } 0093 surfaceItem->resetDamage(); 0094 } 0095 0096 return platformSurfaceTexture->texture(); 0097 } 0098 0099 static RenderGeometry clipQuads(const Item *item, const ItemRendererOpenGL::RenderContext *context) 0100 { 0101 const WindowQuadList quads = item->quads(); 0102 0103 // Item to world translation. 0104 const QPointF worldTranslation = context->transformStack.top().map(QPointF(0., 0.)); 0105 const qreal scale = context->renderTargetScale; 0106 0107 RenderGeometry geometry; 0108 geometry.reserve(quads.count() * 6); 0109 0110 // split all quads in bounding rect with the actual rects in the region 0111 for (const WindowQuad &quad : std::as_const(quads)) { 0112 if (context->clip != infiniteRegion() && !context->hardwareClipping) { 0113 // Scale to device coordinates, rounding as needed. 0114 QRectF deviceBounds = snapToPixelGridF(scaledRect(quad.bounds(), scale)); 0115 0116 for (const QRect &clipRect : std::as_const(context->clip)) { 0117 QRectF deviceClipRect = snapToPixelGridF(scaledRect(clipRect, scale)).translated(-worldTranslation); 0118 0119 const QRectF &intersected = deviceClipRect.intersected(deviceBounds); 0120 if (intersected.isValid()) { 0121 if (deviceBounds == intersected) { 0122 // case 1: completely contains, include and do not check other rects 0123 geometry.appendWindowQuad(quad, scale); 0124 break; 0125 } 0126 // case 2: intersection 0127 geometry.appendSubQuad(quad, intersected, scale); 0128 } 0129 } 0130 } else { 0131 geometry.appendWindowQuad(quad, scale); 0132 } 0133 } 0134 0135 return geometry; 0136 } 0137 0138 void ItemRendererOpenGL::createRenderNode(Item *item, RenderContext *context) 0139 { 0140 const QList<Item *> sortedChildItems = item->sortedChildItems(); 0141 0142 QMatrix4x4 matrix; 0143 const auto logicalPosition = QVector2D(item->position().x(), item->position().y()); 0144 const auto scale = context->renderTargetScale; 0145 matrix.translate(roundVector(logicalPosition * scale).toVector3D()); 0146 matrix *= item->transform(); 0147 context->transformStack.push(context->transformStack.top() * matrix); 0148 0149 context->opacityStack.push(context->opacityStack.top() * item->opacity()); 0150 0151 for (Item *childItem : sortedChildItems) { 0152 if (childItem->z() >= 0) { 0153 break; 0154 } 0155 if (childItem->explicitVisible()) { 0156 createRenderNode(childItem, context); 0157 } 0158 } 0159 0160 item->preprocess(); 0161 0162 RenderGeometry geometry = clipQuads(item, context); 0163 0164 if (auto shadowItem = qobject_cast<ShadowItem *>(item)) { 0165 if (!geometry.isEmpty()) { 0166 OpenGLShadowTextureProvider *textureProvider = static_cast<OpenGLShadowTextureProvider *>(shadowItem->textureProvider()); 0167 context->renderNodes.append(RenderNode{ 0168 .texture = textureProvider->shadowTexture(), 0169 .geometry = geometry, 0170 .transformMatrix = context->transformStack.top(), 0171 .opacity = context->opacityStack.top(), 0172 .hasAlpha = true, 0173 .coordinateType = UnnormalizedCoordinates, 0174 .scale = scale, 0175 .colorDescription = item->colorDescription(), 0176 }); 0177 } 0178 } else if (auto decorationItem = qobject_cast<DecorationItem *>(item)) { 0179 if (!geometry.isEmpty()) { 0180 auto renderer = static_cast<const SceneOpenGLDecorationRenderer *>(decorationItem->renderer()); 0181 context->renderNodes.append(RenderNode{ 0182 .texture = renderer->texture(), 0183 .geometry = geometry, 0184 .transformMatrix = context->transformStack.top(), 0185 .opacity = context->opacityStack.top(), 0186 .hasAlpha = true, 0187 .coordinateType = UnnormalizedCoordinates, 0188 .scale = scale, 0189 .colorDescription = item->colorDescription(), 0190 }); 0191 } 0192 } else if (auto surfaceItem = qobject_cast<SurfaceItem *>(item)) { 0193 SurfacePixmap *pixmap = surfaceItem->pixmap(); 0194 if (pixmap) { 0195 if (!geometry.isEmpty()) { 0196 context->renderNodes.append(RenderNode{ 0197 .texture = bindSurfaceTexture(surfaceItem), 0198 .geometry = geometry, 0199 .transformMatrix = context->transformStack.top(), 0200 .opacity = context->opacityStack.top(), 0201 .hasAlpha = pixmap->hasAlphaChannel(), 0202 .coordinateType = NormalizedCoordinates, 0203 .scale = scale, 0204 .colorDescription = item->colorDescription(), 0205 }); 0206 } 0207 } 0208 } else if (auto imageItem = qobject_cast<ImageItemOpenGL *>(item)) { 0209 if (!geometry.isEmpty()) { 0210 context->renderNodes.append(RenderNode{ 0211 .texture = imageItem->texture(), 0212 .geometry = geometry, 0213 .transformMatrix = context->transformStack.top(), 0214 .opacity = context->opacityStack.top(), 0215 .hasAlpha = imageItem->image().hasAlphaChannel(), 0216 .coordinateType = NormalizedCoordinates, 0217 .scale = scale, 0218 .colorDescription = item->colorDescription(), 0219 }); 0220 } 0221 } 0222 0223 for (Item *childItem : sortedChildItems) { 0224 if (childItem->z() < 0) { 0225 continue; 0226 } 0227 if (childItem->explicitVisible()) { 0228 createRenderNode(childItem, context); 0229 } 0230 } 0231 0232 context->transformStack.pop(); 0233 context->opacityStack.pop(); 0234 } 0235 0236 void ItemRendererOpenGL::renderBackground(const RenderTarget &renderTarget, const RenderViewport &viewport, const QRegion ®ion) 0237 { 0238 if (region == infiniteRegion() || (region.rectCount() == 1 && (*region.begin()) == viewport.renderRect())) { 0239 glClearColor(0, 0, 0, 0); 0240 glClear(GL_COLOR_BUFFER_BIT); 0241 } else if (!region.isEmpty()) { 0242 glClearColor(0, 0, 0, 0); 0243 glEnable(GL_SCISSOR_TEST); 0244 0245 const auto targetSize = renderTarget.size(); 0246 for (const QRect &r : region) { 0247 const auto deviceRect = viewport.mapToRenderTarget(r); 0248 glScissor(deviceRect.x(), targetSize.height() - (deviceRect.y() + deviceRect.height()), deviceRect.width(), deviceRect.height()); 0249 glClear(GL_COLOR_BUFFER_BIT); 0250 } 0251 0252 glDisable(GL_SCISSOR_TEST); 0253 } 0254 } 0255 0256 void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const QRegion ®ion, const WindowPaintData &data) 0257 { 0258 if (region.isEmpty()) { 0259 return; 0260 } 0261 0262 RenderContext renderContext{ 0263 .projectionMatrix = data.projectionMatrix(), 0264 .clip = region, 0265 .hardwareClipping = region != infiniteRegion() && ((mask & Scene::PAINT_WINDOW_TRANSFORMED) || (mask & Scene::PAINT_SCREEN_TRANSFORMED)), 0266 .renderTargetScale = viewport.scale(), 0267 }; 0268 0269 renderContext.transformStack.push(QMatrix4x4()); 0270 renderContext.opacityStack.push(data.opacity()); 0271 0272 item->setTransform(data.toMatrix(renderContext.renderTargetScale)); 0273 0274 createRenderNode(item, &renderContext); 0275 0276 int totalVertexCount = 0; 0277 for (const RenderNode &node : std::as_const(renderContext.renderNodes)) { 0278 totalVertexCount += node.geometry.count(); 0279 } 0280 if (totalVertexCount == 0) { 0281 return; 0282 } 0283 0284 ShaderTraits baseShaderTraits = ShaderTrait::MapTexture; 0285 if (data.brightness() != 1.0) { 0286 baseShaderTraits |= ShaderTrait::Modulate; 0287 } 0288 if (data.saturation() != 1.0) { 0289 baseShaderTraits |= ShaderTrait::AdjustSaturation; 0290 } 0291 0292 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); 0293 vbo->reset(); 0294 vbo->setAttribLayout(std::span(GLVertexBuffer::GLVertex2DLayout), sizeof(GLVertex2D)); 0295 0296 const auto map = vbo->map<GLVertex2D>(totalVertexCount); 0297 if (!map) { 0298 return; 0299 } 0300 0301 for (int i = 0, v = 0; i < renderContext.renderNodes.count(); i++) { 0302 RenderNode &renderNode = renderContext.renderNodes[i]; 0303 if (renderNode.geometry.isEmpty() 0304 || (std::holds_alternative<GLTexture *>(renderNode.texture) && !std::get<GLTexture *>(renderNode.texture)) 0305 || (std::holds_alternative<OpenGLSurfaceContents>(renderNode.texture) && !std::get<OpenGLSurfaceContents>(renderNode.texture).isValid())) { 0306 continue; 0307 } 0308 0309 renderNode.firstVertex = v; 0310 renderNode.vertexCount = renderNode.geometry.count(); 0311 0312 GLTexture *texture = nullptr; 0313 if (std::holds_alternative<GLTexture *>(renderNode.texture)) { 0314 texture = std::get<GLTexture *>(renderNode.texture); 0315 } else { 0316 texture = std::get<OpenGLSurfaceContents>(renderNode.texture).planes.constFirst().get(); 0317 } 0318 renderNode.geometry.postProcessTextureCoordinates(texture->matrix(renderNode.coordinateType)); 0319 0320 renderNode.geometry.copy(map->subspan(v)); 0321 v += renderNode.geometry.count(); 0322 } 0323 0324 vbo->unmap(); 0325 vbo->bindArrays(); 0326 0327 if (renderContext.hardwareClipping) { 0328 glEnable(GL_SCISSOR_TEST); 0329 } 0330 0331 // Make sure the blend function is set up correctly in case we will be doing blending 0332 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 0333 0334 // The scissor region must be in the render target local coordinate system. 0335 QRegion scissorRegion = infiniteRegion(); 0336 if (renderContext.hardwareClipping) { 0337 scissorRegion = viewport.mapToRenderTarget(region); 0338 } 0339 0340 ShaderTraits lastTraits; 0341 GLShader *shader = nullptr; 0342 for (int i = 0; i < renderContext.renderNodes.count(); i++) { 0343 const RenderNode &renderNode = renderContext.renderNodes[i]; 0344 if (renderNode.vertexCount == 0) { 0345 continue; 0346 } 0347 0348 setBlendEnabled(renderNode.hasAlpha || renderNode.opacity < 1.0); 0349 0350 ShaderTraits traits = baseShaderTraits; 0351 if (renderNode.opacity != 1.0) { 0352 traits |= ShaderTrait::Modulate; 0353 } 0354 if (renderNode.colorDescription != renderTarget.colorDescription()) { 0355 traits |= ShaderTrait::TransformColorspace; 0356 } 0357 if (!shader || traits != lastTraits) { 0358 lastTraits = traits; 0359 if (shader) { 0360 ShaderManager::instance()->popShader(); 0361 } 0362 shader = ShaderManager::instance()->pushShader(traits); 0363 if (traits & ShaderTrait::AdjustSaturation) { 0364 const auto toXYZ = renderTarget.colorDescription().colorimetry().toXYZ(); 0365 shader->setUniform(GLShader::FloatUniform::Saturation, data.saturation()); 0366 shader->setUniform(GLShader::Vec3Uniform::PrimaryBrightness, QVector3D(toXYZ(1, 0), toXYZ(1, 1), toXYZ(1, 2))); 0367 } 0368 0369 if (traits & ShaderTrait::MapTexture) { 0370 shader->setUniform(GLShader::IntUniform::Sampler, 0); 0371 shader->setUniform(GLShader::IntUniform::Sampler1, 1); 0372 } 0373 } 0374 shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix); 0375 if (traits & ShaderTrait::Modulate) { 0376 shader->setUniform(GLShader::Vec4Uniform::ModulationConstant, modulate(renderNode.opacity, data.brightness())); 0377 } 0378 if (traits & ShaderTrait::TransformColorspace) { 0379 shader->setColorspaceUniforms(renderNode.colorDescription, renderTarget.colorDescription()); 0380 } 0381 0382 if (std::holds_alternative<GLTexture *>(renderNode.texture)) { 0383 const auto texture = std::get<GLTexture *>(renderNode.texture); 0384 glActiveTexture(GL_TEXTURE0); 0385 shader->setUniform("converter", 0); 0386 texture->bind(); 0387 } else { 0388 const auto contents = std::get<OpenGLSurfaceContents>(renderNode.texture); 0389 shader->setUniform("converter", contents.planes.count() > 1); 0390 for (int plane = 0; plane < contents.planes.count(); ++plane) { 0391 glActiveTexture(GL_TEXTURE0 + plane); 0392 contents.planes[plane]->bind(); 0393 } 0394 } 0395 0396 vbo->draw(scissorRegion, GL_TRIANGLES, renderNode.firstVertex, 0397 renderNode.vertexCount, renderContext.hardwareClipping); 0398 0399 if (std::holds_alternative<GLTexture *>(renderNode.texture)) { 0400 auto texture = std::get<GLTexture *>(renderNode.texture); 0401 glActiveTexture(GL_TEXTURE0); 0402 texture->unbind(); 0403 } else { 0404 const auto contents = std::get<OpenGLSurfaceContents>(renderNode.texture); 0405 for (int plane = 0; plane < contents.planes.count(); ++plane) { 0406 glActiveTexture(GL_TEXTURE0 + plane); 0407 contents.planes[plane]->unbind(); 0408 } 0409 } 0410 } 0411 if (shader) { 0412 ShaderManager::instance()->popShader(); 0413 } 0414 0415 if (m_debug.fractionalEnabled) { 0416 visualizeFractional(viewport, scissorRegion, renderContext); 0417 } 0418 0419 vbo->unbindArrays(); 0420 0421 setBlendEnabled(false); 0422 0423 if (renderContext.hardwareClipping) { 0424 glDisable(GL_SCISSOR_TEST); 0425 } 0426 } 0427 0428 void ItemRendererOpenGL::visualizeFractional(const RenderViewport &viewport, const QRegion ®ion, const RenderContext &renderContext) 0429 { 0430 if (!m_debug.fractionalShader) { 0431 m_debug.fractionalShader = ShaderManager::instance()->generateShaderFromFile( 0432 ShaderTrait::MapTexture, 0433 QStringLiteral(":/scene/shaders/debug_fractional.vert"), 0434 QStringLiteral(":/scene/shaders/debug_fractional.frag")); 0435 } 0436 0437 if (!m_debug.fractionalShader) { 0438 return; 0439 } 0440 0441 ShaderBinder debugShaderBinder(m_debug.fractionalShader.get()); 0442 m_debug.fractionalShader->setUniform("fractionalPrecision", 0.01f); 0443 0444 auto screenSize = viewport.renderRect().size() * viewport.scale(); 0445 m_debug.fractionalShader->setUniform("screenSize", QVector2D(float(screenSize.width()), float(screenSize.height()))); 0446 0447 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); 0448 0449 for (int i = 0; i < renderContext.renderNodes.count(); i++) { 0450 const RenderNode &renderNode = renderContext.renderNodes[i]; 0451 if (renderNode.vertexCount == 0) { 0452 continue; 0453 } 0454 0455 setBlendEnabled(true); 0456 0457 QVector2D size; 0458 if (std::holds_alternative<GLTexture *>(renderNode.texture)) { 0459 auto texture = std::get<GLTexture *>(renderNode.texture); 0460 size = QVector2D(texture->width(), texture->height()); 0461 } else { 0462 auto texture = std::get<OpenGLSurfaceContents>(renderNode.texture).planes.constFirst().get(); 0463 size = QVector2D(texture->width(), texture->height()); 0464 } 0465 0466 m_debug.fractionalShader->setUniform("geometrySize", size); 0467 m_debug.fractionalShader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, renderContext.projectionMatrix * renderNode.transformMatrix); 0468 0469 vbo->draw(region, GL_TRIANGLES, renderNode.firstVertex, 0470 renderNode.vertexCount, renderContext.hardwareClipping); 0471 } 0472 } 0473 0474 } // namespace KWin