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:
parent
df184ebd11
commit
7ab825cba1
33 changed files with 94 additions and 82 deletions
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue