screencasting: implement wayland output streaming for egl and wayland backends

master
Aleix Pol 2020-07-22 19:38:57 +02:00
parent 42e543c5b2
commit 02042908aa
5 changed files with 55 additions and 20 deletions

View File

@ -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
************************************************/

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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";
}