plugins/screencast: Refactor buffer management
Use GraphicsBuffers both for dmabuf and memfd code paths.
This commit is contained in:
parent
608cc0e7f8
commit
c3c3b903b1
15 changed files with 242 additions and 278 deletions
|
@ -4,7 +4,7 @@ target_sources(screencast PRIVATE
|
|||
outputscreencastsource.cpp
|
||||
pipewirecore.cpp
|
||||
regionscreencastsource.cpp
|
||||
screencastdmabuftexture.cpp
|
||||
screencastbuffer.cpp
|
||||
screencastmanager.cpp
|
||||
screencastsource.cpp
|
||||
screencaststream.cpp
|
||||
|
|
|
@ -51,11 +51,11 @@ QSize OutputScreenCastSource::textureSize() const
|
|||
return m_output->pixelSize();
|
||||
}
|
||||
|
||||
void OutputScreenCastSource::render(spa_data *spa, spa_video_format format)
|
||||
void OutputScreenCastSource::render(QImage *target)
|
||||
{
|
||||
const auto [outputTexture, colorDescription] = Compositor::self()->scene()->textureForOutput(m_output);
|
||||
if (outputTexture) {
|
||||
grabTexture(outputTexture.get(), spa, format);
|
||||
grabTexture(outputTexture.get(), target);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
quint32 drmFormat() const override;
|
||||
|
||||
void render(GLFramebuffer *target) override;
|
||||
void render(spa_data *spa, spa_video_format format) override;
|
||||
void render(QImage *target) override;
|
||||
std::chrono::nanoseconds clock() const override;
|
||||
|
||||
void resume() override;
|
||||
|
|
|
@ -152,10 +152,10 @@ void RegionScreenCastSource::render(GLFramebuffer *target)
|
|||
GLFramebuffer::popFramebuffer();
|
||||
}
|
||||
|
||||
void RegionScreenCastSource::render(spa_data *spa, spa_video_format format)
|
||||
void RegionScreenCastSource::render(QImage *target)
|
||||
{
|
||||
ensureTexture();
|
||||
grabTexture(m_renderedTexture.get(), spa, format);
|
||||
grabTexture(m_renderedTexture.get(), target);
|
||||
}
|
||||
|
||||
uint RegionScreenCastSource::refreshRate() const
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "opengl/gltexture.h"
|
||||
#include "opengl/glutils.h"
|
||||
#include <QImage>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -44,7 +43,7 @@ public:
|
|||
uint refreshRate() const override;
|
||||
|
||||
void render(GLFramebuffer *target) override;
|
||||
void render(spa_data *spa, spa_video_format format) override;
|
||||
void render(QImage *target) override;
|
||||
std::chrono::nanoseconds clock() const override;
|
||||
|
||||
void update(Output *output, const QRegion &damage);
|
||||
|
|
121
src/plugins/screencast/screencastbuffer.cpp
Normal file
121
src/plugins/screencast/screencastbuffer.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "screencastbuffer.h"
|
||||
#include "compositor.h"
|
||||
#include "core/drmdevice.h"
|
||||
#include "core/shmgraphicsbufferallocator.h"
|
||||
#include "opengl/glframebuffer.h"
|
||||
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ScreenCastBuffer::ScreenCastBuffer(GraphicsBuffer *buffer)
|
||||
: m_buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
ScreenCastBuffer::~ScreenCastBuffer()
|
||||
{
|
||||
m_buffer->drop();
|
||||
}
|
||||
|
||||
DmaBufScreenCastBuffer::DmaBufScreenCastBuffer(GraphicsBuffer *buffer, std::shared_ptr<GLTexture> &&texture, std::unique_ptr<GLFramebuffer> &&framebuffer)
|
||||
: ScreenCastBuffer(buffer)
|
||||
, texture(std::move(texture))
|
||||
, framebuffer(std::move(framebuffer))
|
||||
{
|
||||
}
|
||||
|
||||
DmaBufScreenCastBuffer *DmaBufScreenCastBuffer::create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options)
|
||||
{
|
||||
AbstractEglBackend *backend = dynamic_cast<AbstractEglBackend *>(Compositor::self()->backend());
|
||||
if (!backend || !backend->drmDevice()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GraphicsBuffer *buffer = backend->drmDevice()->allocator()->allocate(options);
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DmaBufAttributes *attrs = buffer->dmabufAttributes();
|
||||
if (!attrs) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
backend->makeCurrent();
|
||||
|
||||
auto texture = backend->importDmaBufAsTexture(*attrs);
|
||||
if (!texture) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto framebuffer = std::make_unique<GLFramebuffer>(texture.get());
|
||||
if (!framebuffer->valid()) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct spa_data *spaData = pwBuffer->buffer->datas;
|
||||
|
||||
Q_ASSERT(pwBuffer->buffer->n_datas >= uint(attrs->planeCount));
|
||||
for (int i = 0; i < attrs->planeCount; ++i) {
|
||||
spaData[i].type = SPA_DATA_DmaBuf;
|
||||
spaData[i].flags = SPA_DATA_FLAG_READWRITE;
|
||||
spaData[i].mapoffset = 0;
|
||||
spaData[i].maxsize = i == 0 ? attrs->pitch[i] * attrs->height : 0; // TODO: dmabufs don't have a well defined size, it should be zero but some clients check the size to see if the buffer is valid
|
||||
spaData[i].fd = attrs->fd[i].get();
|
||||
spaData[i].data = nullptr;
|
||||
spaData[i].chunk->offset = attrs->offset[i];
|
||||
spaData[i].chunk->size = spaData[i].maxsize;
|
||||
spaData[i].chunk->stride = attrs->pitch[i];
|
||||
spaData[i].chunk->flags = SPA_CHUNK_FLAG_NONE;
|
||||
};
|
||||
|
||||
return new DmaBufScreenCastBuffer(buffer, std::move(texture), std::move(framebuffer));
|
||||
}
|
||||
|
||||
MemFdScreenCastBuffer::MemFdScreenCastBuffer(GraphicsBuffer *buffer, GraphicsBufferView &&view)
|
||||
: ScreenCastBuffer(buffer)
|
||||
, view(std::move(view))
|
||||
{
|
||||
}
|
||||
|
||||
MemFdScreenCastBuffer *MemFdScreenCastBuffer::create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options)
|
||||
{
|
||||
GraphicsBuffer *buffer = ShmGraphicsBufferAllocator().allocate(options);
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GraphicsBufferView view(buffer, GraphicsBuffer::Read | GraphicsBuffer::Write);
|
||||
if (view.isNull()) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ShmAttributes *attributes = buffer->shmAttributes();
|
||||
|
||||
struct spa_data *spaData = pwBuffer->buffer->datas;
|
||||
spaData->type = SPA_DATA_MemFd;
|
||||
spaData->flags = SPA_DATA_FLAG_READWRITE;
|
||||
spaData->mapoffset = 0;
|
||||
spaData->maxsize = attributes->stride * attributes->size.height();
|
||||
spaData->fd = attributes->fd.get();
|
||||
spaData->data = view.image()->bits();
|
||||
spaData->chunk->offset = 0;
|
||||
spaData->chunk->size = spaData->maxsize;
|
||||
spaData->chunk->stride = attributes->stride;
|
||||
spaData->chunk->flags = SPA_CHUNK_FLAG_NONE;
|
||||
|
||||
return new MemFdScreenCastBuffer(buffer, std::move(view));
|
||||
}
|
||||
|
||||
} // namespace KWin
|
54
src/plugins/screencast/screencastbuffer.h
Normal file
54
src/plugins/screencast/screencastbuffer.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/graphicsbufferview.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class GLFramebuffer;
|
||||
class GLTexture;
|
||||
class GraphicsBuffer;
|
||||
struct GraphicsBufferOptions;
|
||||
|
||||
class ScreenCastBuffer
|
||||
{
|
||||
public:
|
||||
explicit ScreenCastBuffer(GraphicsBuffer *buffer);
|
||||
virtual ~ScreenCastBuffer();
|
||||
|
||||
private:
|
||||
GraphicsBuffer *m_buffer;
|
||||
};
|
||||
|
||||
class DmaBufScreenCastBuffer : public ScreenCastBuffer
|
||||
{
|
||||
public:
|
||||
static DmaBufScreenCastBuffer *create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options);
|
||||
|
||||
std::shared_ptr<GLTexture> texture;
|
||||
std::unique_ptr<GLFramebuffer> framebuffer;
|
||||
|
||||
private:
|
||||
DmaBufScreenCastBuffer(GraphicsBuffer *buffer, std::shared_ptr<GLTexture> &&texture, std::unique_ptr<GLFramebuffer> &&framebuffer);
|
||||
};
|
||||
|
||||
class MemFdScreenCastBuffer : public ScreenCastBuffer
|
||||
{
|
||||
public:
|
||||
static MemFdScreenCastBuffer *create(pw_buffer *pwBuffer, const GraphicsBufferOptions &options);
|
||||
|
||||
GraphicsBufferView view;
|
||||
|
||||
private:
|
||||
MemFdScreenCastBuffer(GraphicsBuffer *buffer, GraphicsBufferView &&view);
|
||||
};
|
||||
|
||||
} // namespace KWin
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "plugins/screencast/screencastdmabuftexture.h"
|
||||
#include "core/graphicsbuffer.h"
|
||||
#include "opengl/glutils.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ScreenCastDmaBufTexture::ScreenCastDmaBufTexture(std::shared_ptr<GLTexture> texture, std::unique_ptr<GLFramebuffer> &&framebuffer, GraphicsBuffer *buffer)
|
||||
: m_texture(std::move(texture))
|
||||
, m_framebuffer(std::move(framebuffer))
|
||||
, m_buffer(buffer)
|
||||
{
|
||||
}
|
||||
ScreenCastDmaBufTexture::~ScreenCastDmaBufTexture()
|
||||
{
|
||||
m_framebuffer.reset();
|
||||
m_texture.reset();
|
||||
m_buffer->drop();
|
||||
}
|
||||
|
||||
GraphicsBuffer *ScreenCastDmaBufTexture::buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
KWin::GLTexture *ScreenCastDmaBufTexture::texture() const
|
||||
{
|
||||
return m_texture.get();
|
||||
}
|
||||
|
||||
KWin::GLFramebuffer *ScreenCastDmaBufTexture::framebuffer() const
|
||||
{
|
||||
return m_framebuffer.get();
|
||||
}
|
||||
|
||||
} // namespace KWin
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class GLFramebuffer;
|
||||
class GLTexture;
|
||||
class GraphicsBuffer;
|
||||
|
||||
class ScreenCastDmaBufTexture
|
||||
{
|
||||
public:
|
||||
explicit ScreenCastDmaBufTexture(std::shared_ptr<GLTexture> texture, std::unique_ptr<GLFramebuffer> &&framebuffer, GraphicsBuffer *buffer);
|
||||
virtual ~ScreenCastDmaBufTexture();
|
||||
|
||||
GraphicsBuffer *buffer() const;
|
||||
GLTexture *texture() const;
|
||||
GLFramebuffer *framebuffer() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<GLTexture> m_texture;
|
||||
std::unique_ptr<GLFramebuffer> m_framebuffer;
|
||||
GraphicsBuffer *m_buffer;
|
||||
};
|
||||
|
||||
}
|
|
@ -7,8 +7,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <spa/buffer/buffer.h>
|
||||
#include <spa/param/video/raw.h>
|
||||
|
||||
class QImage;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ public:
|
|||
virtual QSize textureSize() const = 0;
|
||||
|
||||
virtual void render(GLFramebuffer *target) = 0;
|
||||
virtual void render(spa_data *spa, spa_video_format format) = 0;
|
||||
virtual void render(QImage *target) = 0;
|
||||
virtual std::chrono::nanoseconds clock() const = 0;
|
||||
|
||||
virtual void resume() = 0;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
|
||||
#include "platformsupport/scenes/opengl/openglbackend.h"
|
||||
#include "scene/workspacescene.h"
|
||||
#include "screencastdmabuftexture.h"
|
||||
#include "screencastbuffer.h"
|
||||
#include "screencastsource.h"
|
||||
#include "utils/drm_format_helper.h"
|
||||
|
||||
|
@ -31,13 +31,8 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QPainter>
|
||||
|
||||
#include <spa/buffer/meta.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <spa/buffer/meta.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -219,96 +214,44 @@ void ScreenCastStream::onStreamParamChanged(uint32_t id, const struct spa_pod *f
|
|||
m_streaming = true;
|
||||
}
|
||||
|
||||
void ScreenCastStream::onStreamAddBuffer(pw_buffer *buffer)
|
||||
void ScreenCastStream::onStreamAddBuffer(pw_buffer *pwBuffer)
|
||||
{
|
||||
if (m_closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<ScreenCastDmaBufTexture> dmabuff;
|
||||
m_waitForNewBuffers = false;
|
||||
|
||||
struct spa_data *spa_data = buffer->buffer->datas;
|
||||
struct spa_data *spa_data = pwBuffer->buffer->datas;
|
||||
if (spa_data[0].type != SPA_ID_INVALID && spa_data[0].type & (1 << SPA_DATA_DmaBuf)) {
|
||||
Q_ASSERT(m_dmabufParams);
|
||||
dmabuff = createDmaBufTexture(*m_dmabufParams);
|
||||
if (auto dmabuf = DmaBufScreenCastBuffer::create(pwBuffer, GraphicsBufferOptions{
|
||||
.size = QSize(m_dmabufParams->width, m_dmabufParams->height),
|
||||
.format = m_dmabufParams->format,
|
||||
.modifiers = {m_dmabufParams->modifier},
|
||||
})) {
|
||||
pwBuffer->user_data = dmabuf;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dmabuff) {
|
||||
const DmaBufAttributes *dmabufAttribs = dmabuff->buffer()->dmabufAttributes();
|
||||
Q_ASSERT(buffer->buffer->n_datas >= uint(dmabufAttribs->planeCount));
|
||||
for (int i = 0; i < dmabufAttribs->planeCount; ++i) {
|
||||
spa_data[i].type = SPA_DATA_DmaBuf;
|
||||
spa_data[i].flags = SPA_DATA_FLAG_READWRITE;
|
||||
spa_data[i].mapoffset = 0;
|
||||
spa_data[i].maxsize = i == 0 ? dmabufAttribs->pitch[i] * dmabufAttribs->height : 0; // TODO: dmabufs don't have a well defined size, it should be zero but some clients check the size to see if the buffer is valid
|
||||
spa_data[i].fd = dmabufAttribs->fd[i].get();
|
||||
spa_data[i].data = nullptr;
|
||||
spa_data[i].chunk->offset = dmabufAttribs->offset[i];
|
||||
spa_data[i].chunk->size = spa_data[i].maxsize;
|
||||
spa_data[i].chunk->stride = dmabufAttribs->pitch[i];
|
||||
spa_data[i].chunk->flags = SPA_CHUNK_FLAG_NONE;
|
||||
}
|
||||
m_dmabufDataForPwBuffer.insert(buffer, dmabuff);
|
||||
#ifdef F_SEAL_SEAL // Disable memfd on systems that don't have it, like BSD < 12
|
||||
} else {
|
||||
if (!(spa_data->type & (1 << SPA_DATA_MemFd))) {
|
||||
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Client doesn't support memfd buffer data type";
|
||||
if (spa_data->type & (1 << SPA_DATA_MemFd)) {
|
||||
if (auto memfd = MemFdScreenCastBuffer::create(pwBuffer, GraphicsBufferOptions{
|
||||
.size = m_resolution,
|
||||
.format = m_drmFormat,
|
||||
.software = true,
|
||||
})) {
|
||||
pwBuffer->user_data = memfd;
|
||||
return;
|
||||
}
|
||||
|
||||
const int bytesPerPixel = m_source->hasAlphaChannel() ? 4 : 3;
|
||||
const int stride = SPA_ROUND_UP_N(m_resolution.width() * bytesPerPixel, 4);
|
||||
spa_data->type = SPA_DATA_MemFd;
|
||||
spa_data->flags = SPA_DATA_FLAG_READWRITE;
|
||||
spa_data->mapoffset = 0;
|
||||
spa_data->maxsize = stride * m_resolution.height();
|
||||
spa_data->fd = memfd_create("kwin-screencast-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (spa_data->fd == -1) {
|
||||
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Can't create memfd";
|
||||
return;
|
||||
}
|
||||
|
||||
if (ftruncate(spa_data->fd, spa_data->maxsize) < 0) {
|
||||
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Can't truncate to" << spa_data->maxsize;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
|
||||
if (fcntl(spa_data->fd, F_ADD_SEALS, seals) == -1) {
|
||||
qCWarning(KWIN_SCREENCAST) << objectName() << "memfd: Failed to add seals";
|
||||
}
|
||||
|
||||
spa_data->data = mmap(nullptr,
|
||||
spa_data->maxsize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
spa_data->fd,
|
||||
spa_data->mapoffset);
|
||||
if (spa_data->data == MAP_FAILED) {
|
||||
qCCritical(KWIN_SCREENCAST) << objectName() << "memfd: Failed to mmap memory";
|
||||
} else {
|
||||
qCDebug(KWIN_SCREENCAST) << objectName() << "memfd: created successfully" << spa_data->data << spa_data->maxsize;
|
||||
}
|
||||
|
||||
spa_data->chunk->offset = 0;
|
||||
spa_data->chunk->size = spa_data->maxsize;
|
||||
spa_data->chunk->stride = stride;
|
||||
spa_data->chunk->flags = SPA_CHUNK_FLAG_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
m_waitForNewBuffers = false;
|
||||
}
|
||||
|
||||
void ScreenCastStream::onStreamRemoveBuffer(pw_buffer *buffer)
|
||||
void ScreenCastStream::onStreamRemoveBuffer(pw_buffer *pwBuffer)
|
||||
{
|
||||
m_dmabufDataForPwBuffer.remove(buffer);
|
||||
|
||||
struct spa_buffer *spa_buffer = buffer->buffer;
|
||||
struct spa_data *spa_data = spa_buffer->datas;
|
||||
if (spa_data && spa_data->type == SPA_DATA_MemFd) {
|
||||
::munmap(spa_data->data, spa_data->maxsize);
|
||||
::close(spa_data->fd);
|
||||
if (ScreenCastBuffer *buffer = static_cast<ScreenCastBuffer *>(pwBuffer->user_data)) {
|
||||
delete buffer;
|
||||
pwBuffer->user_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,58 +477,38 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
|
|||
return;
|
||||
}
|
||||
|
||||
struct pw_buffer *buffer = pw_stream_dequeue_buffer(m_pwStream);
|
||||
if (!buffer) {
|
||||
struct pw_buffer *pwBuffer = pw_stream_dequeue_buffer(m_pwStream);
|
||||
if (!pwBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct spa_buffer *spa_buffer = buffer->buffer;
|
||||
struct spa_buffer *spa_buffer = pwBuffer->buffer;
|
||||
struct spa_data *spa_data = spa_buffer->datas;
|
||||
|
||||
ScreenCastBuffer *buffer = static_cast<ScreenCastBuffer *>(pwBuffer->user_data);
|
||||
if (!buffer) {
|
||||
qCWarning(KWIN_SCREENCAST) << objectName() << "Failed to record frame: invalid buffer type";
|
||||
corruptHeader(spa_buffer);
|
||||
pw_stream_queue_buffer(m_pwStream, pwBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
EglContext *context = static_cast<AbstractEglBackend *>(Compositor::self()->backend())->openglContext();
|
||||
context->makeCurrent();
|
||||
|
||||
spa_data->chunk->flags = SPA_CHUNK_FLAG_NONE;
|
||||
if (spa_data[0].type == SPA_DATA_MemFd) {
|
||||
uint8_t *data = static_cast<uint8_t *>(spa_data->data);
|
||||
if (!data) {
|
||||
qCWarning(KWIN_SCREENCAST) << objectName() << "Failed to record frame: invalid buffer data";
|
||||
corruptHeader(spa_buffer);
|
||||
pw_stream_queue_buffer(m_pwStream, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool hasAlpha = m_source->hasAlphaChannel();
|
||||
const int bpp = data && !hasAlpha ? 3 : 4;
|
||||
const uint stride = SPA_ROUND_UP_N(size.width() * bpp, 4);
|
||||
|
||||
if ((stride * size.height()) > spa_data->maxsize) {
|
||||
qCDebug(KWIN_SCREENCAST) << objectName() << "Failed to record frame: frame is too big";
|
||||
corruptHeader(spa_buffer);
|
||||
pw_stream_queue_buffer(m_pwStream, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
m_source->render(spa_data, m_videoFormat.format);
|
||||
if (auto memfd = dynamic_cast<MemFdScreenCastBuffer *>(buffer)) {
|
||||
m_source->render(memfd->view.image());
|
||||
|
||||
auto cursor = Cursors::self()->currentCursor();
|
||||
if (m_cursor.mode == ScreencastV1Interface::Embedded && includesCursor(cursor)) {
|
||||
QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
|
||||
QPainter painter(&dest);
|
||||
QPainter painter(memfd->view.image());
|
||||
const auto position = (cursor->pos() - m_cursor.viewport.topLeft() - cursor->hotspot()) * m_cursor.scale;
|
||||
const PlatformCursorImage cursorImage = kwinApp()->cursorImage();
|
||||
painter.drawImage(QRect{position.toPoint(), cursorImage.image().size()}, cursorImage.image());
|
||||
}
|
||||
} else if (spa_data[0].type == SPA_DATA_DmaBuf) {
|
||||
auto dmabuf = m_dmabufDataForPwBuffer.constFind(buffer);
|
||||
if (dmabuf == m_dmabufDataForPwBuffer.constEnd()) {
|
||||
qCDebug(KWIN_SCREENCAST) << objectName() << "Failed to record frame: no dmabuf data";
|
||||
corruptHeader(spa_buffer);
|
||||
pw_stream_queue_buffer(m_pwStream, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
m_source->render((*dmabuf)->framebuffer());
|
||||
} else if (auto dmabuf = dynamic_cast<DmaBufScreenCastBuffer *>(buffer)) {
|
||||
m_source->render(dmabuf->framebuffer.get());
|
||||
|
||||
auto cursor = Cursors::self()->currentCursor();
|
||||
if (m_cursor.mode == ScreencastV1Interface::Embedded && includesCursor(cursor)) {
|
||||
|
@ -599,7 +522,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
|
|||
}
|
||||
}
|
||||
if (m_cursor.texture) {
|
||||
GLFramebuffer::pushFramebuffer((*dmabuf)->framebuffer());
|
||||
GLFramebuffer::pushFramebuffer(dmabuf->framebuffer.get());
|
||||
|
||||
auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
|
||||
|
@ -632,11 +555,6 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
|
|||
} else {
|
||||
glFlush();
|
||||
}
|
||||
} else {
|
||||
qCWarning(KWIN_SCREENCAST, "%s Failed to record frame: invalid buffer type: %d", objectName().toUtf8().constData(), spa_data[0].type);
|
||||
corruptHeader(spa_buffer);
|
||||
pw_stream_queue_buffer(m_pwStream, buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_cursor.mode == ScreencastV1Interface::Metadata) {
|
||||
|
@ -646,7 +564,7 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
|
|||
|
||||
addDamage(spa_buffer, damagedRegion);
|
||||
addHeader(spa_buffer);
|
||||
enqueue(buffer);
|
||||
enqueue(pwBuffer);
|
||||
}
|
||||
|
||||
void ScreenCastStream::addHeader(spa_buffer *spaBuffer)
|
||||
|
@ -918,45 +836,6 @@ std::optional<ScreenCastDmaBufTextureParams> ScreenCastStream::testCreateDmaBuf(
|
|||
};
|
||||
}
|
||||
|
||||
std::shared_ptr<ScreenCastDmaBufTexture> ScreenCastStream::createDmaBufTexture(const ScreenCastDmaBufTextureParams ¶ms)
|
||||
{
|
||||
AbstractEglBackend *backend = dynamic_cast<AbstractEglBackend *>(Compositor::self()->backend());
|
||||
if (!backend) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GraphicsBuffer *buffer = backend->drmDevice()->allocator()->allocate(GraphicsBufferOptions{
|
||||
.size = QSize(params.width, params.height),
|
||||
.format = params.format,
|
||||
.modifiers = {params.modifier},
|
||||
});
|
||||
if (!buffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DmaBufAttributes *attrs = buffer->dmabufAttributes();
|
||||
if (!attrs) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
backend->makeCurrent();
|
||||
|
||||
std::shared_ptr<GLTexture> texture = backend->importDmaBufAsTexture(*attrs);
|
||||
if (!texture) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GLFramebuffer> framebuffer = std::make_unique<GLFramebuffer>(texture.get());
|
||||
if (!framebuffer->valid()) {
|
||||
buffer->drop();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_shared<ScreenCastDmaBufTexture>(std::move(texture), std::move(framebuffer), buffer);
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_screencaststream.cpp"
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "config-kwin.h"
|
||||
|
||||
#include "wayland/screencast_v1.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
@ -30,7 +28,6 @@ namespace KWin
|
|||
{
|
||||
|
||||
class Cursor;
|
||||
class ScreenCastDmaBufTexture;
|
||||
class GLTexture;
|
||||
class PipeWireCore;
|
||||
class ScreenCastSource;
|
||||
|
@ -100,7 +97,6 @@ private:
|
|||
const QList<uint64_t> &modifiers, quint32 modifiersFlags);
|
||||
|
||||
std::optional<ScreenCastDmaBufTextureParams> testCreateDmaBuf(const QSize &size, quint32 format, const QList<uint64_t> &modifiers);
|
||||
std::shared_ptr<ScreenCastDmaBufTexture> createDmaBufTexture(const ScreenCastDmaBufTextureParams ¶ms);
|
||||
|
||||
std::shared_ptr<PipeWireCore> m_pwCore;
|
||||
std::unique_ptr<ScreenCastSource> m_source;
|
||||
|
@ -134,8 +130,6 @@ private:
|
|||
QMetaObject::Connection positionChangedConnection = QMetaObject::Connection();
|
||||
} m_cursor;
|
||||
|
||||
QHash<struct pw_buffer *, std::shared_ptr<ScreenCastDmaBufTexture>> m_dmabufDataForPwBuffer;
|
||||
|
||||
quint64 m_sequential = 0;
|
||||
bool m_hasDmaBuf = false;
|
||||
bool m_waitForNewBuffers = false;
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "opengl/glplatform.h"
|
||||
#include "opengl/gltexture.h"
|
||||
#include "opengl/glutils.h"
|
||||
#include <spa/buffer/buffer.h>
|
||||
#include <spa/param/video/raw.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -28,18 +26,12 @@ static void mirrorVertically(uchar *data, int height, int stride)
|
|||
}
|
||||
}
|
||||
|
||||
static GLenum closestGLType(spa_video_format format)
|
||||
static GLenum closestGLType(QImage::Format format)
|
||||
{
|
||||
switch (format) {
|
||||
case SPA_VIDEO_FORMAT_RGB:
|
||||
return GL_RGB;
|
||||
case SPA_VIDEO_FORMAT_BGR:
|
||||
return GL_BGR;
|
||||
case SPA_VIDEO_FORMAT_RGBx:
|
||||
case SPA_VIDEO_FORMAT_RGBA:
|
||||
return GL_RGBA;
|
||||
case SPA_VIDEO_FORMAT_BGRA:
|
||||
case SPA_VIDEO_FORMAT_BGRx:
|
||||
case QImage::Format_ARGB32:
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
case QImage::Format_RGB32:
|
||||
return GL_BGRA;
|
||||
default:
|
||||
qDebug() << "unknown format" << format;
|
||||
|
@ -47,7 +39,7 @@ static GLenum closestGLType(spa_video_format format)
|
|||
}
|
||||
}
|
||||
|
||||
static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
|
||||
static void doGrabTexture(GLTexture *texture, QImage *target)
|
||||
{
|
||||
const auto context = OpenGlContext::currentContext();
|
||||
const QSize size = texture->size();
|
||||
|
@ -65,12 +57,12 @@ static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format fo
|
|||
if (context->isOpenGLES() || context->glPlatform()->driver() == Driver_NVidia) {
|
||||
GLFramebuffer fbo(texture);
|
||||
GLFramebuffer::pushFramebuffer(&fbo);
|
||||
glReadPixels(0, 0, size.width(), size.height(), closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
|
||||
glReadPixels(0, 0, size.width(), size.height(), closestGLType(target->format()), GL_UNSIGNED_BYTE, target->bits());
|
||||
GLFramebuffer::popFramebuffer();
|
||||
} else if (context->openglVersion() >= Version(4, 5)) {
|
||||
glGetTextureImage(texture->texture(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->chunk->size, spa->data);
|
||||
glGetTextureImage(texture->texture(), 0, closestGLType(target->format()), GL_UNSIGNED_BYTE, target->sizeInBytes(), target->bits());
|
||||
} else {
|
||||
glGetTexImage(texture->target(), 0, closestGLType(format), GL_UNSIGNED_BYTE, spa->data);
|
||||
glGetTexImage(texture->target(), 0, closestGLType(target->format()), GL_UNSIGNED_BYTE, target->bits());
|
||||
}
|
||||
|
||||
if (invertNeededAndSupported) {
|
||||
|
@ -78,15 +70,15 @@ static void doGrabTexture(GLTexture *texture, spa_data *spa, spa_video_format fo
|
|||
glPixelStorei(GL_PACK_INVERT_MESA, prev);
|
||||
}
|
||||
} else if (invertNeeded) {
|
||||
mirrorVertically(static_cast<uchar *>(spa->data), size.height(), spa->chunk->stride);
|
||||
mirrorVertically(static_cast<uchar *>(target->bits()), size.height(), target->bytesPerLine());
|
||||
}
|
||||
}
|
||||
|
||||
static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format format)
|
||||
static void grabTexture(GLTexture *texture, QImage *target)
|
||||
{
|
||||
const OutputTransform contentTransform = texture->contentTransform();
|
||||
if (contentTransform == OutputTransform::Normal || contentTransform == OutputTransform::FlipY) {
|
||||
doGrabTexture(texture, spa, format);
|
||||
doGrabTexture(texture, target);
|
||||
} else {
|
||||
const QSize size = contentTransform.map(texture->size());
|
||||
const auto backingTexture = GLTexture::allocate(GL_RGBA8, size);
|
||||
|
@ -105,7 +97,7 @@ static void grabTexture(GLTexture *texture, spa_data *spa, spa_video_format form
|
|||
GLFramebuffer::pushFramebuffer(&fbo);
|
||||
texture->render(size);
|
||||
GLFramebuffer::popFramebuffer();
|
||||
doGrabTexture(backingTexture.get(), spa, format);
|
||||
doGrabTexture(backingTexture.get(), target);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ QSize WindowScreenCastSource::textureSize() const
|
|||
return m_window->clientGeometry().size().toSize();
|
||||
}
|
||||
|
||||
void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
|
||||
void WindowScreenCastSource::render(QImage *target)
|
||||
{
|
||||
const auto offscreenTexture = GLTexture::allocate(hasAlphaChannel() ? GL_RGBA8 : GL_RGB8, textureSize());
|
||||
if (!offscreenTexture) {
|
||||
|
@ -66,7 +66,7 @@ void WindowScreenCastSource::render(spa_data *spa, spa_video_format format)
|
|||
|
||||
GLFramebuffer offscreenTarget(offscreenTexture.get());
|
||||
render(&offscreenTarget);
|
||||
grabTexture(offscreenTexture.get(), spa, format);
|
||||
grabTexture(offscreenTexture.get(), target);
|
||||
}
|
||||
|
||||
void WindowScreenCastSource::render(GLFramebuffer *target)
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
uint refreshRate() const override;
|
||||
|
||||
void render(GLFramebuffer *target) override;
|
||||
void render(spa_data *spa, spa_video_format format) override;
|
||||
void render(QImage *target) override;
|
||||
std::chrono::nanoseconds clock() const override;
|
||||
|
||||
void resume() override;
|
||||
|
|
Loading…
Reference in a new issue