screencasting: implement wayland output streaming for egl and wayland backends
This commit is contained in:
parent
42e543c5b2
commit
02042908aa
5 changed files with 55 additions and 20 deletions
|
@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <kwinglplatform.h>
|
||||
// Qt
|
||||
#include <QOpenGLContext>
|
||||
#include <kwineglimagetexture.h>
|
||||
// system
|
||||
#include <gbm.h>
|
||||
|
||||
|
@ -251,6 +252,7 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput)
|
|||
if (it == m_outputs.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanupOutput(*it);
|
||||
m_outputs.erase(it);
|
||||
}
|
||||
|
@ -438,17 +440,12 @@ void EglGbmBackend::present()
|
|||
// Not in use. This backend does per-screen rendering.
|
||||
}
|
||||
|
||||
void EglGbmBackend::presentOnOutput(Output &output)
|
||||
void EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion)
|
||||
{
|
||||
eglSwapBuffers(eglDisplay(), output.eglSurface);
|
||||
output.buffer = m_backend->createBuffer(output.gbmSurface);
|
||||
|
||||
if(m_remoteaccessManager && gbm_surface_has_free_buffers(output.gbmSurface->surface())) {
|
||||
// GBM surface is released on page flip so
|
||||
// we should pass the buffer before it's presented.
|
||||
m_remoteaccessManager->passBuffer(output.output, output.buffer);
|
||||
}
|
||||
|
||||
Q_EMIT output.output->outputChange(damagedRegion);
|
||||
m_backend->present(output.buffer, output.output);
|
||||
|
||||
if (supportsBufferAge()) {
|
||||
|
@ -537,7 +534,7 @@ void EglGbmBackend::endRenderingFrameForScreen(int screenId,
|
|||
}
|
||||
return;
|
||||
}
|
||||
presentOnOutput(output);
|
||||
presentOnOutput(output, damagedRegion);
|
||||
|
||||
// Save the damaged region to history
|
||||
// Note: damage history is only collected for the first screen. For any other screen full
|
||||
|
@ -565,6 +562,33 @@ bool EglGbmBackend::perScreenRendering() const
|
|||
return true;
|
||||
}
|
||||
|
||||
QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstractOutput) const
|
||||
{
|
||||
const QVector<KWin::EglGbmBackend::Output>::const_iterator itOutput = std::find_if(m_outputs.begin(), m_outputs.end(),
|
||||
[abstractOutput] (const auto &output) {
|
||||
return output.output == abstractOutput;
|
||||
}
|
||||
);
|
||||
if (itOutput == m_outputs.end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
DrmOutput *drmOutput = itOutput->output;
|
||||
if (!drmOutput->hardwareTransforms()) {
|
||||
const auto glTexture = QSharedPointer<KWin::GLTexture>::create(itOutput->render.texture, GL_RGBA8, drmOutput->pixelSize());
|
||||
glTexture->setYInverted(true);
|
||||
return glTexture;
|
||||
}
|
||||
|
||||
EGLImageKHR image = eglCreateImageKHR(eglDisplay(), nullptr, EGL_NATIVE_PIXMAP_KHR, itOutput->buffer->getBo(), nullptr);
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
qCWarning(KWIN_DRM) << "Failed to record frame: Error creating EGLImageKHR - " << glGetError();
|
||||
return {};
|
||||
}
|
||||
|
||||
return QSharedPointer<EGLImageTexture>::create(eglDisplay(), image, GL_RGBA8, drmOutput->modeSize());
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* EglTexture
|
||||
************************************************/
|
||||
|
|
|
@ -28,8 +28,10 @@ struct gbm_surface;
|
|||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractOutput;
|
||||
class DrmBackend;
|
||||
class DrmBuffer;
|
||||
class DrmSurfaceBuffer;
|
||||
class DrmOutput;
|
||||
class GbmSurface;
|
||||
|
||||
|
@ -52,6 +54,8 @@ public:
|
|||
QRegion prepareRenderingForScreen(int screenId) override;
|
||||
void init() override;
|
||||
|
||||
QSharedPointer<GLTexture> textureForOutput(AbstractOutput *requestedOutput) const override;
|
||||
|
||||
protected:
|
||||
void present() override;
|
||||
void cleanupSurfaces() override;
|
||||
|
@ -63,7 +67,7 @@ private:
|
|||
void initRemotePresent();
|
||||
struct Output {
|
||||
DrmOutput *output = nullptr;
|
||||
DrmBuffer *buffer = nullptr;
|
||||
DrmSurfaceBuffer *buffer = nullptr;
|
||||
std::shared_ptr<GbmSurface> gbmSurface;
|
||||
EGLSurface eglSurface = EGL_NO_SURFACE;
|
||||
int bufferAge = 0;
|
||||
|
@ -93,7 +97,7 @@ private:
|
|||
void prepareRenderFramebuffer(const Output &output) const;
|
||||
void renderFramebufferToSurface(Output &output);
|
||||
|
||||
void presentOnOutput(Output &output);
|
||||
void presentOnOutput(Output &output, const QRegion &damagedRegion);
|
||||
|
||||
void removeOutput(DrmOutput *drmOutput);
|
||||
void cleanupOutput(Output &output);
|
||||
|
|
|
@ -32,6 +32,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "wayland_server.h"
|
||||
#include "screens.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// kwin libs
|
||||
#include <kwinglplatform.h>
|
||||
|
||||
|
@ -41,6 +44,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include <KWaylandServer/display.h>
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
namespace KWin
|
||||
|
@ -288,11 +292,11 @@ void EglWaylandBackend::present()
|
|||
{
|
||||
for (auto *output: qAsConst(m_outputs)) {
|
||||
makeContextCurrent(output);
|
||||
presentOnSurface(output);
|
||||
presentOnSurface(output, output->m_waylandOutput->geometry());
|
||||
}
|
||||
}
|
||||
|
||||
void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output)
|
||||
void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output, const QRegion &damage)
|
||||
{
|
||||
output->m_waylandOutput->surface()->setupFrameCallback();
|
||||
if (!m_swapping) {
|
||||
|
@ -300,6 +304,8 @@ void EglWaylandBackend::presentOnSurface(EglWaylandOutput *output)
|
|||
Compositor::self()->aboutToSwapBuffers();
|
||||
}
|
||||
|
||||
Q_EMIT output->m_waylandOutput->outputChange(damage);
|
||||
|
||||
if (supportsBufferAge()) {
|
||||
eglSwapBuffers(eglDisplay(), output->m_eglSurface);
|
||||
eglQuerySurface(eglDisplay(), output->m_eglSurface, EGL_BUFFER_AGE_EXT, &output->m_bufferAge);
|
||||
|
@ -363,7 +369,8 @@ void EglWaylandBackend::endRenderingFrame(const QRegion &renderedRegion, const Q
|
|||
void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion)
|
||||
{
|
||||
EglWaylandOutput *output = m_outputs[screenId];
|
||||
if (damagedRegion.intersected(output->m_waylandOutput->geometry()).isEmpty() && screenId == 0) {
|
||||
QRegion damage = damagedRegion.intersected(output->m_waylandOutput->geometry());
|
||||
if (damage.isEmpty() && screenId == 0) {
|
||||
|
||||
// If the damaged region of a window is fully occluded, the only
|
||||
// rendering done, if any, will have been to repair a reused back
|
||||
|
@ -381,7 +388,7 @@ void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion &
|
|||
}
|
||||
return;
|
||||
}
|
||||
presentOnSurface(output);
|
||||
presentOnSurface(output, damage);
|
||||
|
||||
// Save the damaged region to history
|
||||
// Note: damage history is only collected for the first screen. See EglGbmBackend
|
||||
|
@ -391,7 +398,7 @@ void EglWaylandBackend::endRenderingFrameForScreen(int screenId, const QRegion &
|
|||
output->m_damageHistory.removeLast();
|
||||
}
|
||||
|
||||
output->m_damageHistory.prepend(damagedRegion.intersected(output->m_waylandOutput->geometry()));
|
||||
output->m_damageHistory.prepend(damage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ private:
|
|||
|
||||
bool makeContextCurrent(EglWaylandOutput *output);
|
||||
void present() override;
|
||||
void presentOnSurface(EglWaylandOutput *output);
|
||||
void presentOnSurface(EglWaylandOutput *output, const QRegion &damagedRegion);
|
||||
|
||||
WaylandBackend *m_backend;
|
||||
QVector<EglWaylandOutput*> m_outputs;
|
||||
|
|
|
@ -460,13 +460,13 @@ WaylandBackend::WaylandBackend(QObject *parent)
|
|||
|
||||
|
||||
char const *drm_render_node = "/dev/dri/renderD128";
|
||||
m_drm_fd = open(drm_render_node, O_RDWR);
|
||||
if (m_drm_fd < 0) {
|
||||
m_drmFileDescriptor = open(drm_render_node, O_RDWR);
|
||||
if (m_drmFileDescriptor < 0) {
|
||||
qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to open drm render node" << drm_render_node;
|
||||
m_gbmDevice = nullptr;
|
||||
return;
|
||||
}
|
||||
m_gbmDevice = gbm_create_device(m_drm_fd);
|
||||
m_gbmDevice = gbm_create_device(m_drmFileDescriptor);
|
||||
}
|
||||
|
||||
WaylandBackend::~WaylandBackend()
|
||||
|
@ -492,7 +492,7 @@ WaylandBackend::~WaylandBackend()
|
|||
m_connectionThread->wait();
|
||||
m_connectionThreadObject->deleteLater();
|
||||
gbm_device_destroy(m_gbmDevice);
|
||||
close(m_drm_fd);
|
||||
close(m_drmFileDescriptor);
|
||||
|
||||
qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue