compositor_wayland: optimize out black background surfaces for direct scanout
If the application uses a surface with a single-pixel black buffer as a background, for example behind a video or video game, that surface can be ignored, as the KMS background is black as well. With this commit, mpv with --vo=dmabuf-wayland can get direct scanout even if the aspect ratio of the video and screen don't match, which can result in very significant power savings
This commit is contained in:
parent
70ceed51fc
commit
1f2e439b66
1 changed files with 28 additions and 4 deletions
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include "compositor_wayland.h"
|
||||
#include "core/graphicsbufferview.h"
|
||||
#include "core/output.h"
|
||||
#include "core/outputbackend.h"
|
||||
#include "core/renderbackend.h"
|
||||
|
@ -272,6 +273,25 @@ static QRect centerBuffer(const QSizeF &bufferSize, const QSize &modeSize)
|
|||
}
|
||||
}
|
||||
|
||||
static bool checkForBlackBackground(SurfaceItem *background)
|
||||
{
|
||||
if (!background->pixmap()
|
||||
|| !background->pixmap()->buffer()
|
||||
|| !background->pixmap()->buffer()->shmAttributes()
|
||||
|| background->pixmap()->buffer()->shmAttributes()->size != QSize(1, 1)) {
|
||||
return false;
|
||||
}
|
||||
const GraphicsBufferView view(background->pixmap()->buffer());
|
||||
if (!view.image()) {
|
||||
return false;
|
||||
}
|
||||
const QRgb rgb = view.image()->pixel(0, 0);
|
||||
const QVector3D encoded(qRed(rgb) / 255.0, qGreen(rgb) / 255.0, qBlue(rgb) / 255.0);
|
||||
const QVector3D nits = background->colorDescription().mapTo(encoded, ColorDescription(NamedColorimetry::BT709, TransferFunction::linear, 100, 0, std::nullopt, std::nullopt));
|
||||
// below 0.1 nits, it shouldn't be noticeable that we replace it with black
|
||||
return nits.lengthSquared() <= (0.1 * 0.1);
|
||||
}
|
||||
|
||||
void WaylandCompositor::composite(RenderLoop *renderLoop)
|
||||
{
|
||||
if (m_backend->checkGraphicsReset()) {
|
||||
|
@ -316,14 +336,18 @@ void WaylandCompositor::composite(RenderLoop *renderLoop)
|
|||
}
|
||||
|
||||
bool directScanout = false;
|
||||
if (const auto scanoutCandidates = superLayer->delegate()->scanoutCandidates(1); !scanoutCandidates.isEmpty()) {
|
||||
const uint32_t planeCount = 1;
|
||||
if (const auto scanoutCandidates = superLayer->delegate()->scanoutCandidates(planeCount + 1); !scanoutCandidates.isEmpty()) {
|
||||
const auto sublayers = superLayer->sublayers();
|
||||
const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
|
||||
bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
|
||||
return sublayer->isVisible();
|
||||
});
|
||||
if (scanoutCandidates.size() > planeCount) {
|
||||
scanoutPossible &= checkForBlackBackground(scanoutCandidates.back());
|
||||
}
|
||||
if (scanoutPossible) {
|
||||
primaryLayer->setTargetRect(centerBuffer(output->transform().map(scanoutCandidates[0]->size()), output->modeSize()));
|
||||
directScanout = primaryLayer->attemptScanout(scanoutCandidates[0], frame);
|
||||
primaryLayer->setTargetRect(centerBuffer(output->transform().map(scanoutCandidates.front()->size()), output->modeSize()));
|
||||
directScanout = primaryLayer->attemptScanout(scanoutCandidates.front(), frame);
|
||||
}
|
||||
} else {
|
||||
primaryLayer->notifyNoScanoutCandidate();
|
||||
|
|
Loading…
Reference in a new issue