backends/drm: test direct scanout with the same code as presentation

This means that we prefer direct scanout over a specific presentation mode (tearing),
which usually just means we first engage direct scanout and program the relevant
properties, and then switch to tearing afterwards.
It also removes a hack for direct scanout with legacy, and is one step less for
implementing overlay plane support.
This commit is contained in:
Xaver Hugl 2024-07-06 15:14:25 +02:00
parent df184ebd11
commit 7ab825cba1
33 changed files with 94 additions and 82 deletions

View file

@ -31,7 +31,6 @@ void DrmAbstractOutput::updateEnabled(bool enabled)
next.enabled = enabled; next.enabled = enabled;
setState(next); setState(next);
} }
} }
#include "moc_drm_abstract_output.cpp" #include "moc_drm_abstract_output.cpp"

View file

@ -141,9 +141,16 @@ DrmDevice *EglGbmBackend::drmDevice() const
return gpu()->drmDevice(); return gpu()->drmDevice();
} }
void EglGbmBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool EglGbmBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
static_cast<DrmAbstractOutput *>(output)->present(frame); return static_cast<DrmAbstractOutput *>(output)->present(frame);
}
void EglGbmBackend::repairPresentation(Output *output)
{
// read back drm properties, most likely our info is out of date somehow
// or we need a modeset
QTimer::singleShot(0, m_backend, &DrmBackend::updateOutputs);
} }
OutputLayer *EglGbmBackend::primaryLayer(Output *output) OutputLayer *EglGbmBackend::primaryLayer(Output *output)

View file

@ -46,7 +46,8 @@ public:
DrmDevice *drmDevice() const override; DrmDevice *drmDevice() const override;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
void repairPresentation(Output *output) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -86,13 +86,18 @@ ColorDescription EglGbmLayer::colorDescription() const
return m_surface.colorDescription(); return m_surface.colorDescription();
} }
bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) bool EglGbmLayer::doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame)
{ {
static bool valid; static bool valid;
static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;
if (directScanoutDisabled) { if (directScanoutDisabled) {
return false; return false;
} }
if (m_pipeline->gpu()->needsModeset()) {
// don't do direct scanout with modeset, it might lead to locking
// the hardware to some buffer format we can't switch away from
return false;
}
if (m_pipeline->output()->colorProfileSource() == Output::ColorProfileSource::ICC && !m_pipeline->output()->highDynamicRange() && m_pipeline->iccProfile()) { if (m_pipeline->output()->colorProfileSource() == Output::ColorProfileSource::ICC && !m_pipeline->output()->highDynamicRange() && m_pipeline->iccProfile()) {
// TODO make the icc profile output a color pipeline too? // TODO make the icc profile output a color pipeline too?
return false; return false;
@ -119,13 +124,10 @@ bool EglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescriptio
return false; return false;
} }
m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{}); m_scanoutBuffer = m_pipeline->gpu()->importBuffer(buffer, FileDescriptor{});
if (m_scanoutBuffer && m_pipeline->testScanout(frame)) { if (m_scanoutBuffer) {
m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone m_surface.forgetDamage(); // TODO: Use absolute frame sequence numbers for indexing the DamageJournal. It's more flexible and less error-prone
return true;
} else {
m_scanoutBuffer.reset();
return false;
} }
return m_scanoutBuffer != nullptr;
} }
std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const std::shared_ptr<DrmFramebuffer> EglGbmLayer::currentBuffer() const

View file

@ -40,7 +40,7 @@ public:
const ColorPipeline &colorPipeline() const override; const ColorPipeline &colorPipeline() const override;
private: private:
bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override; bool doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override;
std::shared_ptr<DrmFramebuffer> m_scanoutBuffer; std::shared_ptr<DrmFramebuffer> m_scanoutBuffer;
ColorPipeline m_colorPipeline; ColorPipeline m_colorPipeline;

View file

@ -302,18 +302,12 @@ bool DrmOutput::present(const std::shared_ptr<OutputFrame> &frame)
err = m_pipeline->present(frame); err = m_pipeline->present(frame);
} }
success = err == DrmPipeline::Error::None; success = err == DrmPipeline::Error::None;
if (err == DrmPipeline::Error::InvalidArguments) {
QTimer::singleShot(0, m_gpu->platform(), &DrmBackend::updateOutputs);
}
} }
m_renderLoop->setPresentationMode(m_pipeline->presentationMode()); m_renderLoop->setPresentationMode(m_pipeline->presentationMode());
if (success) { if (success) {
Q_EMIT outputChange(frame->damage()); Q_EMIT outputChange(frame->damage());
return true;
} else if (!needsModeset) {
qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno);
} }
return false; return success;
} }
DrmConnector *DrmOutput::connector() const DrmConnector *DrmOutput::connector() const

View file

@ -51,28 +51,6 @@ DrmPipeline::~DrmPipeline()
} }
} }
bool DrmPipeline::testScanout(const std::shared_ptr<OutputFrame> &frame)
{
if (gpu()->needsModeset()) {
return false;
}
if (gpu()->atomicModeSetting()) {
return DrmPipeline::commitPipelinesAtomic({this}, CommitMode::Test, frame, {}) == Error::None;
} else {
if (m_primaryLayer->currentBuffer()->buffer()->size() != m_pending.mode->size()) {
// scaling isn't supported with the legacy API
return false;
}
// no other way to test than to do it.
// As we only have a maximum of one test per scanout cycle, this is fine
const bool ret = presentLegacy(frame) == Error::None;
if (ret) {
m_didLegacyScanoutHack = true;
}
return ret;
}
}
DrmPipeline::Error DrmPipeline::present(const std::shared_ptr<OutputFrame> &frame) DrmPipeline::Error DrmPipeline::present(const std::shared_ptr<OutputFrame> &frame)
{ {
Q_ASSERT(m_pending.crtc); Q_ASSERT(m_pending.crtc);
@ -93,11 +71,6 @@ DrmPipeline::Error DrmPipeline::present(const std::shared_ptr<OutputFrame> &fram
m_commitThread->addCommit(std::move(primaryPlaneUpdate)); m_commitThread->addCommit(std::move(primaryPlaneUpdate));
return Error::None; return Error::None;
} else { } else {
if (m_didLegacyScanoutHack) {
// already presented
m_didLegacyScanoutHack = false;
return Error::None;
}
return presentLegacy(frame); return presentLegacy(frame);
} }
} }

View file

@ -57,7 +57,6 @@ public:
* if the test fails, there is a guarantee for no lasting changes * if the test fails, there is a guarantee for no lasting changes
*/ */
Error present(const std::shared_ptr<OutputFrame> &frame); Error present(const std::shared_ptr<OutputFrame> &frame);
bool testScanout(const std::shared_ptr<OutputFrame> &frame);
bool maybeModeset(const std::shared_ptr<OutputFrame> &frame); bool maybeModeset(const std::shared_ptr<OutputFrame> &frame);
void forceLegacyModeset(); void forceLegacyModeset();
@ -150,7 +149,6 @@ private:
DrmConnector *m_connector = nullptr; DrmConnector *m_connector = nullptr;
bool m_modesetPresentPending = false; bool m_modesetPresentPending = false;
bool m_didLegacyScanoutHack = false;
ColorPipeline m_currentLegacyGamma; ColorPipeline m_currentLegacyGamma;
struct State struct State

View file

@ -37,9 +37,16 @@ DrmDevice *DrmQPainterBackend::drmDevice() const
return m_backend->primaryGpu()->drmDevice(); return m_backend->primaryGpu()->drmDevice();
} }
void DrmQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool DrmQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
static_cast<DrmAbstractOutput *>(output)->present(frame); return static_cast<DrmAbstractOutput *>(output)->present(frame);
}
void DrmQPainterBackend::repairPresentation(Output *output)
{
// read back drm properties, most likely our info is out of date somehow
// or we need a modeset
QTimer::singleShot(0, m_backend, &DrmBackend::updateOutputs);
} }
OutputLayer *DrmQPainterBackend::primaryLayer(Output *output) OutputLayer *DrmQPainterBackend::primaryLayer(Output *output)

View file

@ -30,7 +30,8 @@ public:
DrmDevice *drmDevice() const override; DrmDevice *drmDevice() const override;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
void repairPresentation(Output *output) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -133,7 +133,7 @@ std::shared_ptr<GLTexture> VirtualEglGbmLayer::texture() const
return nullptr; return nullptr;
} }
bool VirtualEglGbmLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) bool VirtualEglGbmLayer::doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame)
{ {
static bool valid; static bool valid;
static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid; static const bool directScanoutDisabled = qEnvironmentVariableIntValue("KWIN_DRM_NO_DIRECT_SCANOUT", &valid) == 1 && valid;

View file

@ -44,7 +44,7 @@ public:
const ColorDescription &colorDescription() const; const ColorDescription &colorDescription() const;
private: private:
bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override; bool doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override;
std::shared_ptr<EglSwapchain> createGbmSwapchain() const; std::shared_ptr<EglSwapchain> createGbmSwapchain() const;
bool doesGbmSwapchainFit(EglSwapchain *swapchain) const; bool doesGbmSwapchainFit(EglSwapchain *swapchain) const;

View file

@ -178,9 +178,10 @@ OutputLayer *VirtualEglBackend::primaryLayer(Output *output)
return m_outputs[output].get(); return m_outputs[output].get();
} }
void VirtualEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool VirtualEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
static_cast<VirtualOutput *>(output)->present(frame); static_cast<VirtualOutput *>(output)->present(frame);
return true;
} }
std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> VirtualEglBackend::textureForOutput(Output *output) const std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> VirtualEglBackend::textureForOutput(Output *output) const

View file

@ -57,7 +57,7 @@ public:
std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap) override; std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap) override;
std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> textureForOutput(Output *output) const override; std::pair<std::shared_ptr<KWin::GLTexture>, ColorDescription> textureForOutput(Output *output) const override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
void init() override; void init() override;
VirtualBackend *backend() const; VirtualBackend *backend() const;

View file

@ -100,9 +100,10 @@ GraphicsBufferAllocator *VirtualQPainterBackend::graphicsBufferAllocator() const
return m_allocator.get(); return m_allocator.get();
} }
void VirtualQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool VirtualQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
static_cast<VirtualOutput *>(output)->present(frame); static_cast<VirtualOutput *>(output)->present(frame);
return true;
} }
VirtualQPainterLayer *VirtualQPainterBackend::primaryLayer(Output *output) VirtualQPainterLayer *VirtualQPainterBackend::primaryLayer(Output *output)

View file

@ -55,7 +55,7 @@ public:
GraphicsBufferAllocator *graphicsBufferAllocator() const; GraphicsBufferAllocator *graphicsBufferAllocator() const;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
VirtualQPainterLayer *primaryLayer(Output *output) override; VirtualQPainterLayer *primaryLayer(Output *output) override;
private: private:

View file

@ -111,7 +111,7 @@ bool WaylandEglPrimaryLayer::doEndFrame(const QRegion &renderedRegion, const QRe
return true; return true;
} }
bool WaylandEglPrimaryLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) bool WaylandEglPrimaryLayer::doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame)
{ {
Q_ASSERT(!m_presentationBuffer); Q_ASSERT(!m_presentationBuffer);
// TODO use viewporter to relax this check // TODO use viewporter to relax this check
@ -347,11 +347,12 @@ std::unique_ptr<SurfaceTexture> WaylandEglBackend::createSurfaceTextureWayland(S
return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap); return std::make_unique<BasicEGLSurfaceTextureWayland>(this, pixmap);
} }
void WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool WaylandEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_outputs[output].primaryLayer->present(); m_outputs[output].primaryLayer->present();
static_cast<WaylandOutput *>(output)->setPendingFrame(frame); static_cast<WaylandOutput *>(output)->setPendingFrame(frame);
Q_EMIT static_cast<WaylandOutput *>(output)->outputChange(frame->damage()); Q_EMIT static_cast<WaylandOutput *>(output)->outputChange(frame->damage());
return true;
} }
OutputLayer *WaylandEglBackend::primaryLayer(Output *output) OutputLayer *WaylandEglBackend::primaryLayer(Output *output)

View file

