kwin/src/compositor.cpp

297 lines
8.2 KiB
C++
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
2023-08-31 12:23:42 +00:00
#include "compositor.h"
#include <config-kwin.h>
#include "core/output.h"
#include "core/outputlayer.h"
#include "core/renderbackend.h"
#include "core/renderlayer.h"
#include "core/renderloop.h"
2023-11-17 08:50:33 +00:00
#include "cursor.h"
#include "dbusinterface.h"
#include "ftrace.h"
#include "scene/cursorscene.h"
#include "scene/surfaceitem.h"
#include "scene/workspacescene.h"
2022-01-18 08:35:52 +00:00
#include "utils/common.h"
#include "wayland_server.h"
#include "workspace.h"
#include <KLocalizedString>
#if KWIN_BUILD_NOTIFICATIONS
#include <KNotification>
#endif
namespace KWin
{
Compositor *Compositor::s_compositor = nullptr;
Compositor *Compositor::self()
{
return s_compositor;
}
Compositor::Compositor(QObject *workspace)
: QObject(workspace)
{
// 2 sec which should be enough to restart the compositor.
static const int compositorLostMessageDelay = 2000;
m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
m_unusedSupportPropertyTimer.setSingleShot(true);
connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
this, &Compositor::deleteUnusedSupportProperties);
// Delay the call to start by one event cycle.
// The ctor of this class is invoked from the Workspace ctor, that means before
// Workspace is completely constructed, so calling Workspace::self() would result
// in undefined behavior. This is fixed by using a delayed invocation.
QTimer::singleShot(0, this, &Compositor::start);
// register DBus
new CompositorDBusInterface(this);
FTraceLogger::create();
}
Compositor::~Compositor()
{
deleteUnusedSupportProperties();
s_compositor = nullptr;
}
Output *Compositor::findOutput(RenderLoop *loop) const
{
const auto outputs = workspace()->outputs();
for (Output *output : outputs) {
if (output->renderLoop() == loop) {
return output;
}
}
return nullptr;
}
void Compositor::addSuperLayer(RenderLayer *layer)
2020-11-19 08:52:29 +00:00
{
m_superlayers.insert(layer->loop(), layer);
connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
2020-11-19 08:52:29 +00:00
}
void Compositor::removeSuperLayer(RenderLayer *layer)
2020-11-19 08:52:29 +00:00
{
m_superlayers.remove(layer->loop());
disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
delete layer;
2020-11-19 08:52:29 +00:00
}
void Compositor::keepSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties.removeAll(atom);
}
void Compositor::removeSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties << atom;
m_unusedSupportPropertyTimer.start();
}
void Compositor::deleteUnusedSupportProperties()
{
if (m_state == State::Starting || m_state == State::Stopping) {
// Currently still maybe restarting the compositor.
m_unusedSupportPropertyTimer.start();
return;
}
if (auto *con = kwinApp()->x11Connection()) {
for (const xcb_atom_t &atom : std::as_const(m_unusedSupportProperties)) {
// remove property from root window
xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
}
m_unusedSupportProperties.clear();
}
}
void Compositor::reinitialize()
{
// Restart compositing
stop();
start();
}
2020-11-19 08:52:29 +00:00
void Compositor::handleFrameRequested(RenderLoop *renderLoop)
2011-01-30 14:34:42 +00:00
{
composite(renderLoop);
}
uint Compositor::outputFormat(Output *output)
{
OutputLayer *primaryLayer = m_backend->primaryLayer(output);
return primaryLayer->format();
}
void Compositor::composite(RenderLoop *renderLoop)
{
if (m_backend->checkGraphicsReset()) {
qCDebug(KWIN_CORE) << "Graphics reset occurred";
#if KWIN_BUILD_NOTIFICATIONS
KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
#endif
reinitialize();
return;
}
Output *output = findOutput(renderLoop);
OutputLayer *primaryLayer = m_backend->primaryLayer(output);
fTraceDuration("Paint (", output->name(), ")");
RenderLayer *superLayer = m_superlayers[renderLoop];
superLayer->setOutputLayer(primaryLayer);
2020-11-19 08:52:29 +00:00
renderLoop->prepareNewFrame();
2023-10-19 14:35:14 +00:00
auto frame = std::make_shared<OutputFrame>(renderLoop);
2020-11-19 08:52:29 +00:00
if (superLayer->needsRepaint()) {
renderLoop->beginPaint();
QRegion surfaceDamage = primaryLayer->repaints();
primaryLayer->resetRepaints();
prePaintPass(superLayer, &surfaceDamage);
SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
renderLoop->setFullscreenSurface(scanoutCandidate);
output->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None);
bool directScanout = false;
if (scanoutCandidate) {
const auto sublayers = superLayer->sublayers();
const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
return sublayer->isVisible();
});
if (scanoutPossible && !output->directScanoutInhibited()) {
directScanout = primaryLayer->scanout(scanoutCandidate);
}
}
if (!directScanout) {
if (auto beginInfo = primaryLayer->beginFrame()) {
auto &[renderTarget, repaint] = beginInfo.value();
const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect().toAlignedRect());
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
paintPass(superLayer, renderTarget, bufferDamage);
primaryLayer->endFrame(bufferDamage, surfaceDamage);
}
}
postPaintPass(superLayer);
}
2023-10-19 14:35:14 +00:00
m_backend->present(output, frame);
2023-10-19 14:35:14 +00:00
framePass(superLayer, frame.get());
// TODO: Put it inside the cursor layer once the cursor layer can be backed by a real output layer.
if (waylandServer()) {
const std::chrono::milliseconds frameTime =
std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
if (!Cursors::self()->isCursorHidden()) {
Cursor *cursor = Cursors::self()->currentCursor();
if (cursor->geometry().intersects(output->geometry())) {
cursor->markAsRendered(frameTime);
}
}
}
}
2023-10-19 14:35:14 +00:00
void Compositor::framePass(RenderLayer *layer, OutputFrame *frame)
{
2023-10-19 14:35:14 +00:00
layer->delegate()->frame(frame);
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
2023-10-19 14:35:14 +00:00
framePass(sublayer, frame);
}
}
void Compositor::prePaintPass(RenderLayer *layer, QRegion *damage)
{
if (const QRegion repaints = layer->repaints(); !repaints.isEmpty()) {
*damage += layer->mapToGlobal(repaints);
layer->resetRepaints();
}
const QRegion repaints = layer->delegate()->prePaint();
if (!repaints.isEmpty()) {
*damage += layer->mapToGlobal(repaints);
}
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
prePaintPass(sublayer, damage);
}
}
}
void Compositor::postPaintPass(RenderLayer *layer)
{
layer->delegate()->postPaint();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
postPaintPass(sublayer);
}
}
}
void Compositor::paintPass(RenderLayer *layer, const RenderTarget &renderTarget, const QRegion &region)
{
layer->delegate()->paint(renderTarget, region);
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
paintPass(sublayer, renderTarget, region);
}
}
2011-01-30 14:34:42 +00:00
}
bool Compositor::isActive()
2011-01-30 14:34:42 +00:00
{
return m_state == State::On;
2011-01-30 14:34:42 +00:00
}
bool Compositor::compositingPossible() const
{
return true;
}
QString Compositor::compositingNotPossibleReason() const
{
return QString();
}
bool Compositor::openGLCompositingIsBroken() const
{
return false;
}
void Compositor::inhibit(Window *window)
{
}
void Compositor::uninhibit(Window *window)
{
}
} // namespace KWin
2023-08-31 12:23:42 +00:00
#include "moc_compositor.cpp"