Changeset View
Standalone View
plugins/scenes/opengl/scene_opengl.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> | 5 | Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> | ||
6 | Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> | 6 | Copyright (C) 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> | ||
7 | Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com> | ||||
7 | 8 | | |||
8 | Based on glcompmgr code by Felix Bellaby. | 9 | Based on glcompmgr code by Felix Bellaby. | ||
9 | Using code from Compiz and Beryl. | 10 | Using code from Compiz and Beryl. | ||
10 | 11 | | |||
11 | Explicit command stream synchronization based on the sample | 12 | Explicit command stream synchronization based on the sample | ||
12 | implementation by James Jones <jajones@nvidia.com>, | 13 | implementation by James Jones <jajones@nvidia.com>, | ||
13 | 14 | | |||
14 | Copyright © 2011 NVIDIA Corporation | 15 | Copyright © 2011 NVIDIA Corporation | ||
▲ Show 20 Lines • Show All 2498 Lines • ▼ Show 20 Line(s) | 2513 | for (int y = 0; y < image.height(); y++) { | |||
2513 | *d = s[y]; | 2514 | *d = s[y]; | ||
2514 | d += image.width(); | 2515 | d += image.width(); | ||
2515 | } | 2516 | } | ||
2516 | } | 2517 | } | ||
2517 | 2518 | | |||
2518 | return image; | 2519 | return image; | ||
2519 | } | 2520 | } | ||
2520 | 2521 | | |||
2522 | static void clamp_row(int left, int width, int right, const uint32_t *src, uint32_t *dest) | ||||
2523 | { | ||||
2524 | std::fill_n(dest, left, *src); | ||||
2525 | std::copy(src, src + width, dest + left); | ||||
2526 | std::fill_n(dest + left + width, right, *(src + width - 1)); | ||||
2527 | } | ||||
2528 | | ||||
2529 | static void clamp_sides(int left, int width, int right, const uint32_t *src, uint32_t *dest) | ||||
2530 | { | ||||
2531 | std::fill_n(dest, left, *src); | ||||
2532 | std::fill_n(dest + left + width, right, *(src + width - 1)); | ||||
2533 | } | ||||
2534 | | ||||
2535 | static void clamp(QImage &image, const QRect &viewport) | ||||
2536 | { | ||||
2537 | Q_ASSERT(image.depth() == 32); | ||||
2538 | | ||||
2539 | const QRect rect = image.rect(); | ||||
2540 | | ||||
2541 | const int left = viewport.left() - rect.left(); | ||||
2542 | const int top = viewport.top() - rect.top(); | ||||
2543 | const int right = rect.right() - viewport.right(); | ||||
2544 | const int bottom = rect.bottom() - viewport.bottom(); | ||||
2545 | | ||||
2546 | const int width = rect.width() - left - right; | ||||
2547 | const int height = rect.height() - top - bottom; | ||||
2548 | | ||||
2549 | const uint32_t *firstRow = reinterpret_cast<uint32_t *>(image.scanLine(top)); | ||||
2550 | const uint32_t *lastRow = reinterpret_cast<uint32_t *>(image.scanLine(top + height - 1)); | ||||
2551 | | ||||
2552 | for (int i = 0; i < top; ++i) { | ||||
2553 | uint32_t *dest = reinterpret_cast<uint32_t *>(image.scanLine(i)); | ||||
2554 | clamp_row(left, width, right, firstRow + left, dest); | ||||
2555 | } | ||||
2556 | | ||||
2557 | for (int i = 0; i < height; ++i) { | ||||
2558 | uint32_t *dest = reinterpret_cast<uint32_t *>(image.scanLine(top + i)); | ||||
2559 | clamp_sides(left, width, right, dest + left, dest); | ||||
2560 | } | ||||
2561 | | ||||
2562 | for (int i = 0; i < bottom; ++i) { | ||||
2563 | uint32_t *dest = reinterpret_cast<uint32_t *>(image.scanLine(top + height + i)); | ||||
2564 | clamp_row(left, width, right, lastRow + left, dest); | ||||
2565 | } | ||||
2566 | } | ||||
2567 | | ||||
2521 | void SceneOpenGLDecorationRenderer::render() | 2568 | void SceneOpenGLDecorationRenderer::render() | ||
2522 | { | 2569 | { | ||
2523 | const QRegion scheduled = getScheduled(); | 2570 | const QRegion scheduled = getScheduled(); | ||
2524 | const bool dirty = areImageSizesDirty(); | 2571 | const bool dirty = areImageSizesDirty(); | ||
2525 | if (scheduled.isEmpty() && !dirty) { | 2572 | if (scheduled.isEmpty() && !dirty) { | ||
2526 | return; | 2573 | return; | ||
2527 | } | 2574 | } | ||
2528 | if (dirty) { | 2575 | if (dirty) { | ||
2529 | resizeTexture(); | 2576 | resizeTexture(); | ||
2530 | resetImageSizesDirty(); | 2577 | resetImageSizesDirty(); | ||
2531 | } | 2578 | } | ||
2532 | 2579 | | |||
2533 | if (!m_texture) { | 2580 | if (!m_texture) { | ||
2534 | // for invalid sizes we get no texture, see BUG 361551 | 2581 | // for invalid sizes we get no texture, see BUG 361551 | ||
2535 | return; | 2582 | return; | ||
2536 | } | 2583 | } | ||
2537 | 2584 | | |||
2538 | QRect left, top, right, bottom; | 2585 | QRect left, top, right, bottom; | ||
2539 | client()->client()->layoutDecorationRects(left, top, right, bottom); | 2586 | client()->client()->layoutDecorationRects(left, top, right, bottom); | ||
2540 | 2587 | | |||
2541 | const QRect geometry = dirty ? QRect(QPoint(0, 0), client()->client()->size()) : scheduled.boundingRect(); | 2588 | const QRect geometry = dirty ? QRect(QPoint(0, 0), client()->client()->size()) : scheduled.boundingRect(); | ||
2542 | 2589 | | |||
2543 | auto renderPart = [this](const QRect &geo, const QRect &partRect, const QPoint &offset, bool rotated = false) { | 2590 | // We pad each part in the decoration atlas in order to avoid texture bleeding. | ||
2591 | const int padding = 1; | ||||
2592 | | ||||
2593 | auto renderPart = [=](const QRect &geo, const QRect &partRect, const QPoint &position, bool rotated = false) { | ||||
2544 | if (!geo.isValid()) { | 2594 | if (!geo.isValid()) { | ||
2545 | return; | 2595 | return; | ||
2546 | } | 2596 | } | ||
2547 | QImage image = renderToImage(geo); | 2597 | | ||
2598 | QRect rect = geo; | ||||
2599 | | ||||
2600 | // We allow partial decoration updates and it might just so happen that the dirty region | ||||
2601 | // is completely contained inside the decoration part, i.e. the dirty region doesn't touch | ||||
2602 | // any of the decoration's edges. In that case, we should **not** pad the dirty region. | ||||
2603 | if (rect.left() == partRect.left()) { | ||||
2604 | rect.setLeft(rect.left() - padding); | ||||
2605 | } | ||||
2606 | if (rect.top() == partRect.top()) { | ||||
2607 | rect.setTop(rect.top() - padding); | ||||
2608 | } | ||||
2609 | if (rect.right() == partRect.right()) { | ||||
2610 | rect.setRight(rect.right() + padding); | ||||
2611 | } | ||||
2612 | if (rect.bottom() == partRect.bottom()) { | ||||
2613 | rect.setBottom(rect.bottom() + padding); | ||||
2614 | } | ||||
2615 | | ||||
2616 | QRect viewport = geo.translated(-rect.x(), -rect.y()); | ||||
2617 | const qreal devicePixelRatio = client()->client()->screenScale(); | ||||
2618 | | ||||
2619 | QImage image(rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); | ||||
2620 | image.setDevicePixelRatio(devicePixelRatio); | ||||
2621 | image.fill(Qt::transparent); | ||||
2622 | | ||||
2623 | QPainter painter(&image); | ||||
2624 | painter.setRenderHint(QPainter::Antialiasing); | ||||
2625 | painter.setViewport(QRect(viewport.topLeft(), viewport.size() * devicePixelRatio)); | ||||
davidedmundson: QRect point should also be *= devicePixelRatio? | |||||
zzag: No, since it must be in device-independent pixels. | |||||
2626 | painter.setWindow(QRect(geo.topLeft(), geo.size() * devicePixelRatio)); | ||||
2627 | painter.setClipRect(geo); | ||||
2628 | renderToPainter(&painter, geo); | ||||
2629 | painter.end(); | ||||
2630 | | ||||
2631 | clamp(image, QRect(viewport.topLeft(), viewport.size() * devicePixelRatio)); | ||||
2632 | | ||||
2548 | if (rotated) { | 2633 | if (rotated) { | ||
2549 | // TODO: get this done directly when rendering to the image | 2634 | // TODO: get this done directly when rendering to the image | ||
2550 | image = rotate(image, QRect(geo.topLeft() - partRect.topLeft(), geo.size())); | 2635 | image = rotate(image, QRect(QPoint(), rect.size())); | ||
2636 | viewport = QRect(viewport.y(), viewport.x(), viewport.height(), viewport.width()); | ||||
2551 | } | 2637 | } | ||
2552 | m_texture->update(image, (geo.topLeft() - partRect.topLeft() + offset) * image.devicePixelRatio()); | 2638 | | ||
2639 | const QPoint dirtyOffset = geo.topLeft() - partRect.topLeft(); | ||||
fredrik: How does this perform compared to the rotate() function? | |||||
Both QPainter::rotate() and QPainter::translate() operate on the world transform matrix. Hence, performance should be the same. Would you prefer to use the rotate() function instead? zzag: Both QPainter::rotate() and QPainter::translate() operate on the world transform matrix. Hence… | |||||
It seems like I misunderstood your question. To be honest, I didn't do benchmarks. I saw a todo comment and decided to fix it. I will revert this change. zzag: It seems like I misunderstood your question. To be honest, I didn't do benchmarks. I saw a todo… | |||||
Back to the question: I expect performance to be the same if the decoration theme fills borders with some solid color. If it layers multiple gradients, then I expect performance to go down due to cache misses. But the only way to find whether that's true is to do lots of benchmarks... zzag: Back to the question: I expect performance to be the same if the decoration theme fills borders… | |||||
2640 | m_texture->update(image, (position + dirtyOffset - viewport.topLeft()) * image.devicePixelRatio()); | ||||
2553 | }; | 2641 | }; | ||
2554 | renderPart(left.intersected(geometry), left, QPoint(0, top.height() + bottom.height() + 2), true); | 2642 | | ||
2555 | renderPart(top.intersected(geometry), top, QPoint(0, 0)); | 2643 | const QPoint topPosition(padding, padding); | ||
2556 | renderPart(right.intersected(geometry), right, QPoint(0, top.height() + bottom.height() + left.width() + 3), true); | 2644 | const QPoint bottomPosition(padding, topPosition.y() + top.height() + 2 * padding); | ||
2557 | renderPart(bottom.intersected(geometry), bottom, QPoint(0, top.height() + 1)); | 2645 | const QPoint leftPosition(padding, bottomPosition.y() + bottom.height() + 2 * padding); | ||
2646 | const QPoint rightPosition(padding, leftPosition.y() + left.width() + 2 * padding); | ||||
2647 | | ||||
2648 | renderPart(left.intersected(geometry), left, leftPosition, true); | ||||
2649 | renderPart(top.intersected(geometry), top, topPosition); | ||||
2650 | renderPart(right.intersected(geometry), right, rightPosition, true); | ||||
2651 | renderPart(bottom.intersected(geometry), bottom, bottomPosition); | ||||
2558 | } | 2652 | } | ||
2559 | 2653 | | |||
2560 | static int align(int value, int align) | 2654 | static int align(int value, int align) | ||
2561 | { | 2655 | { | ||
2562 | return (value + align - 1) & ~(align - 1); | 2656 | return (value + align - 1) & ~(align - 1); | ||
2563 | } | 2657 | } | ||
2564 | 2658 | | |||
2565 | void SceneOpenGLDecorationRenderer::resizeTexture() | 2659 | void SceneOpenGLDecorationRenderer::resizeTexture() | ||
2566 | { | 2660 | { | ||
2567 | QRect left, top, right, bottom; | 2661 | QRect left, top, right, bottom; | ||
2568 | client()->client()->layoutDecorationRects(left, top, right, bottom); | 2662 | client()->client()->layoutDecorationRects(left, top, right, bottom); | ||
2569 | QSize size; | 2663 | QSize size; | ||
2570 | 2664 | | |||
2571 | size.rwidth() = qMax(qMax(top.width(), bottom.width()), | 2665 | size.rwidth() = qMax(qMax(top.width(), bottom.width()), | ||
2572 | qMax(left.height(), right.height())); | 2666 | qMax(left.height(), right.height())); | ||
2573 | size.rheight() = top.height() + bottom.height() + | 2667 | size.rheight() = top.height() + bottom.height() + | ||
2574 | left.width() + right.width() + 3; | 2668 | left.width() + right.width(); | ||
2669 | | ||||
2670 | // Reserve some space for padding. We pad decoration parts to avoid texture bleeding. | ||||
We rotate the vertical bars so everything is horizontal, so in theory we could: GL_CLAMP_S=GL_CLAMP_TO_EDGE and only pad T. Though maybe that's just making things more complicated... davidedmundson: We rotate the vertical bars so everything is horizontal, so in theory we could… | |||||
This will be the case only if all decoration parts have the same width. zzag: This will be the case only if all decoration parts have the same width. | |||||
2671 | const int padding = 1; | ||||
2672 | size.rwidth() += 2 * padding; | ||||
2673 | size.rheight() += 4 * 2 * padding; | ||||
2575 | 2674 | | |||
2576 | size.rwidth() = align(size.width(), 128); | 2675 | size.rwidth() = align(size.width(), 128); | ||
2577 | 2676 | | |||
2578 | size *= client()->client()->screenScale(); | 2677 | size *= client()->client()->screenScale(); | ||
2579 | if (m_texture && m_texture->size() == size) | 2678 | if (m_texture && m_texture->size() == size) | ||
2580 | return; | 2679 | return; | ||
2581 | 2680 | | |||
2582 | if (!size.isEmpty()) { | 2681 | if (!size.isEmpty()) { | ||
▲ Show 20 Lines • Show All 43 Lines • Show Last 20 Lines |
QRect point should also be *= devicePixelRatio?