diff --git a/autotests/drm/drmTest.cpp b/autotests/drm/drmTest.cpp index 8464960bfd..10df17c570 100644 --- a/autotests/drm/drmTest.cpp +++ b/autotests/drm/drmTest.cpp @@ -405,7 +405,6 @@ void DrmTest::testModeset() const auto layer = renderBackend->primaryLayer(output); layer->beginFrame(); output->renderLoop()->beginFrame(); - output->renderLoop()->endFrame(); layer->endFrame(infiniteRegion(), infiniteRegion()); QVERIFY(gpu->drmOutputs().front()->present()); diff --git a/src/backends/drm/drm_abstract_output.cpp b/src/backends/drm/drm_abstract_output.cpp index 67b981bd30..e120a59e8b 100644 --- a/src/backends/drm/drm_abstract_output.cpp +++ b/src/backends/drm/drm_abstract_output.cpp @@ -10,6 +10,7 @@ #include "core/renderloop_p.h" #include "drm_backend.h" #include "drm_gpu.h" +#include "drm_layer.h" namespace KWin { @@ -33,7 +34,8 @@ void DrmAbstractOutput::frameFailed() const void DrmAbstractOutput::pageFlipped(std::chrono::nanoseconds timestamp) const { - RenderLoopPrivate::get(m_renderLoop.get())->notifyFrameCompleted(timestamp); + const auto gpuTime = primaryLayer() ? primaryLayer()->queryRenderTime() : std::chrono::nanoseconds::zero(); + RenderLoopPrivate::get(m_renderLoop.get())->notifyFrameCompleted(timestamp, gpuTime); } DrmGpu *DrmAbstractOutput::gpu() const diff --git a/src/backends/drm/drm_egl_cursor_layer.cpp b/src/backends/drm/drm_egl_cursor_layer.cpp index 3071e00999..9da850ed49 100644 --- a/src/backends/drm/drm_egl_cursor_layer.cpp +++ b/src/backends/drm/drm_egl_cursor_layer.cpp @@ -78,4 +78,9 @@ quint32 EglGbmCursorLayer::format() const { return m_surface.currentBuffer()->buffer()->dmabufAttributes()->format; } + +std::chrono::nanoseconds EglGbmCursorLayer::queryRenderTime() const +{ + return m_surface.queryRenderTime(); +} } diff --git a/src/backends/drm/drm_egl_cursor_layer.h b/src/backends/drm/drm_egl_cursor_layer.h index c970e7ce85..71ee16ca79 100644 --- a/src/backends/drm/drm_egl_cursor_layer.h +++ b/src/backends/drm/drm_egl_cursor_layer.h @@ -35,6 +35,7 @@ public: bool checkTestBuffer() override; void releaseBuffers() override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: EglGbmLayerSurface m_surface; diff --git a/src/backends/drm/drm_egl_layer.cpp b/src/backends/drm/drm_egl_layer.cpp index 57524871a3..c26453ed87 100644 --- a/src/backends/drm/drm_egl_layer.cpp +++ b/src/backends/drm/drm_egl_layer.cpp @@ -167,4 +167,9 @@ void EglGbmLayer::releaseBuffers() m_scanoutBuffer.reset(); m_surface.destroyResources(); } + +std::chrono::nanoseconds EglGbmLayer::queryRenderTime() const +{ + return m_surface.queryRenderTime(); +} } diff --git a/src/backends/drm/drm_egl_layer.h b/src/backends/drm/drm_egl_layer.h index c6ee6a9545..f98f74f515 100644 --- a/src/backends/drm/drm_egl_layer.h +++ b/src/backends/drm/drm_egl_layer.h @@ -38,6 +38,7 @@ public: std::shared_ptr texture() const override; ColorDescription colorDescription() const; void releaseBuffers() override; + std::chrono::nanoseconds queryRenderTime() const override; private: std::shared_ptr m_scanoutBuffer; diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index 5a868fc315..a34b34b675 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -15,6 +15,7 @@ #include "drm_logging.h" #include "platformsupport/scenes/opengl/eglnativefence.h" #include "platformsupport/scenes/opengl/eglswapchain.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "platformsupport/scenes/qpainter/qpainterswapchain.h" #include "utils/drm_format_helper.h" @@ -98,6 +99,8 @@ std::optional EglGbmLayerSurface::startRendering(cons } m_surface.shadowBuffer = std::make_shared(m_surface.shadowTexture.get()); } + m_surface.renderStart = std::chrono::steady_clock::now(); + m_surface.timeQuery->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_surface.shadowBuffer.get(), m_surface.intermediaryColorDescription), .repaint = repaint, @@ -105,6 +108,8 @@ std::optional EglGbmLayerSurface::startRendering(cons } else { m_surface.shadowTexture.reset(); m_surface.shadowBuffer.reset(); + m_surface.renderStart = std::chrono::steady_clock::now(); + m_surface.timeQuery->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_surface.currentSlot->framebuffer()), .repaint = repaint, @@ -134,8 +139,10 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) } m_surface.damageJournal.add(damagedRegion); m_surface.gbmSwapchain->release(m_surface.currentSlot); + m_surface.timeQuery->end(); glFlush(); const auto buffer = importBuffer(m_surface, m_surface.currentSlot.get()); + m_surface.renderEnd = std::chrono::steady_clock::now(); if (buffer) { m_surface.currentFramebuffer = buffer; return true; @@ -144,6 +151,21 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) } } +std::chrono::nanoseconds EglGbmLayerSurface::queryRenderTime() const +{ + const auto cpuTime = m_surface.renderEnd - m_surface.renderStart; + if (m_surface.timeQuery) { + m_eglBackend->makeCurrent(); + auto gpuTime = m_surface.timeQuery->result(); + if (m_surface.importTimeQuery && m_eglBackend->contextForGpu(m_gpu)->makeCurrent()) { + gpuTime += m_surface.importTimeQuery->result(); + } + return std::max(gpuTime, cpuTime); + } else { + return cpuTime; + } +} + EglGbmBackend *EglGbmLayerSurface::eglBackend() const { return m_eglBackend; @@ -320,7 +342,9 @@ std::optional EglGbmLayerSurface::createSurface(con if (!ret.importGbmSwapchain) { return std::nullopt; } + ret.importTimeQuery = std::make_shared(); } + ret.timeQuery = std::make_shared(); if (!doRenderTestBuffer(ret)) { return std::nullopt; } @@ -402,6 +426,7 @@ std::shared_ptr EglGbmLayerSurface::importWithEgl(Surface &surfa if (!context || context->isSoftwareRenderer() || !context->makeCurrent()) { return nullptr; } + surface.importTimeQuery->begin(); if (sourceFence.isValid()) { const auto destinationFence = EGLNativeFence::importFence(context->displayObject(), sourceFence.fileDescriptor().duplicate()); @@ -441,9 +466,9 @@ std::shared_ptr EglGbmLayerSurface::importWithEgl(Surface &surfa context->shaderManager()->popShader(); glFlush(); surface.importGbmSwapchain->release(slot); + surface.importTimeQuery->end(); // restore the old context - context->doneCurrent(); m_eglBackend->makeCurrent(); return m_gpu->importBuffer(slot->buffer()); } diff --git a/src/backends/drm/drm_egl_layer_surface.h b/src/backends/drm/drm_egl_layer_surface.h index 241bce1720..7b96fe3ede 100644 --- a/src/backends/drm/drm_egl_layer_surface.h +++ b/src/backends/drm/drm_egl_layer_surface.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "core/outputlayer.h" @@ -34,6 +35,7 @@ class EglGbmBackend; class GraphicsBuffer; class SurfaceItem; class GLTexture; +class GLRenderTimeQuery; class EglGbmLayerSurface : public QObject { @@ -53,6 +55,7 @@ public: std::optional startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, bool enableColormanagement); bool endRendering(const QRegion &damagedRegion); + std::chrono::nanoseconds queryRenderTime() const; bool doesSurfaceFit(const QSize &size, const QMap> &formats) const; std::shared_ptr texture() const; @@ -89,6 +92,12 @@ private: MultiGpuImportMode importMode; std::shared_ptr currentFramebuffer; bool forceLinear = false; + + // for render timing + std::shared_ptr timeQuery; + std::shared_ptr importTimeQuery; + std::chrono::steady_clock::time_point renderStart; + std::chrono::steady_clock::time_point renderEnd; }; bool checkSurface(const QSize &size, const QMap> &formats); bool doesSurfaceFit(const Surface &surface, const QSize &size, const QMap> &formats) const; diff --git a/src/backends/drm/drm_qpainter_layer.cpp b/src/backends/drm/drm_qpainter_layer.cpp index a053b66a4d..87eca32f3c 100644 --- a/src/backends/drm/drm_qpainter_layer.cpp +++ b/src/backends/drm/drm_qpainter_layer.cpp @@ -38,6 +38,7 @@ std::optional DrmQPainterLayer::beginFrame() return std::nullopt; } + m_renderStart = std::chrono::steady_clock::now(); const QRegion repaint = m_damageJournal.accumulate(m_currentBuffer->age(), infiniteRegion()); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_currentBuffer->view()->image()), @@ -47,6 +48,7 @@ std::optional DrmQPainterLayer::beginFrame() bool DrmQPainterLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; m_currentFramebuffer = m_pipeline->gpu()->importBuffer(m_currentBuffer->buffer()); m_damageJournal.add(damagedRegion); m_swapchain->release(m_currentBuffer); @@ -99,6 +101,11 @@ quint32 DrmQPainterLayer::format() const return DRM_FORMAT_XRGB8888; } +std::chrono::nanoseconds DrmQPainterLayer::queryRenderTime() const +{ + return m_renderTime; +} + DrmCursorQPainterLayer::DrmCursorQPainterLayer(DrmPipeline *pipeline) : DrmOverlayLayer(pipeline) { @@ -113,6 +120,7 @@ std::optional DrmCursorQPainterLayer::beginFrame() if (!m_currentBuffer) { return std::nullopt; } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_currentBuffer->view()->image()), .repaint = infiniteRegion(), @@ -121,6 +129,7 @@ std::optional DrmCursorQPainterLayer::beginFrame() bool DrmCursorQPainterLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; m_currentFramebuffer = m_pipeline->gpu()->importBuffer(m_currentBuffer->buffer()); m_swapchain->release(m_currentBuffer); if (!m_currentFramebuffer) { @@ -149,6 +158,11 @@ void DrmCursorQPainterLayer::releaseBuffers() m_swapchain.reset(); } +std::chrono::nanoseconds DrmCursorQPainterLayer::queryRenderTime() const +{ + return m_renderTime; +} + DrmVirtualQPainterLayer::DrmVirtualQPainterLayer(DrmVirtualOutput *output) : m_output(output) { @@ -159,6 +173,7 @@ std::optional DrmVirtualQPainterLayer::beginFrame() if (m_image.isNull() || m_image.size() != m_output->modeSize()) { m_image = QImage(m_output->modeSize(), QImage::Format_RGB32); } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(&m_image), .repaint = QRegion(), @@ -167,6 +182,7 @@ std::optional DrmVirtualQPainterLayer::beginFrame() bool DrmVirtualQPainterLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; m_currentDamage = damagedRegion; return true; } @@ -179,4 +195,9 @@ QRegion DrmVirtualQPainterLayer::currentDamage() const void DrmVirtualQPainterLayer::releaseBuffers() { } + +std::chrono::nanoseconds DrmVirtualQPainterLayer::queryRenderTime() const +{ + return m_renderTime; +} } diff --git a/src/backends/drm/drm_qpainter_layer.h b/src/backends/drm/drm_qpainter_layer.h index 29b9847992..e91ab27e9e 100644 --- a/src/backends/drm/drm_qpainter_layer.h +++ b/src/backends/drm/drm_qpainter_layer.h @@ -34,6 +34,7 @@ public: QRegion currentDamage() const override; void releaseBuffers() override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: bool doesSwapchainFit() const; @@ -42,6 +43,8 @@ private: std::shared_ptr m_currentBuffer; std::shared_ptr m_currentFramebuffer; DamageJournal m_damageJournal; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; class DrmCursorQPainterLayer : public DrmOverlayLayer @@ -56,11 +59,14 @@ public: std::shared_ptr currentBuffer() const override; QRegion currentDamage() const override; void releaseBuffers() override; + std::chrono::nanoseconds queryRenderTime() const override; private: std::shared_ptr m_swapchain; std::shared_ptr m_currentBuffer; std::shared_ptr m_currentFramebuffer; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; class DrmVirtualQPainterLayer : public DrmOutputLayer @@ -73,10 +79,13 @@ public: QRegion currentDamage() const override; void releaseBuffers() override; + std::chrono::nanoseconds queryRenderTime() const override; private: QImage m_image; QRegion m_currentDamage; DrmVirtualOutput *const m_output; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; } diff --git a/src/backends/drm/drm_virtual_egl_layer.cpp b/src/backends/drm/drm_virtual_egl_layer.cpp index 67a98f933f..fb55e3c3b6 100644 --- a/src/backends/drm/drm_virtual_egl_layer.cpp +++ b/src/backends/drm/drm_virtual_egl_layer.cpp @@ -12,6 +12,7 @@ #include "drm_logging.h" #include "drm_virtual_output.h" #include "platformsupport/scenes/opengl/eglswapchain.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "scene/surfaceitem_wayland.h" #include "wayland/surface_interface.h" @@ -30,6 +31,8 @@ VirtualEglGbmLayer::VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutp { } +VirtualEglGbmLayer::~VirtualEglGbmLayer() = default; + std::optional VirtualEglGbmLayer::beginFrame() { // gbm surface @@ -68,6 +71,11 @@ std::optional VirtualEglGbmLayer::beginFrame() m_scanoutBuffer = nullptr; } + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); + const QRegion repair = m_damageJournal.accumulate(slot->age(), infiniteRegion()); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(slot->framebuffer()), @@ -77,6 +85,7 @@ std::optional VirtualEglGbmLayer::beginFrame() bool VirtualEglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); glFlush(); m_currentDamage = damagedRegion; m_damageJournal.add(damagedRegion); @@ -181,4 +190,10 @@ quint32 VirtualEglGbmLayer::format() const } return m_gbmSwapchain->format(); } + +std::chrono::nanoseconds VirtualEglGbmLayer::queryRenderTime() const +{ + m_eglBackend->makeCurrent(); + return m_query->result(); +} } diff --git a/src/backends/drm/drm_virtual_egl_layer.h b/src/backends/drm/drm_virtual_egl_layer.h index efed3780bb..2e92bb0980 100644 --- a/src/backends/drm/drm_virtual_egl_layer.h +++ b/src/backends/drm/drm_virtual_egl_layer.h @@ -30,11 +30,13 @@ class GraphicsBuffer; class GLTexture; class EglGbmBackend; class DrmVirtualOutput; +class GLRenderTimeQuery; class VirtualEglGbmLayer : public DrmOutputLayer { public: VirtualEglGbmLayer(EglGbmBackend *eglBackend, DrmVirtualOutput *output); + ~VirtualEglGbmLayer() override; std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; @@ -44,6 +46,7 @@ public: std::shared_ptr texture() const override; void releaseBuffers() override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: std::shared_ptr createGbmSwapchain() const; @@ -57,6 +60,7 @@ private: std::shared_ptr m_gbmSwapchain; std::shared_ptr m_oldGbmSwapchain; std::shared_ptr m_currentSlot; + std::unique_ptr m_query; DrmVirtualOutput *const m_output; EglGbmBackend *const m_eglBackend; diff --git a/src/backends/virtual/virtual_egl_backend.cpp b/src/backends/virtual/virtual_egl_backend.cpp index 448fd9ce98..a1ff7c9116 100644 --- a/src/backends/virtual/virtual_egl_backend.cpp +++ b/src/backends/virtual/virtual_egl_backend.cpp @@ -11,6 +11,7 @@ #include "libkwineffects/kwinglutils.h" #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" #include "platformsupport/scenes/opengl/eglswapchain.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "utils/softwarevsyncmonitor.h" #include "virtual_backend.h" #include "virtual_logging.h" @@ -49,6 +50,11 @@ std::optional VirtualEglLayer::beginFrame() return std::nullopt; } + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); + return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_current->framebuffer()), .repaint = infiniteRegion(), @@ -57,6 +63,7 @@ std::optional VirtualEglLayer::beginFrame() bool VirtualEglLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); glFlush(); // flush pending rendering commands. Q_EMIT m_output->outputChange(damagedRegion); return true; @@ -67,6 +74,16 @@ quint32 VirtualEglLayer::format() const return DRM_FORMAT_XRGB8888; } +std::chrono::nanoseconds VirtualEglLayer::queryRenderTime() const +{ + if (m_query) { + m_backend->makeCurrent(); + return m_query->result(); + } else { + return std::chrono::nanoseconds::zero(); + } +} + VirtualEglBackend::VirtualEglBackend(VirtualBackend *b) : AbstractEglBackend() , m_backend(b) diff --git a/src/backends/virtual/virtual_egl_backend.h b/src/backends/virtual/virtual_egl_backend.h index 7248608967..6925ae8d70 100644 --- a/src/backends/virtual/virtual_egl_backend.h +++ b/src/backends/virtual/virtual_egl_backend.h @@ -10,6 +10,7 @@ #include "core/outputlayer.h" #include "platformsupport/scenes/opengl/abstract_egl_backend.h" +#include #include namespace KWin @@ -22,6 +23,7 @@ class VirtualBackend; class GLFramebuffer; class GLTexture; class VirtualEglBackend; +class GLRenderTimeQuery; class VirtualEglLayer : public OutputLayer { @@ -33,12 +35,14 @@ public: std::shared_ptr texture() const; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: VirtualEglBackend *const m_backend; Output *m_output; std::shared_ptr m_swapchain; std::shared_ptr m_current; + std::unique_ptr m_query; }; /** diff --git a/src/backends/virtual/virtual_output.cpp b/src/backends/virtual/virtual_output.cpp index ccc879e89a..50c81f1d0b 100644 --- a/src/backends/virtual/virtual_output.cpp +++ b/src/backends/virtual/virtual_output.cpp @@ -9,6 +9,9 @@ #include "virtual_output.h" #include "virtual_backend.h" +#include "composite.h" +#include "core/outputlayer.h" +#include "core/renderbackend.h" #include "core/renderloop_p.h" #include "utils/softwarevsyncmonitor.h" @@ -71,7 +74,7 @@ void VirtualOutput::updateEnabled(bool enabled) void VirtualOutput::vblank(std::chrono::nanoseconds timestamp) { RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop.get()); - renderLoopPrivate->notifyFrameCompleted(timestamp); + renderLoopPrivate->notifyFrameCompleted(timestamp, Compositor::self()->backend()->primaryLayer(this)->queryRenderTime()); } } diff --git a/src/backends/virtual/virtual_qpainter_backend.cpp b/src/backends/virtual/virtual_qpainter_backend.cpp index d43a4e5730..dd084bfc26 100644 --- a/src/backends/virtual/virtual_qpainter_backend.cpp +++ b/src/backends/virtual/virtual_qpainter_backend.cpp @@ -41,6 +41,7 @@ std::optional VirtualQPainterLayer::beginFrame() return std::nullopt; } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_current->view()->image()), .repaint = m_output->rect(), @@ -49,6 +50,7 @@ std::optional VirtualQPainterLayer::beginFrame() bool VirtualQPainterLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; return true; } @@ -62,6 +64,11 @@ quint32 VirtualQPainterLayer::format() const return DRM_FORMAT_XRGB8888; } +std::chrono::nanoseconds VirtualQPainterLayer::queryRenderTime() const +{ + return m_renderTime; +} + VirtualQPainterBackend::VirtualQPainterBackend(VirtualBackend *backend) : m_allocator(std::make_unique()) { diff --git a/src/backends/virtual/virtual_qpainter_backend.h b/src/backends/virtual/virtual_qpainter_backend.h index e48baf0b94..1f3f45a706 100644 --- a/src/backends/virtual/virtual_qpainter_backend.h +++ b/src/backends/virtual/virtual_qpainter_backend.h @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace KWin @@ -35,12 +36,15 @@ public: bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; QImage *image(); quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: Output *const m_output; VirtualQPainterBackend *const m_backend; std::unique_ptr m_swapchain; std::shared_ptr m_current; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime = std::chrono::nanoseconds::zero(); }; class VirtualQPainterBackend : public QPainterBackend diff --git a/src/backends/wayland/wayland_egl_backend.cpp b/src/backends/wayland/wayland_egl_backend.cpp index 0ab7884dd1..264ecff239 100644 --- a/src/backends/wayland/wayland_egl_backend.cpp +++ b/src/backends/wayland/wayland_egl_backend.cpp @@ -13,6 +13,7 @@ #include "libkwineffects/kwinglutils.h" #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" #include "platformsupport/scenes/opengl/eglswapchain.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "wayland_backend.h" #include "wayland_display.h" #include "wayland_logging.h" @@ -89,7 +90,10 @@ std::optional WaylandEglPrimaryLayer::beginFrame() if (m_backend->supportsBufferAge()) { repair = m_damageJournal.accumulate(m_buffer->age(), infiniteRegion()); } - + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_buffer->framebuffer()), .repaint = repair, @@ -98,6 +102,7 @@ std::optional WaylandEglPrimaryLayer::beginFrame() bool WaylandEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); // Flush rendering commands to the dmabuf. glFlush(); @@ -120,6 +125,17 @@ void WaylandEglPrimaryLayer::present() m_swapchain->release(m_buffer); } +quint32 WaylandEglPrimaryLayer::format() const +{ + return m_buffer->buffer()->dmabufAttributes()->format; +} + +std::chrono::nanoseconds WaylandEglPrimaryLayer::queryRenderTime() const +{ + m_backend->makeCurrent(); + return m_query->result(); +} + WaylandEglCursorLayer::WaylandEglCursorLayer(WaylandOutput *output, WaylandEglBackend *backend) : m_output(output) , m_backend(backend) @@ -160,6 +176,10 @@ std::optional WaylandEglCursorLayer::beginFrame() } m_buffer = m_swapchain->acquire(); + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_buffer->framebuffer()), .repaint = infiniteRegion(), @@ -168,6 +188,7 @@ std::optional WaylandEglCursorLayer::beginFrame() bool WaylandEglCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); // Flush rendering commands to the dmabuf. glFlush(); @@ -185,9 +206,10 @@ quint32 WaylandEglCursorLayer::format() const return m_buffer->buffer()->dmabufAttributes()->format; } -quint32 WaylandEglPrimaryLayer::format() const +std::chrono::nanoseconds WaylandEglCursorLayer::queryRenderTime() const { - return m_buffer->buffer()->dmabufAttributes()->format; + m_backend->makeCurrent(); + return m_query->result(); } WaylandEglBackend::WaylandEglBackend(WaylandBackend *b) diff --git a/src/backends/wayland/wayland_egl_backend.h b/src/backends/wayland/wayland_egl_backend.h index e585438960..f8d5b968f4 100644 --- a/src/backends/wayland/wayland_egl_backend.h +++ b/src/backends/wayland/wayland_egl_backend.h @@ -21,6 +21,7 @@ class EglSwapchainSlot; class EglSwapchain; class GLFramebuffer; class GraphicsBufferAllocator; +class GLRenderTimeQuery; namespace Wayland { @@ -41,12 +42,14 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: WaylandOutput *m_waylandOutput; DamageJournal m_damageJournal; std::shared_ptr m_swapchain; std::shared_ptr m_buffer; + std::unique_ptr m_query; WaylandEglBackend *const m_backend; friend class WaylandEglBackend; @@ -63,12 +66,14 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: WaylandOutput *m_output; WaylandEglBackend *m_backend; std::shared_ptr m_swapchain; std::shared_ptr m_buffer; + std::unique_ptr m_query; }; /** diff --git a/src/backends/wayland/wayland_output.cpp b/src/backends/wayland/wayland_output.cpp index e0ebd1fc13..3b7f875738 100644 --- a/src/backends/wayland/wayland_output.cpp +++ b/src/backends/wayland/wayland_output.cpp @@ -136,7 +136,8 @@ WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend) connect(m_surface.get(), &KWayland::Client::Surface::frameRendered, this, [this]() { RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(renderLoop()); - renderLoopPrivate->notifyFrameCompleted(std::chrono::steady_clock::now().time_since_epoch()); + const auto primary = Compositor::self()->backend()->primaryLayer(this); + renderLoopPrivate->notifyFrameCompleted(std::chrono::steady_clock::now().time_since_epoch(), primary ? primary->queryRenderTime() : std::chrono::nanoseconds::zero()); }); updateWindowTitle(); diff --git a/src/backends/wayland/wayland_qpainter_backend.cpp b/src/backends/wayland/wayland_qpainter_backend.cpp index 7d4da20a41..3ec1147459 100644 --- a/src/backends/wayland/wayland_qpainter_backend.cpp +++ b/src/backends/wayland/wayland_qpainter_backend.cpp @@ -66,6 +66,7 @@ std::optional WaylandQPainterPrimaryLayer::beginFrame return std::nullopt; } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_back->view()->image()), .repaint = accumulateDamage(m_back->age()), @@ -74,6 +75,7 @@ std::optional WaylandQPainterPrimaryLayer::beginFrame bool WaylandQPainterPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; m_damageJournal.add(damagedRegion); return true; } @@ -83,6 +85,11 @@ quint32 WaylandQPainterPrimaryLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds WaylandQPainterPrimaryLayer::queryRenderTime() const +{ + return m_renderTime; +} + WaylandQPainterCursorLayer::WaylandQPainterCursorLayer(WaylandOutput *output, WaylandQPainterBackend *backend) : m_output(output) , m_backend(backend) @@ -106,6 +113,7 @@ std::optional WaylandQPainterCursorLayer::beginFrame( return std::nullopt; } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_back->view()->image()), .repaint = infiniteRegion(), @@ -114,6 +122,7 @@ std::optional WaylandQPainterCursorLayer::beginFrame( bool WaylandQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; wl_buffer *buffer = m_output->backend()->importBuffer(m_back->buffer()); Q_ASSERT(buffer); @@ -127,6 +136,11 @@ quint32 WaylandQPainterCursorLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds WaylandQPainterCursorLayer::queryRenderTime() const +{ + return m_renderTime; +} + WaylandQPainterBackend::WaylandQPainterBackend(Wayland::WaylandBackend *b) : QPainterBackend() , m_backend(b) diff --git a/src/backends/wayland/wayland_qpainter_backend.h b/src/backends/wayland/wayland_qpainter_backend.h index ca0ad3d457..7e6cee1727 100644 --- a/src/backends/wayland/wayland_qpainter_backend.h +++ b/src/backends/wayland/wayland_qpainter_backend.h @@ -15,6 +15,7 @@ #include #include +#include namespace KWin { @@ -39,6 +40,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; void present(); @@ -51,6 +53,8 @@ private: std::unique_ptr m_swapchain; std::shared_ptr m_back; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; friend class WaylandQPainterBackend; }; @@ -66,12 +70,15 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: WaylandOutput *m_output; WaylandQPainterBackend *m_backend; std::unique_ptr m_swapchain; std::shared_ptr m_back; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; class WaylandQPainterBackend : public QPainterBackend diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp index 91f2753868..86cf69baee 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp @@ -6,11 +6,14 @@ */ #include "x11_standalone_egl_backend.h" +#include "composite.h" #include "core/outputbackend.h" +#include "core/outputlayer.h" #include "core/overlaywindow.h" #include "core/renderloop_p.h" #include "libkwineffects/kwinglplatform.h" #include "options.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "scene/surfaceitem_x11.h" #include "scene/workspacescene.h" #include "utils/c_ptr.h" @@ -47,6 +50,11 @@ uint EglLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds EglLayer::queryRenderTime() const +{ + return m_backend->queryRenderTime(); +} + EglBackend::EglBackend(Display *display, X11StandaloneBackend *backend) : m_backend(backend) , m_overlayWindow(std::make_unique()) @@ -322,6 +330,10 @@ OutputLayerBeginFrameInfo EglBackend::beginFrame() } eglWaitNative(EGL_CORE_NATIVE_ENGINE); + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_fbo.get()), .repaint = repaint, @@ -330,6 +342,7 @@ OutputLayerBeginFrameInfo EglBackend::beginFrame() void EglBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); // Save the damaged region to history if (supportsBufferAge()) { m_damageJournal.add(damagedRegion); @@ -389,10 +402,16 @@ OutputLayer *EglBackend::primaryLayer(Output *output) return m_layer.get(); } +std::chrono::nanoseconds EglBackend::queryRenderTime() +{ + makeCurrent(); + return m_query->result(); +} + void EglBackend::vblank(std::chrono::nanoseconds timestamp) { RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_backend->renderLoop()); - renderLoopPrivate->notifyFrameCompleted(timestamp); + renderLoopPrivate->notifyFrameCompleted(timestamp, m_layer->queryRenderTime()); } EglSurfaceTextureX11::EglSurfaceTextureX11(EglBackend *backend, SurfacePixmapX11 *texture) diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.h b/src/backends/x11/standalone/x11_standalone_egl_backend.h index 90736b5254..f95d5a2cb8 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.h +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.h @@ -23,6 +23,7 @@ class EglPixmapTexturePrivate; class SoftwareVsyncMonitor; class X11StandaloneBackend; class EglBackend; +class GLRenderTimeQuery; class EglLayer : public OutputLayer { @@ -32,6 +33,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; uint format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: EglBackend *const m_backend; @@ -53,6 +55,7 @@ public: void present(Output *output) override; OverlayWindow *overlayWindow() const override; OutputLayer *primaryLayer(Output *output) override; + std::chrono::nanoseconds queryRenderTime(); protected: EGLConfig chooseBufferConfig(); @@ -72,6 +75,7 @@ private: int m_bufferAge = 0; QRegion m_lastRenderedRegion; std::unique_ptr m_layer; + std::unique_ptr m_query; int m_havePostSubBuffer = false; bool m_havePlatformBase = false; }; diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp index 140864c3ab..b8081a8b48 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp @@ -28,6 +28,7 @@ #include "core/overlaywindow.h" #include "core/renderloop_p.h" #include "options.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "scene/surfaceitem_x11.h" #include "scene/workspacescene.h" #include "utils/xcbutils.h" @@ -93,7 +94,7 @@ bool SwapEventFilter::event(xcb_generic_event_t *event) const std::chrono::microseconds timestamp((uint64_t(swapEvent->ust_hi) << 32) | swapEvent->ust_lo); const auto platform = static_cast(kwinApp()->outputBackend()); - RenderLoopPrivate::get(platform->renderLoop())->notifyFrameCompleted(timestamp); + RenderLoopPrivate::get(platform->renderLoop())->notifyFrameCompleted(timestamp, Compositor::self()->backend()->primaryLayer(nullptr)->queryRenderTime()); return true; } @@ -119,6 +120,11 @@ uint GlxLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds GlxLayer::queryRenderTime() const +{ + return m_backend->queryRenderTime(); +} + GlxBackend::GlxBackend(Display *display, X11StandaloneBackend *backend) : OpenGLBackend() , m_overlayWindow(std::make_unique()) @@ -770,6 +776,9 @@ OutputLayerBeginFrameInfo GlxBackend::beginFrame() { QRegion repaint; makeCurrent(); + if (!m_query) { + m_query = std::make_unique(); + } if (supportsBufferAge()) { repaint = m_damageJournal.accumulate(m_bufferAge, infiniteRegion()); @@ -777,6 +786,10 @@ OutputLayerBeginFrameInfo GlxBackend::beginFrame() glXWaitX(); + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_fbo.get()), .repaint = repaint, @@ -785,6 +798,7 @@ OutputLayerBeginFrameInfo GlxBackend::beginFrame() void GlxBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); // Save the damaged region to history if (supportsBufferAge()) { m_damageJournal.add(damagedRegion); @@ -792,6 +806,12 @@ void GlxBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedR m_lastRenderedRegion = renderedRegion; } +std::chrono::nanoseconds GlxBackend::queryRenderTime() +{ + makeCurrent(); + return m_query->result(); +} + void GlxBackend::present(Output *output) { // If the GLX_INTEL_swap_event extension is not used for getting presentation feedback, @@ -820,7 +840,7 @@ void GlxBackend::present(Output *output) void GlxBackend::vblank(std::chrono::nanoseconds timestamp) { RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_backend->renderLoop()); - renderLoopPrivate->notifyFrameCompleted(timestamp); + renderLoopPrivate->notifyFrameCompleted(timestamp, queryRenderTime()); } bool GlxBackend::makeCurrent() diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.h b/src/backends/x11/standalone/x11_standalone_glx_backend.h index 556314531f..3558d51e57 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.h +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.h @@ -30,6 +30,7 @@ class GlxPixmapTexturePrivate; class VsyncMonitor; class X11StandaloneBackend; class GlxBackend; +class GLRenderTimeQuery; // GLX_MESA_swap_interval using glXSwapIntervalMESA_func = int (*)(unsigned int interval); @@ -66,6 +67,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; uint format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: GlxBackend *const m_backend; @@ -84,6 +86,7 @@ public: std::unique_ptr createSurfaceTextureX11(SurfacePixmapX11 *pixmap) override; OutputLayerBeginFrameInfo beginFrame(); void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion); + std::chrono::nanoseconds queryRenderTime(); void present(Output *output) override; bool makeCurrent() override; void doneCurrent() override; @@ -134,6 +137,7 @@ private: X11StandaloneBackend *m_backend; std::unique_ptr m_vsyncMonitor; std::unique_ptr m_layer; + std::unique_ptr m_query; friend class GlxPixmapTexture; }; diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp index 75c9e6730c..04a2b5c40b 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp @@ -10,6 +10,7 @@ #include "core/gbmgraphicsbufferallocator.h" #include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h" #include "platformsupport/scenes/opengl/eglswapchain.h" +#include "platformsupport/scenes/opengl/glrendertimequery.h" #include "x11_windowed_backend.h" #include "x11_windowed_logging.h" #include "x11_windowed_output.h" @@ -56,6 +57,10 @@ std::optional X11WindowedEglPrimaryLayer::beginFrame( QRegion repaint = m_output->exposedArea() + m_output->rect(); m_output->clearExposedArea(); + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_buffer->framebuffer()), .repaint = repaint, @@ -64,6 +69,7 @@ std::optional X11WindowedEglPrimaryLayer::beginFrame( bool X11WindowedEglPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_query->end(); return true; } @@ -111,6 +117,12 @@ quint32 X11WindowedEglPrimaryLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds X11WindowedEglPrimaryLayer::queryRenderTime() const +{ + m_backend->makeCurrent(); + return m_query->result(); +} + X11WindowedEglCursorLayer::X11WindowedEglCursorLayer(X11WindowedEglBackend *backend, X11WindowedOutput *output) : m_output(output) , m_backend(backend) @@ -139,7 +151,10 @@ std::optional X11WindowedEglCursorLayer::beginFrame() } m_framebuffer = std::make_unique(m_texture.get()); } - + if (!m_query) { + m_query = std::make_unique(); + } + m_query->begin(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_framebuffer.get()), .repaint = infiniteRegion(), @@ -155,6 +170,7 @@ bool X11WindowedEglCursorLayer::endFrame(const QRegion &renderedRegion, const QR GLFramebuffer::popFramebuffer(); m_output->cursor()->update(buffer.mirrored(false, true), hotspot()); + m_query->end(); return true; } @@ -164,6 +180,12 @@ quint32 X11WindowedEglCursorLayer::format() const return DRM_FORMAT_RGBA8888; } +std::chrono::nanoseconds X11WindowedEglCursorLayer::queryRenderTime() const +{ + m_backend->makeCurrent(); + return m_query->result(); +} + X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend) : m_allocator(std::make_unique(backend->gbmDevice())) , m_backend(backend) diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.h b/src/backends/x11/windowed/x11_windowed_egl_backend.h index bbb4352200..347838efdc 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.h +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.h @@ -21,6 +21,7 @@ class GraphicsBufferAllocator; class X11WindowedBackend; class X11WindowedOutput; class X11WindowedEglBackend; +class GLRenderTimeQuery; class X11WindowedEglPrimaryLayer : public OutputLayer { @@ -31,6 +32,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; std::shared_ptr texture() const; void present(); @@ -38,6 +40,7 @@ public: private: std::shared_ptr m_swapchain; std::shared_ptr m_buffer; + std::unique_ptr m_query; X11WindowedOutput *const m_output; X11WindowedEglBackend *const m_backend; }; @@ -53,12 +56,14 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: X11WindowedOutput *const m_output; X11WindowedEglBackend *const m_backend; std::unique_ptr m_framebuffer; std::unique_ptr m_texture; + std::unique_ptr m_query; }; /** diff --git a/src/backends/x11/windowed/x11_windowed_output.cpp b/src/backends/x11/windowed/x11_windowed_output.cpp index 2797ab4bc4..bd5cd983db 100644 --- a/src/backends/x11/windowed/x11_windowed_output.cpp +++ b/src/backends/x11/windowed/x11_windowed_output.cpp @@ -311,7 +311,7 @@ void X11WindowedOutput::resize(const QSize &pixelSize) void X11WindowedOutput::handlePresentCompleteNotify(xcb_present_complete_notify_event_t *event) { std::chrono::microseconds timestamp(event->ust); - RenderLoopPrivate::get(m_renderLoop.get())->notifyFrameCompleted(timestamp); + RenderLoopPrivate::get(m_renderLoop.get())->notifyFrameCompleted(timestamp, Compositor::self()->backend()->primaryLayer(this)->queryRenderTime()); } void X11WindowedOutput::handlePresentIdleNotify(xcb_present_idle_notify_event_t *event) diff --git a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp index e9016248ef..158d8b049f 100644 --- a/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_qpainter_backend.cpp @@ -47,6 +47,7 @@ std::optional X11WindowedQPainterPrimaryLayer::beginF QRegion repaint = m_output->exposedArea() + m_output->rect(); m_output->clearExposedArea(); + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(m_current->view()->image()), .repaint = repaint, @@ -55,6 +56,7 @@ std::optional X11WindowedQPainterPrimaryLayer::beginF bool X11WindowedQPainterPrimaryLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; return true; } @@ -93,6 +95,11 @@ quint32 X11WindowedQPainterPrimaryLayer::format() const return m_current->buffer()->shmAttributes()->format; } +std::chrono::nanoseconds X11WindowedQPainterPrimaryLayer::queryRenderTime() const +{ + return m_renderTime; +} + X11WindowedQPainterCursorLayer::X11WindowedQPainterCursorLayer(X11WindowedOutput *output) : m_output(output) { @@ -106,6 +113,7 @@ std::optional X11WindowedQPainterCursorLayer::beginFr m_buffer = QImage(bufferSize, QImage::Format_ARGB32_Premultiplied); } + m_renderStart = std::chrono::steady_clock::now(); return OutputLayerBeginFrameInfo{ .renderTarget = RenderTarget(&m_buffer), .repaint = infiniteRegion(), @@ -117,8 +125,14 @@ quint32 X11WindowedQPainterCursorLayer::format() const return DRM_FORMAT_ARGB8888; } +std::chrono::nanoseconds X11WindowedQPainterCursorLayer::queryRenderTime() const +{ + return m_renderTime; +} + bool X11WindowedQPainterCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { + m_renderTime = std::chrono::steady_clock::now() - m_renderStart; m_output->cursor()->update(m_buffer, hotspot()); return true; } diff --git a/src/backends/x11/windowed/x11_windowed_qpainter_backend.h b/src/backends/x11/windowed/x11_windowed_qpainter_backend.h index 8af0897833..446f7c9629 100644 --- a/src/backends/x11/windowed/x11_windowed_qpainter_backend.h +++ b/src/backends/x11/windowed/x11_windowed_qpainter_backend.h @@ -15,6 +15,7 @@ #include #include +#include #include namespace KWin @@ -36,6 +37,7 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; void present(); @@ -44,6 +46,8 @@ private: X11WindowedQPainterBackend *const m_backend; std::unique_ptr m_swapchain; std::shared_ptr m_current; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; class X11WindowedQPainterCursorLayer : public OutputLayer @@ -56,10 +60,13 @@ public: std::optional beginFrame() override; bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; quint32 format() const override; + std::chrono::nanoseconds queryRenderTime() const override; private: QImage m_buffer; X11WindowedOutput *m_output; + std::chrono::steady_clock::time_point m_renderStart; + std::chrono::nanoseconds m_renderTime; }; class X11WindowedQPainterBackend : public QPainterBackend diff --git a/src/composite.cpp b/src/composite.cpp index 3c7841d1b1..9dfd07897d 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -662,7 +662,6 @@ void Compositor::composite(RenderLoop *renderLoop) } postPaintPass(superLayer); - renderLoop->endFrame(); m_backend->present(output); diff --git a/src/core/outputlayer.h b/src/core/outputlayer.h index 3eba3fbf71..63df1defbe 100644 --- a/src/core/outputlayer.h +++ b/src/core/outputlayer.h @@ -11,6 +11,7 @@ #include #include +#include #include namespace KWin @@ -57,6 +58,11 @@ public: */ virtual bool scanout(SurfaceItem *surfaceItem); + /** + * queries the render time of the last frame. If rendering isn't complete yet, this may block until it is + */ + virtual std::chrono::nanoseconds queryRenderTime() const = 0; + private: QRegion m_repaints; QPointF m_hotspot; diff --git a/src/core/renderjournal.cpp b/src/core/renderjournal.cpp index 1189e88e45..492602e66f 100644 --- a/src/core/renderjournal.cpp +++ b/src/core/renderjournal.cpp @@ -13,18 +13,12 @@ RenderJournal::RenderJournal() { } -void RenderJournal::beginFrame() +void RenderJournal::add(std::chrono::nanoseconds renderTime) { - m_timer.start(); -} - -void RenderJournal::endFrame() -{ - std::chrono::nanoseconds duration(m_timer.nsecsElapsed()); if (m_log.count() >= m_size) { m_log.dequeue(); } - m_log.enqueue(duration); + m_log.enqueue(renderTime); } std::chrono::nanoseconds RenderJournal::minimum() const diff --git a/src/core/renderjournal.h b/src/core/renderjournal.h index cbc90379f9..dfbb059126 100644 --- a/src/core/renderjournal.h +++ b/src/core/renderjournal.h @@ -23,15 +23,7 @@ class KWIN_EXPORT RenderJournal public: RenderJournal(); - /** - * This function must be called before starting rendering a new frame. - */ - void beginFrame(); - - /** - * This function must be called after finishing rendering a frame. - */ - void endFrame(); + void add(std::chrono::nanoseconds renderTime); /** * Returns the maximum estimated amount of time that it takes to render a single frame. @@ -49,7 +41,6 @@ public: std::chrono::nanoseconds average() const; private: - QElapsedTimer m_timer; QQueue m_log; int m_size = 15; }; diff --git a/src/core/renderloop.cpp b/src/core/renderloop.cpp index f8dc4fcea3..dd1af6eaef 100644 --- a/src/core/renderloop.cpp +++ b/src/core/renderloop.cpp @@ -127,7 +127,7 @@ void RenderLoopPrivate::notifyFrameFailed() } } -void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp) +void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime) { Q_ASSERT(pendingFrameCount > 0); pendingFrameCount--; @@ -142,6 +142,7 @@ void RenderLoopPrivate::notifyFrameCompleted(std::chrono::nanoseconds timestamp) lastPresentationTimestamp = std::chrono::steady_clock::now().time_since_epoch(); } + renderJournal.add(renderTime); if (!inhibitCount) { maybeScheduleRepaint(); } @@ -201,12 +202,6 @@ void RenderLoop::beginFrame() { d->pendingRepaint = false; d->pendingFrameCount++; - d->renderJournal.beginFrame(); -} - -void RenderLoop::endFrame() -{ - d->renderJournal.endFrame(); } int RenderLoop::refreshRate() const diff --git a/src/core/renderloop.h b/src/core/renderloop.h index b0761eba0d..f092fc66b7 100644 --- a/src/core/renderloop.h +++ b/src/core/renderloop.h @@ -52,12 +52,6 @@ public: */ void beginFrame(); - /** - * This function must be called after the Compositor has finished rendering the - * next frame. - */ - void endFrame(); - /** * Returns the refresh rate at which the output is being updated, in millihertz. */ diff --git a/src/core/renderloop_p.h b/src/core/renderloop_p.h index 5dafe510a3..bdff3185ea 100644 --- a/src/core/renderloop_p.h +++ b/src/core/renderloop_p.h @@ -30,7 +30,7 @@ public: void maybeScheduleRepaint(); void notifyFrameFailed(); - void notifyFrameCompleted(std::chrono::nanoseconds timestamp); + void notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime); RenderLoop *q; std::chrono::nanoseconds lastPresentationTimestamp = std::chrono::nanoseconds::zero();