@ -44,7 +44,7 @@ public:
std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override; std::optional<OutputLayerBeginFrameInfo> doBeginFrame() override;
bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override; bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) override;
bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override; bool doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) override;
DrmDevice *scanoutDevice() const override; DrmDevice *scanoutDevice() const override;
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override; QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
@ -104,7 +104,7 @@ public:
std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap) override; std::unique_ptr<SurfaceTexture> createSurfaceTextureWayland(SurfacePixmap *pixmap) override;
void init() override; void init() override;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -178,10 +178,11 @@ GraphicsBufferAllocator *WaylandQPainterBackend::graphicsBufferAllocator() const
return m_allocator.get(); return m_allocator.get();
} }
void WaylandQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool WaylandQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_outputs[output].primaryLayer->present(); m_outputs[output].primaryLayer->present();
static_cast<WaylandOutput *>(output)->setPendingFrame(frame); static_cast<WaylandOutput *>(output)->setPendingFrame(frame);
return true;
} }
OutputLayer *WaylandQPainterBackend::primaryLayer(Output *output) OutputLayer *WaylandQPainterBackend::primaryLayer(Output *output)

View file

@ -87,7 +87,7 @@ public:
GraphicsBufferAllocator *graphicsBufferAllocator() const; GraphicsBufferAllocator *graphicsBufferAllocator() const;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -380,7 +380,7 @@ void EglBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedR
m_lastRenderedRegion = renderedRegion; m_lastRenderedRegion = renderedRegion;
} }
void EglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool EglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_frame = frame; m_frame = frame;
// Start the software vsync monitor. There is no any reliable way to determine when // Start the software vsync monitor. There is no any reliable way to determine when
@ -403,6 +403,7 @@ void EglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &fra
if (overlayWindow() && overlayWindow()->window()) { // show the window only after the first pass, if (overlayWindow() && overlayWindow()->window()) { // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long overlayWindow()->show(); // since that pass may take long
} }
return true;
} }
void EglBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry) void EglBackend::presentSurface(EGLSurface surface, const QRegion &damage, const QRect &screenGeometry)

View file

@ -56,7 +56,7 @@ public:
std::unique_ptr<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *texture) override; std::unique_ptr<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *texture) override;
OutputLayerBeginFrameInfo beginFrame(); OutputLayerBeginFrameInfo beginFrame();
void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame); void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame);
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
OverlayWindow *overlayWindow() const override; OverlayWindow *overlayWindow() const override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
EglDisplay *eglDisplayObject() const override; EglDisplay *eglDisplayObject() const override;

View file

@ -681,7 +681,7 @@ void GlxBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedR
m_lastRenderedRegion = renderedRegion; m_lastRenderedRegion = renderedRegion;
} }
void GlxBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool GlxBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_frame = frame; m_frame = frame;
// If the GLX_INTEL_swap_event extension is not used for getting presentation feedback, // If the GLX_INTEL_swap_event extension is not used for getting presentation feedback,
@ -705,6 +705,7 @@ void GlxBackend::present(Output *output, const std::shared_ptr<OutputFrame> &fra
if (overlayWindow()->window()) { // show the window only after the first pass, if (overlayWindow()->window()) { // show the window only after the first pass,
overlayWindow()->show(); // since that pass may take long overlayWindow()->show(); // since that pass may take long
} }
return true;
} }
void GlxBackend::vblank(std::chrono::nanoseconds timestamp) void GlxBackend::vblank(std::chrono::nanoseconds timestamp)

View file

@ -85,7 +85,7 @@ public:
std::unique_ptr<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *pixmap) override; std::unique_ptr<SurfaceTexture> createSurfaceTextureX11(SurfacePixmapX11 *pixmap) override;
OutputLayerBeginFrameInfo doBeginFrame(); OutputLayerBeginFrameInfo doBeginFrame();
void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame); void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame);
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
bool makeCurrent() override; bool makeCurrent() override;
void doneCurrent() override; void doneCurrent() override;
OpenGlContext *openglContext() const override; OpenGlContext *openglContext() const override;

View file

@ -265,11 +265,12 @@ void X11WindowedEglBackend::init()
} }
} }
void X11WindowedEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool X11WindowedEglBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_outputs[output].primaryLayer->present(); m_outputs[output].primaryLayer->present();
Q_EMIT static_cast<X11WindowedOutput *>(output)->outputChange(frame->damage()); Q_EMIT static_cast<X11WindowedOutput *>(output)->outputChange(frame->damage());
static_cast<X11WindowedOutput *>(output)->framePending(frame); static_cast<X11WindowedOutput *>(output)->framePending(frame);
return true;
} }
OutputLayer *X11WindowedEglBackend::primaryLayer(Output *output) OutputLayer *X11WindowedEglBackend::primaryLayer(Output *output)

View file

@ -82,7 +82,7 @@ public:
std::pair<std::shared_ptr<GLTexture>, ColorDescription> textureForOutput(Output *output) const override; std::pair<std::shared_ptr<GLTexture>, ColorDescription> textureForOutput(Output *output) const override;
void init() override; void init() override;
void endFrame(Output *output, const QRegion &renderedRegion, const QRegion &damagedRegion); void endFrame(Output *output, const QRegion &renderedRegion, const QRegion &damagedRegion);
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -181,10 +181,11 @@ GraphicsBufferAllocator *X11WindowedQPainterBackend::graphicsBufferAllocator() c
return m_allocator.get(); return m_allocator.get();
} }
void X11WindowedQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame) bool X11WindowedQPainterBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
{ {
m_outputs[output].primaryLayer->present(); m_outputs[output].primaryLayer->present();
static_cast<X11WindowedOutput *>(output)->framePending(frame); static_cast<X11WindowedOutput *>(output)->framePending(frame);
return true;
} }
OutputLayer *X11WindowedQPainterBackend::primaryLayer(Output *output) OutputLayer *X11WindowedQPainterBackend::primaryLayer(Output *output)

View file

@ -76,7 +76,7 @@ public:
GraphicsBufferAllocator *graphicsBufferAllocator() const; GraphicsBufferAllocator *graphicsBufferAllocator() const;
void present(Output *output, const std::shared_ptr<OutputFrame> &frame) override; bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) override;
OutputLayer *primaryLayer(Output *output) override; OutputLayer *primaryLayer(Output *output) override;
OutputLayer *cursorLayer(Output *output) override; OutputLayer *cursorLayer(Output *output) override;

View file

@ -312,6 +312,7 @@ void WaylandCompositor::composite(RenderLoop *renderLoop)
renderLoop->prepareNewFrame(); renderLoop->prepareNewFrame();
auto frame = std::make_shared<OutputFrame>(renderLoop, std::chrono::nanoseconds(1'000'000'000'000 / output->refreshRate())); auto frame = std::make_shared<OutputFrame>(renderLoop, std::chrono::nanoseconds(1'000'000'000'000 / output->refreshRate()));
bool directScanout = false;
if (primaryLayer->needsRepaint() || superLayer->needsRepaint()) { if (primaryLayer->needsRepaint() || superLayer->needsRepaint()) {
auto totalTimeQuery = std::make_unique<CpuRenderTimeQuery>(); auto totalTimeQuery = std::make_unique<CpuRenderTimeQuery>();
@ -347,7 +348,16 @@ void WaylandCompositor::composite(RenderLoop *renderLoop)
} }
if (scanoutPossible) { if (scanoutPossible) {
primaryLayer->setTargetRect(centerBuffer(output->transform().map(scanoutCandidates.front()->size()), output->modeSize())); primaryLayer->setTargetRect(centerBuffer(output->transform().map(scanoutCandidates.front()->size()), output->modeSize()));
directScanout = primaryLayer->attemptScanout(scanoutCandidates.front(), frame); directScanout = primaryLayer->importScanoutBuffer(scanoutCandidates.front(), frame);
if (directScanout) {
// if present works, we don't want to touch the frame object again afterwards,
// so end the time query here instead of later
totalTimeQuery->end();
frame->addRenderTimeQuery(std::move(totalTimeQuery));
totalTimeQuery = std::make_unique<CpuRenderTimeQuery>();
directScanout &= m_backend->present(output, frame);
}
} }
} else { } else {
primaryLayer->notifyNoScanoutCandidate(); primaryLayer->notifyNoScanoutCandidate();
@ -366,11 +376,17 @@ void WaylandCompositor::composite(RenderLoop *renderLoop)
} }
postPaintPass(superLayer); postPaintPass(superLayer);
totalTimeQuery->end(); if (!directScanout) {
frame->addRenderTimeQuery(std::move(totalTimeQuery)); totalTimeQuery->end();
frame->addRenderTimeQuery(std::move(totalTimeQuery));
}
} }
m_backend->present(output, frame); if (!directScanout) {
if (!m_backend->present(output, frame)) {
m_backend->repairPresentation(output);
}
}
framePass(superLayer, frame.get()); framePass(superLayer, frame.get());

View file

@ -62,12 +62,12 @@ bool OutputLayer::needsRepaint() const
return !m_repaints.isEmpty(); return !m_repaints.isEmpty();
} }
bool OutputLayer::doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame) bool OutputLayer::doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame)
{ {
return false; return false;
} }
bool OutputLayer::attemptScanout(SurfaceItem *surfaceItem, const std::shared_ptr<OutputFrame> &frame) bool OutputLayer::importScanoutBuffer(SurfaceItem *surfaceItem, const std::shared_ptr<OutputFrame> &frame)
{ {
SurfaceItemWayland *wayland = qobject_cast<SurfaceItemWayland *>(surfaceItem); SurfaceItemWayland *wayland = qobject_cast<SurfaceItemWayland *>(surfaceItem);
if (!wayland || !wayland->surface()) { if (!wayland || !wayland->surface()) {
@ -91,7 +91,7 @@ bool OutputLayer::attemptScanout(SurfaceItem *surfaceItem, const std::shared_ptr
m_bufferTransform = surfaceItem->bufferTransform(); m_bufferTransform = surfaceItem->bufferTransform();
const auto desiredTransform = m_output ? m_output->transform() : OutputTransform::Kind::Normal; const auto desiredTransform = m_output ? m_output->transform() : OutputTransform::Kind::Normal;
m_offloadTransform = m_bufferTransform.combine(desiredTransform.inverted()); m_offloadTransform = m_bufferTransform.combine(desiredTransform.inverted());
const bool ret = doAttemptScanout(buffer, surfaceItem->colorDescription(), surfaceItem->renderingIntent(), frame); const bool ret = doImportScanoutBuffer(buffer, surfaceItem->colorDescription(), surfaceItem->renderingIntent(), frame);
if (ret) { if (ret) {
surfaceItem->resetDamage(); surfaceItem->resetDamage();
// ensure the pixmap is updated when direct scanout ends // ensure the pixmap is updated when direct scanout ends

View file

@ -62,10 +62,11 @@ public:
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame); bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame);
/** /**
* Tries to import the newest buffer of the surface for direct scanout * Tries to import the newest buffer of the surface for direct scanout and does some early checks
* Returns @c true if scanout succeeds, @c false if rendering is necessary * for whether or not direct scanout *could* be successful
* A presentation request on the output must however be used afterwards to find out if it's actually successful!
*/ */
bool attemptScanout(SurfaceItem *item, const std::shared_ptr<OutputFrame> &frame); bool importScanoutBuffer(SurfaceItem *item, const std::shared_ptr<OutputFrame> &frame);
/** /**
* Notify that there's no scanout candidate this frame * Notify that there's no scanout candidate this frame
@ -95,7 +96,7 @@ public:
OutputTransform bufferTransform() const; OutputTransform bufferTransform() const;
protected: protected:
virtual bool doAttemptScanout(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame); virtual bool doImportScanoutBuffer(GraphicsBuffer *buffer, const ColorDescription &color, RenderingIntent intent, const std::shared_ptr<OutputFrame> &frame);
virtual std::optional<OutputLayerBeginFrameInfo> doBeginFrame() = 0; virtual std::optional<OutputLayerBeginFrameInfo> doBeginFrame() = 0;
virtual bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) = 0; virtual bool doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame) = 0;

View file

@ -187,6 +187,10 @@ std::unique_ptr<SurfaceTexture> RenderBackend::createSurfaceTextureWayland(Surfa
return nullptr; return nullptr;
} }
void RenderBackend::repairPresentation(Output *output)
{
}
} // namespace KWin } // namespace KWin
#include "moc_renderbackend.cpp" #include "moc_renderbackend.cpp"

View file

@ -126,7 +126,8 @@ public:
virtual OutputLayer *primaryLayer(Output *output) = 0; virtual OutputLayer *primaryLayer(Output *output) = 0;
virtual OutputLayer *cursorLayer(Output *output); virtual OutputLayer *cursorLayer(Output *output);
virtual void present(Output *output, const std::shared_ptr<OutputFrame> &frame) = 0; virtual bool present(Output *output, const std::shared_ptr<OutputFrame> &frame) = 0;
virtual void repairPresentation(Output *output);
virtual DrmDevice *drmDevice() const; virtual DrmDevice *drmDevice() const;