sharing works (only dma piece of shit maybe obs is wonk)

This commit is contained in:
vaxerski 2023-08-28 14:14:12 +02:00
parent 9081ca2592
commit 5fca07636b
8 changed files with 440 additions and 36 deletions

View file

@ -29,7 +29,7 @@ add_subdirectory(subprojects/sdbus-cpp)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols cairo pango pangocairo libjpeg libpipewire-0.3 libspa-0.2) pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols cairo pango pangocairo libjpeg libpipewire-0.3 libspa-0.2 libdrm gbm)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(xdg-desktop-portal-hyprland ${SRCFILES}) add_executable(xdg-desktop-portal-hyprland ${SRCFILES})

View file

@ -9,6 +9,8 @@
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <thread> #include <thread>
@ -78,6 +80,117 @@ inline const zwp_linux_dmabuf_v1_listener dmabufListener = {
.modifier = handleDMABUFModifier, .modifier = handleDMABUFModifier,
}; };
static void dmabufFeedbackMainDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* device_arr) {
Debug::log(LOG, "[core] dmabufFeedbackMainDevice");
RASSERT(!g_pPortalManager->m_sWaylandConnection.gbm, "double dmabuf feedback");
dev_t device;
assert(device_arr->size == sizeof(device));
memcpy(&device, device_arr->data, sizeof(device));
drmDevice* drmDev;
if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) {
Debug::log(WARN, "[dmabuf] unable to open main device?");
exit(1);
}
g_pPortalManager->m_sWaylandConnection.gbmDevice = g_pPortalManager->createGBMDevice(drmDev);
}
static void dmabufFeedbackFormatTable(void* data, zwp_linux_dmabuf_feedback_v1* feedback, int fd, uint32_t size) {
Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");
g_pPortalManager->m_vDMABUFMods.clear();
g_pPortalManager->m_sWaylandConnection.dma.formatTable = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (g_pPortalManager->m_sWaylandConnection.dma.formatTable == MAP_FAILED) {
Debug::log(ERR, "[core] format table failed to mmap");
g_pPortalManager->m_sWaylandConnection.dma.formatTable = nullptr;
g_pPortalManager->m_sWaylandConnection.dma.formatTableSize = 0;
return;
}
g_pPortalManager->m_sWaylandConnection.dma.formatTableSize = size;
}
static void dmabufFeedbackDone(void* data, zwp_linux_dmabuf_feedback_v1* feedback) {
Debug::log(TRACE, "[core] dmabufFeedbackDone");
if (g_pPortalManager->m_sWaylandConnection.dma.formatTable)
munmap(g_pPortalManager->m_sWaylandConnection.dma.formatTable, g_pPortalManager->m_sWaylandConnection.dma.formatTableSize);
g_pPortalManager->m_sWaylandConnection.dma.formatTable = nullptr;
g_pPortalManager->m_sWaylandConnection.dma.formatTableSize = 0;
}
static void dmabufFeedbackTrancheTargetDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* device_arr) {
Debug::log(TRACE, "[core] dmabufFeedbackTrancheTargetDevice");
dev_t device;
assert(device_arr->size == sizeof(device));
memcpy(&device, device_arr->data, sizeof(device));
drmDevice* drmDev;
if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0)
return;
if (g_pPortalManager->m_sWaylandConnection.gbmDevice) {
drmDevice* drmDevRenderer = NULL;
drmGetDevice2(gbm_device_get_fd(g_pPortalManager->m_sWaylandConnection.gbmDevice), /* flags */ 0, &drmDevRenderer);
g_pPortalManager->m_sWaylandConnection.dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
} else {
g_pPortalManager->m_sWaylandConnection.gbmDevice = g_pPortalManager->createGBMDevice(drmDev);
g_pPortalManager->m_sWaylandConnection.dma.deviceUsed = g_pPortalManager->m_sWaylandConnection.gbm;
}
}
static void dmabufFeedbackTrancheFlags(void* data, zwp_linux_dmabuf_feedback_v1* feedback, uint32_t flags) {
;
}
static void dmabufFeedbackTrancheFormats(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* indices) {
Debug::log(TRACE, "[core] dmabufFeedbackTrancheFormats");
if (!g_pPortalManager->m_sWaylandConnection.dma.deviceUsed || !g_pPortalManager->m_sWaylandConnection.dma.formatTable)
return;
struct fm_entry {
uint32_t format;
uint32_t padding;
uint64_t modifier;
};
// An entry in the table has to be 16 bytes long
assert(sizeof(struct fm_entry) == 16);
uint32_t n_modifiers = g_pPortalManager->m_sWaylandConnection.dma.formatTableSize / sizeof(struct fm_entry);
fm_entry* fm_entry = (struct fm_entry*)g_pPortalManager->m_sWaylandConnection.dma.formatTable;
uint16_t* idx;
wl_array_for_each(idx, indices) {
if (*idx >= n_modifiers)
continue;
g_pPortalManager->m_vDMABUFMods.push_back({(fm_entry + *idx)->format, (fm_entry + *idx)->modifier});
}
}
static void dmabufFeedbackTrancheDone(void* data, struct zwp_linux_dmabuf_feedback_v1* zwp_linux_dmabuf_feedback_v1) {
Debug::log(TRACE, "[core] dmabufFeedbackTrancheDone");
g_pPortalManager->m_sWaylandConnection.dma.deviceUsed = false;
}
inline const zwp_linux_dmabuf_feedback_v1_listener dmabufFeedbackListener = {
.done = dmabufFeedbackDone,
.format_table = dmabufFeedbackFormatTable,
.main_device = dmabufFeedbackMainDevice,
.tranche_done = dmabufFeedbackTrancheDone,
.tranche_target_device = dmabufFeedbackTrancheTargetDevice,
.tranche_formats = dmabufFeedbackTrancheFormats,
.tranche_flags = dmabufFeedbackTrancheFlags,
};
// //
void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
@ -106,7 +219,7 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t
m_sWaylandConnection.linuxDmabuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version); m_sWaylandConnection.linuxDmabuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version);
m_sWaylandConnection.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)m_sWaylandConnection.linuxDmabuf); m_sWaylandConnection.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)m_sWaylandConnection.linuxDmabuf);
// TODO: dmabuf zwp_linux_dmabuf_feedback_v1_add_listener((zwp_linux_dmabuf_feedback_v1*)m_sWaylandConnection.linuxDmabufFeedback, &dmabufFeedbackListener, nullptr);
} }
else if (INTERFACE == wl_shm_interface.name) else if (INTERFACE == wl_shm_interface.name)
@ -212,3 +325,45 @@ SOutput* CPortalManager::getOutputFromName(const std::string& name) {
} }
return nullptr; return nullptr;
} }
static char* gbm_find_render_node(drmDevice* device) {
drmDevice* devices[64];
char* render_node = NULL;
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
for (int i = 0; i < n; ++i) {
drmDevice* dev = devices[i];
if (device && !drmDevicesEqual(device, dev)) {
continue;
}
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
continue;
render_node = strdup(dev->nodes[DRM_NODE_RENDER]);
break;
}
drmFreeDevices(devices, n);
return render_node;
}
gbm_device* CPortalManager::createGBMDevice(drmDevice* dev) {
char* renderNode = gbm_find_render_node(dev);
if (!renderNode) {
Debug::log(ERR, "[core] Couldn't find a render node");
return nullptr;
}
Debug::log(TRACE, "[core] createGBMDevice: render node {}", renderNode);
int fd = open(renderNode, O_RDWR | O_CLOEXEC);
if (fd < 0) {
Debug::log(ERR, "[core] couldn't open render node");
free(renderNode);
return NULL;
}
free(renderNode);
return gbm_create_device(fd);
}

View file

@ -6,6 +6,8 @@
#include "../portals/Screencopy.hpp" #include "../portals/Screencopy.hpp"
#include "../helpers/Timer.hpp" #include "../helpers/Timer.hpp"
#include <gbm.h>
#include <xf86drm.h>
#include <mutex> #include <mutex>
@ -46,12 +48,21 @@ class CPortalManager {
void* linuxDmabuf = nullptr; void* linuxDmabuf = nullptr;
void* linuxDmabufFeedback = nullptr; void* linuxDmabufFeedback = nullptr;
wl_shm* shm = nullptr; wl_shm* shm = nullptr;
gbm_bo* gbm;
gbm_device* gbmDevice;
struct {
void* formatTable = nullptr;
size_t formatTableSize = 0;
bool deviceUsed = false;
} dma;
} m_sWaylandConnection; } m_sWaylandConnection;
std::vector<SDMABUFModifier> m_vDMABUFMods; std::vector<SDMABUFModifier> m_vDMABUFMods;
std::vector<std::unique_ptr<CTimer>> m_vTimers; std::vector<std::unique_ptr<CTimer>> m_vTimers;
gbm_device* createGBMDevice(drmDevice* dev);
private: private:
std::unique_ptr<sdbus::IConnection> m_pConnection; std::unique_ptr<sdbus::IConnection> m_pConnection;
std::vector<std::unique_ptr<SOutput>> m_vOutputs; std::vector<std::unique_ptr<SOutput>> m_vOutputs;

View file

@ -13,6 +13,17 @@ enum eLogLevel
CRIT CRIT
}; };
#define RASSERT(expr, reason, ...) \
if (!(expr)) { \
Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \
std::format(reason, ##__VA_ARGS__), __LINE__, \
([]() constexpr->std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \
printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \
*((int*)nullptr) = 1; /* so that we crash and get a coredump */ \
}
#define ASSERT(expr) RASSERT(expr, "?")
namespace Debug { namespace Debug {
template <typename... Args> template <typename... Args>
void log(eLogLevel level, const std::string& fmt, Args&&... args) { void log(eLogLevel level, const std::string& fmt, Args&&... args) {

View file

@ -5,6 +5,7 @@
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <protocols/linux-dmabuf-unstable-v1-protocol.h>
// --------------- Wayland Protocol Handlers --------------- // // --------------- Wayland Protocol Handlers --------------- //
@ -45,7 +46,7 @@ static void wlrOnReady(void* data, struct zwlr_screencopy_frame_v1* frame, uint3
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(PSESSION); g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(PSESSION);
g_pPortalManager->m_vTimers.emplace_back(std::make_unique<CTimer>(1000.0 / FRAMERATE, [PSESSION]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(PSESSION); })); g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
zwlr_screencopy_frame_v1_destroy(frame); zwlr_screencopy_frame_v1_destroy(frame);
PSESSION->sharingData.frameCallback = nullptr; PSESSION->sharingData.frameCallback = nullptr;
@ -94,6 +95,17 @@ static void wlrOnBufferDone(void* data, struct zwlr_screencopy_frame_v1* frame)
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height); Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[sc] wlr format {} size {}x{}", (int)PSESSION->sharingData.frameInfoSHM.fmt, PSESSION->sharingData.frameInfoSHM.w, PSESSION->sharingData.frameInfoSHM.h); Debug::log(TRACE, "[sc] wlr format {} size {}x{}", (int)PSESSION->sharingData.frameInfoSHM.fmt, PSESSION->sharingData.frameInfoSHM.w, PSESSION->sharingData.frameInfoSHM.h);
const auto FMT = PSTREAM->isDMA ? PSESSION->sharingData.frameInfoDMA.fmt : PSESSION->sharingData.frameInfoSHM.fmt;
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
(PSTREAM->pwVideoInfo.size.width != PSESSION->sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != PSESSION->sharingData.frameInfoDMA.h)) {
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
PSESSION->sharingData.status = FRAME_RENEG;
zwlr_screencopy_frame_v1_destroy(frame);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
return;
}
if (!PSTREAM->currentPWBuffer) { if (!PSTREAM->currentPWBuffer) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer"); Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(PSESSION); g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(PSESSION);
@ -351,8 +363,8 @@ void CScreencopyPortal::startSharing(CScreencopyPortal::SSession* pSession) {
wl_display_dispatch(g_pPortalManager->m_sWaylandConnection.display); wl_display_dispatch(g_pPortalManager->m_sWaylandConnection.display);
wl_display_roundtrip(g_pPortalManager->m_sWaylandConnection.display); wl_display_roundtrip(g_pPortalManager->m_sWaylandConnection.display);
if (pSession->sharingData.frameInfoSHM.fmt == DRM_FORMAT_INVALID) { if (pSession->sharingData.frameInfoDMA.fmt == DRM_FORMAT_INVALID) {
Debug::log(ERR, "[screencopy] Couldn't obtain a format from shm"); Debug::log(ERR, "[screencopy] Couldn't obtain a format from dma");
return; return;
} }
@ -368,9 +380,9 @@ void CScreencopyPortal::startSharing(CScreencopyPortal::SSession* pSession) {
Debug::log(LOG, "[screencopy] Sharing initialized"); Debug::log(LOG, "[screencopy] Sharing initialized");
g_pPortalManager->m_vTimers.emplace_back(std::make_unique<CTimer>(1000.0 / FRAMERATE, [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); })); g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(pSession);
Debug::log(TRACE, "[sc] queued frame in {}ms", 1000.0 / FRAMERATE); Debug::log(TRACE, "[sc] queued frame in {}ms", 1000.0 / pSession->sharingData.framerate);
} }
void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) { void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
@ -408,6 +420,11 @@ void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
Debug::log(LOG, "[screencopy] frame callbacks initialized"); Debug::log(LOG, "[screencopy] frame callbacks initialized");
} }
void CScreencopyPortal::queueNextShareFrame(CScreencopyPortal::SSession* pSession) {
g_pPortalManager->m_vTimers.emplace_back(
std::make_unique<CTimer>(1000.0 / pSession->sharingData.framerate, [pSession]() { g_pPortalManager->m_sPortals.screencopy->startFrameCopy(pSession); }));
}
CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& path) { CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& path) {
for (auto& s : m_vSessions) { for (auto& s : m_vSessions) {
if (s->sessionHandle == path) if (s->sessionHandle == path)
@ -517,12 +534,82 @@ static void pwStreamParamChanged(void* data, uint32_t id, const spa_pod* param)
spa_pod_dynamic_builder_init(&dynBuilder[2], params_buffer[2], sizeof(params_buffer[2]), 2048); spa_pod_dynamic_builder_init(&dynBuilder[2], params_buffer[2], sizeof(params_buffer[2]), 2048);
spa_format_video_raw_parse(param, &PSTREAM->pwVideoInfo); spa_format_video_raw_parse(param, &PSTREAM->pwVideoInfo);
// todo: framerate PSTREAM->pSession->sharingData.framerate = PSTREAM->pwVideoInfo.max_framerate.num / PSTREAM->pwVideoInfo.max_framerate.denom;
uint32_t data_type = 1 << SPA_DATA_MemFd;
const struct spa_pod_prop* prop_modifier; const struct spa_pod_prop* prop_modifier;
if ((prop_modifier = spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) { if ((prop_modifier = spa_pod_find_prop(param, nullptr, SPA_FORMAT_VIDEO_modifier))) {
Debug::log(ERR, "[pipewire] pw requested dmabuf"); Debug::log(TRACE, "[pipewire] pw requested dmabuf");
return; PSTREAM->isDMA = true;
data_type = 1 << SPA_DATA_DmaBuf;
RASSERT(PSTREAM->pwVideoInfo.format == pwFromDrmFourcc(PSTREAM->pSession->sharingData.frameInfoDMA.fmt), "invalid format in dma pw param change");
if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) {
Debug::log(TRACE, "[pw] don't fixate");
const spa_pod* pod_modifier = &prop_modifier->value;
uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier) - 1;
uint64_t* modifiers = SPA_POD_CHOICE_VALUES(pod_modifier);
modifiers++;
uint32_t flags = GBM_BO_USE_RENDERING;
uint64_t modifier;
uint32_t n_params;
spa_pod_builder* builder[2] = {&dynBuilder[0].b, &dynBuilder[1].b};
gbm_bo* bo =
gbm_bo_create_with_modifiers2(g_pPortalManager->m_sWaylandConnection.gbmDevice, PSTREAM->pSession->sharingData.frameInfoDMA.w,
PSTREAM->pSession->sharingData.frameInfoDMA.h, PSTREAM->pSession->sharingData.frameInfoDMA.fmt, modifiers, n_modifiers, flags);
if (bo) {
modifier = gbm_bo_get_modifier(bo);
gbm_bo_destroy(bo);
goto fixate_format;
}
Debug::log(TRACE, "[pw] unable to allocate a dmabuf with modifiers. Falling back to the old api");
for (uint32_t i = 0; i < n_modifiers; i++) {
switch (modifiers[i]) {
case DRM_FORMAT_MOD_INVALID:
flags =
GBM_BO_USE_RENDERING; // ;cast->ctx->state->config->screencast_conf.force_mod_linear ? GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR : GBM_BO_USE_RENDERING;
break;
case DRM_FORMAT_MOD_LINEAR: flags = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; break;
default: continue;
}
bo = gbm_bo_create(g_pPortalManager->m_sWaylandConnection.gbmDevice, PSTREAM->pSession->sharingData.frameInfoDMA.w, PSTREAM->pSession->sharingData.frameInfoDMA.h,
PSTREAM->pSession->sharingData.frameInfoDMA.fmt, flags);
if (bo) {
modifier = gbm_bo_get_modifier(bo);
gbm_bo_destroy(bo);
goto fixate_format;
}
}
Debug::log(ERR, "[pw] failed to alloc dma");
return;
fixate_format:
params[0] = fixate_format(&dynBuilder[2].b, pwFromDrmFourcc(PSTREAM->pSession->sharingData.frameInfoDMA.fmt), PSTREAM->pSession->sharingData.frameInfoDMA.w,
PSTREAM->pSession->sharingData.frameInfoDMA.h, PSTREAM->pSession->sharingData.framerate, &modifier);
n_params = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->buildFormatsFor(builder, &params[1], PSTREAM);
n_params++;
pw_stream_update_params(PSTREAM->stream, params, n_params);
spa_pod_dynamic_builder_clean(&dynBuilder[0]);
spa_pod_dynamic_builder_clean(&dynBuilder[1]);
spa_pod_dynamic_builder_clean(&dynBuilder[2]);
Debug::log(TRACE, "[pw] Format renegotiated:");
Debug::log(TRACE, "[pw] | buffer_type {}", "DMA");
Debug::log(TRACE, "[pw] | format: {}", (int)PSTREAM->pwVideoInfo.format);
Debug::log(TRACE, "[pw] | modifier: {}", PSTREAM->pwVideoInfo.modifier);
Debug::log(TRACE, "[pw] | size: {}x{}", PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[pw] | framerate {}", PSTREAM->pSession->sharingData.framerate);
return;
}
} }
Debug::log(TRACE, "[pw] Format renegotiated:"); Debug::log(TRACE, "[pw] Format renegotiated:");
@ -530,10 +617,9 @@ static void pwStreamParamChanged(void* data, uint32_t id, const spa_pod* param)
Debug::log(TRACE, "[pw] | format: {}", (int)PSTREAM->pwVideoInfo.format); Debug::log(TRACE, "[pw] | format: {}", (int)PSTREAM->pwVideoInfo.format);
Debug::log(TRACE, "[pw] | modifier: {}", PSTREAM->pwVideoInfo.modifier); Debug::log(TRACE, "[pw] | modifier: {}", PSTREAM->pwVideoInfo.modifier);
Debug::log(TRACE, "[pw] | size: {}x{}", PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height); Debug::log(TRACE, "[pw] | size: {}x{}", PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[pw] | framerate {}", FRAMERATE); Debug::log(TRACE, "[pw] | framerate {}", PSTREAM->pSession->sharingData.framerate);
uint32_t blocks = 1; uint32_t blocks = 1;
uint32_t data_type = 1 << SPA_DATA_MemFd;
params[0] = build_buffer(&dynBuilder[0].b, blocks, PSTREAM->pSession->sharingData.frameInfoSHM.size, PSTREAM->pSession->sharingData.frameInfoSHM.stride, data_type); params[0] = build_buffer(&dynBuilder[0].b, blocks, PSTREAM->pSession->sharingData.frameInfoSHM.size, PSTREAM->pSession->sharingData.frameInfoSHM.stride, data_type);
@ -556,6 +642,7 @@ static void pwStreamAddBuffer(void* data, pw_buffer* buffer) {
if ((spaData[0].type & (1u << SPA_DATA_MemFd)) > 0) { if ((spaData[0].type & (1u << SPA_DATA_MemFd)) > 0) {
type = SPA_DATA_MemFd; type = SPA_DATA_MemFd;
Debug::log(WARN, "[pipewire] Asked for a wl_shm buffer which is legacy.");
} else if ((spaData[0].type & (1u << SPA_DATA_DmaBuf)) > 0) { } else if ((spaData[0].type & (1u << SPA_DATA_DmaBuf)) > 0) {
type = SPA_DATA_DmaBuf; type = SPA_DATA_DmaBuf;
} else { } else {
@ -679,15 +766,73 @@ void CPipewireConnection::destroyStream(CScreencopyPortal::SSession* pSession) {
std::erase_if(m_vStreams, [&](const auto& other) { return other.get() == PSTREAM; }); std::erase_if(m_vStreams, [&](const auto& other) { return other.get() == PSTREAM; });
} }
static bool wlr_query_dmabuf_modifiers(uint32_t drm_format, uint32_t num_modifiers, uint64_t* modifiers, uint32_t* max_modifiers) {
if (g_pPortalManager->m_vDMABUFMods.empty())
return false;
if (num_modifiers == 0) {
*max_modifiers = 0;
for (auto& mod : g_pPortalManager->m_vDMABUFMods) {
if (mod.fourcc == drm_format &&
(mod.mod == DRM_FORMAT_MOD_INVALID || gbm_device_get_format_modifier_plane_count(g_pPortalManager->m_sWaylandConnection.gbmDevice, mod.fourcc, mod.mod) > 0))
(*max_modifiers)++;
}
return true;
}
for (size_t i = 0; i < g_pPortalManager->m_vDMABUFMods.size(); ++i) {
if (i >= num_modifiers)
break;
const auto& mod = g_pPortalManager->m_vDMABUFMods[i];
if (mod.fourcc == drm_format &&
(mod.mod == DRM_FORMAT_MOD_INVALID || gbm_device_get_format_modifier_plane_count(g_pPortalManager->m_sWaylandConnection.gbmDevice, mod.fourcc, mod.mod) > 0))
modifiers[i] = mod.mod;
}
*max_modifiers = num_modifiers;
return true;
}
static bool build_modifierlist(CPipewireConnection::SPWStream* stream, uint32_t drm_format, uint64_t** modifiers, uint32_t* modifier_count) {
if (!wlr_query_dmabuf_modifiers(drm_format, 0, nullptr, modifier_count)) {
*modifiers = NULL;
*modifier_count = 0;
return false;
}
if (*modifier_count == 0) {
Debug::log(ERR, "[pw] build_modifierlist: no mods");
*modifiers = NULL;
return true;
}
*modifiers = (uint64_t*)calloc(*modifier_count, sizeof(uint64_t));
bool ret = wlr_query_dmabuf_modifiers(drm_format, *modifier_count, *modifiers, modifier_count);
Debug::log(TRACE, "[pw] build_modifierlist: count {}", *modifier_count);
return ret;
}
uint32_t CPipewireConnection::buildFormatsFor(spa_pod_builder* b[2], const spa_pod* params[2], CPipewireConnection::SPWStream* stream) { uint32_t CPipewireConnection::buildFormatsFor(spa_pod_builder* b[2], const spa_pod* params[2], CPipewireConnection::SPWStream* stream) {
uint32_t paramCount = 0; uint32_t paramCount = 0;
uint32_t modCount = 0;
uint64_t* modifiers = nullptr;
if (/*TODO: dmabuf*/ false) { if (build_modifierlist(stream, stream->pSession->sharingData.frameInfoDMA.fmt, &modifiers, &modCount) && modCount > 0) {
Debug::log(LOG, "[pw] Building modifiers for dma");
paramCount = 2;
params[0] = build_format(b[0], pwFromDrmFourcc(stream->pSession->sharingData.frameInfoDMA.fmt), stream->pSession->sharingData.frameInfoDMA.w,
stream->pSession->sharingData.frameInfoDMA.h, stream->pSession->sharingData.framerate, modifiers, modCount);
assert(params[0] != NULL);
params[1] = build_format(b[1], pwFromDrmFourcc(stream->pSession->sharingData.frameInfoSHM.fmt), stream->pSession->sharingData.frameInfoSHM.w,
stream->pSession->sharingData.frameInfoSHM.h, stream->pSession->sharingData.framerate, NULL, 0);
assert(params[1] != NULL);
} else { } else {
Debug::log(LOG, "[pw] Building modifiers for shm");
paramCount = 1; paramCount = 1;
params[0] = build_format(b[0], pwFromDrmFourcc(stream->pSession->sharingData.frameInfoSHM.fmt), stream->pSession->sharingData.frameInfoSHM.w, params[0] = build_format(b[0], pwFromDrmFourcc(stream->pSession->sharingData.frameInfoSHM.fmt), stream->pSession->sharingData.frameInfoSHM.w,
stream->pSession->sharingData.frameInfoSHM.h, FRAMERATE /*TODO: FRAMERATE*/, NULL, 0); stream->pSession->sharingData.frameInfoSHM.h, stream->pSession->sharingData.framerate, NULL, 0);
} }
return paramCount; return paramCount;
@ -716,16 +861,16 @@ void CPipewireConnection::enqueue(CScreencopyPortal::SSession* pSession) {
} }
spa_buffer* spaBuf = PSTREAM->currentPWBuffer->pwBuffer->buffer; spa_buffer* spaBuf = PSTREAM->currentPWBuffer->pwBuffer->buffer;
bool corrupt = PSTREAM->pSession->sharingData.status != FRAME_READY; const bool CORRUPT = PSTREAM->pSession->sharingData.status != FRAME_READY;
if (corrupt) if (CORRUPT)
Debug::log(TRACE, "[pw] buffer corrupt"); Debug::log(TRACE, "[pw] buffer corrupt");
Debug::log(TRACE, "[pw] Enqueue data:"); Debug::log(TRACE, "[pw] Enqueue data:");
spa_meta_header* header; spa_meta_header* header = (spa_meta_header*)spa_buffer_find_meta_data(spaBuf, SPA_META_Header, sizeof(*header));
if ((header = (spa_meta_header*)spa_buffer_find_meta_data(spaBuf, SPA_META_Header, sizeof(*header)))) { if (header) {
header->pts = PSTREAM->pSession->sharingData.tvTimestampNs; header->pts = PSTREAM->pSession->sharingData.tvTimestampNs;
header->flags = corrupt ? SPA_META_HEADER_FLAG_CORRUPTED : 0; header->flags = CORRUPT ? SPA_META_HEADER_FLAG_CORRUPTED : 0;
header->seq = PSTREAM->seq++; header->seq = PSTREAM->seq++;
header->dts_offset = 0; header->dts_offset = 0;
Debug::log(TRACE, "[pw] | seq {}", header->seq); Debug::log(TRACE, "[pw] | seq {}", header->seq);
@ -734,10 +879,10 @@ void CPipewireConnection::enqueue(CScreencopyPortal::SSession* pSession) {
spa_data* datas = spaBuf->datas; spa_data* datas = spaBuf->datas;
Debug::log(TRACE, "[pw] | size {}x{}", PSTREAM->pSession->sharingData.frameInfoSHM.w, PSTREAM->pSession->sharingData.frameInfoSHM.h); Debug::log(TRACE, "[pw] | size {}x{}", PSTREAM->pSession->sharingData.frameInfoDMA.w, PSTREAM->pSession->sharingData.frameInfoDMA.h);
for (uint32_t plane = 0; plane < spaBuf->n_datas; plane++) { for (uint32_t plane = 0; plane < spaBuf->n_datas; plane++) {
datas[plane].chunk->flags = corrupt ? SPA_CHUNK_FLAG_CORRUPTED : SPA_CHUNK_FLAG_NONE; datas[plane].chunk->flags = CORRUPT ? SPA_CHUNK_FLAG_CORRUPTED : SPA_CHUNK_FLAG_NONE;
Debug::log(TRACE, "[pw] | plane {}", plane); Debug::log(TRACE, "[pw] | plane {}", plane);
Debug::log(TRACE, "[pw] | fd {}", datas[plane].fd); Debug::log(TRACE, "[pw] | fd {}", datas[plane].fd);
@ -776,15 +921,76 @@ void CPipewireConnection::dequeue(CScreencopyPortal::SSession* pSession) {
std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::SPWStream* pStream, bool dmabuf) { std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::SPWStream* pStream, bool dmabuf) {
std::unique_ptr<SBuffer> pBuffer = std::make_unique<SBuffer>(); std::unique_ptr<SBuffer> pBuffer = std::make_unique<SBuffer>();
pBuffer->w = pStream->pSession->sharingData.frameInfoSHM.w;
pBuffer->h = pStream->pSession->sharingData.frameInfoSHM.h;
pBuffer->fmt = pStream->pSession->sharingData.frameInfoSHM.fmt;
pBuffer->isDMABUF = dmabuf; pBuffer->isDMABUF = dmabuf;
Debug::log(TRACE, "[pw] createBuffer: type {}", dmabuf ? "dma" : "shm");
if (dmabuf) { if (dmabuf) {
// todo pBuffer->w = pStream->pSession->sharingData.frameInfoDMA.w;
pBuffer->h = pStream->pSession->sharingData.frameInfoDMA.h;
pBuffer->fmt = pStream->pSession->sharingData.frameInfoDMA.fmt;
uint32_t flags = GBM_BO_USE_RENDERING;
if (pStream->pwVideoInfo.modifier != DRM_FORMAT_MOD_INVALID) {
uint64_t* mods = (uint64_t*)&pStream->pwVideoInfo.modifier;
pBuffer->bo = gbm_bo_create_with_modifiers2(g_pPortalManager->m_sWaylandConnection.gbmDevice, pBuffer->w, pBuffer->h, pBuffer->fmt, mods, 1, flags);
} else {
pBuffer->bo = gbm_bo_create(g_pPortalManager->m_sWaylandConnection.gbmDevice, pBuffer->w, pBuffer->h, pBuffer->fmt, flags);
}
if (!pBuffer->bo) {
Debug::log(ERR, "[pw] Couldn't create a drm buffer");
return nullptr;
}
pBuffer->planeCount = gbm_bo_get_plane_count(pBuffer->bo);
zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params((zwp_linux_dmabuf_v1*)g_pPortalManager->m_sWaylandConnection.linuxDmabuf);
if (!params) {
Debug::log(ERR, "[pw] zwp_linux_dmabuf_v1_create_params failed");
gbm_bo_destroy(pBuffer->bo);
return nullptr;
}
for (size_t plane = 0; plane < (size_t)pBuffer->planeCount; plane++) {
pBuffer->size[plane] = 0;
pBuffer->stride[plane] = gbm_bo_get_stride_for_plane(pBuffer->bo, plane);
pBuffer->offset[plane] = gbm_bo_get_offset(pBuffer->bo, plane);
uint64_t mod = gbm_bo_get_modifier(pBuffer->bo);
pBuffer->fd[plane] = gbm_bo_get_fd_for_plane(pBuffer->bo, plane);
if (pBuffer->fd[plane] < 0) {
Debug::log(ERR, "[pw] gbm_bo_get_fd_for_plane failed");
zwp_linux_buffer_params_v1_destroy(params);
gbm_bo_destroy(pBuffer->bo);
for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
close(pBuffer->fd[plane_tmp]);
}
return NULL;
}
zwp_linux_buffer_params_v1_add(params, pBuffer->fd[plane], plane, pBuffer->offset[plane], pBuffer->stride[plane], mod >> 32, mod & 0xffffffff);
}
pBuffer->wlBuffer = zwp_linux_buffer_params_v1_create_immed(params, pBuffer->w, pBuffer->h, pBuffer->fmt, /* flags */ 0);
zwp_linux_buffer_params_v1_destroy(params);
if (!pBuffer->wlBuffer) {
Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
gbm_bo_destroy(pBuffer->bo);
for (size_t plane = 0; plane < (size_t)pBuffer->planeCount; plane++) {
close(pBuffer->fd[plane]);
}
return nullptr;
}
} else { } else {
pBuffer->w = pStream->pSession->sharingData.frameInfoSHM.w;
pBuffer->h = pStream->pSession->sharingData.frameInfoSHM.h;
pBuffer->fmt = pStream->pSession->sharingData.frameInfoSHM.fmt;
pBuffer->planeCount = 1; pBuffer->planeCount = 1;
pBuffer->size[0] = pStream->pSession->sharingData.frameInfoSHM.size; pBuffer->size[0] = pStream->pSession->sharingData.frameInfoSHM.size;
pBuffer->stride[0] = pStream->pSession->sharingData.frameInfoSHM.stride; pBuffer->stride[0] = pStream->pSession->sharingData.frameInfoSHM.stride;
@ -811,3 +1017,20 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
return pBuffer; return pBuffer;
} }
void CPipewireConnection::updateStreamParam(SPWStream* pStream) {
Debug::log(TRACE, "[pw] update stream params");
uint8_t paramsBuf[2][1024];
spa_pod_dynamic_builder dynBuilder[2];
spa_pod_dynamic_builder_init(&dynBuilder[0], paramsBuf[0], sizeof(paramsBuf[0]), 2048);
spa_pod_dynamic_builder_init(&dynBuilder[1], paramsBuf[1], sizeof(paramsBuf[1]), 2048);
const spa_pod* params[2];
spa_pod_builder* builder[2] = {&dynBuilder[0].b, &dynBuilder[1].b};
uint32_t n_params = buildFormatsFor(builder, params, pStream);
pw_stream_update_params(pStream->stream, params, n_params);
spa_pod_dynamic_builder_clean(&dynBuilder[0]);
spa_pod_dynamic_builder_clean(&dynBuilder[1]);
}

View file

@ -6,8 +6,6 @@
#include "../shared/ScreencopyShared.hpp" #include "../shared/ScreencopyShared.hpp"
#include <gbm.h> #include <gbm.h>
#define FRAMERATE 60
enum cursorModes enum cursorModes
{ {
HIDDEN = 1, HIDDEN = 1,
@ -28,6 +26,7 @@ enum frameStatus
FRAME_QUEUED, FRAME_QUEUED,
FRAME_READY, FRAME_READY,
FRAME_FAILED, FRAME_FAILED,
FRAME_RENEG,
}; };
struct pw_context; struct pw_context;
@ -78,6 +77,7 @@ class CScreencopyPortal {
uint32_t tvNsec = 0; uint32_t tvNsec = 0;
uint64_t tvTimestampNs = 0; uint64_t tvTimestampNs = 0;
uint32_t nodeID = 0; uint32_t nodeID = 0;
uint32_t framerate = 60;
struct { struct {
uint32_t w = 0, h = 0, size = 0, stride = 0, fmt = 0; uint32_t w = 0, h = 0, size = 0, stride = 0, fmt = 0;
@ -93,6 +93,7 @@ class CScreencopyPortal {
}; };
void startFrameCopy(SSession* pSession); void startFrameCopy(SSession* pSession);
void queueNextShareFrame(SSession* pSession);
std::unique_ptr<CPipewireConnection> m_pPipewire; std::unique_ptr<CPipewireConnection> m_pPipewire;
@ -133,18 +134,20 @@ class CPipewireConnection {
spa_hook streamListener; spa_hook streamListener;
SBuffer* currentPWBuffer = nullptr; SBuffer* currentPWBuffer = nullptr;
spa_video_info_raw pwVideoInfo; spa_video_info_raw pwVideoInfo;
uint32_t seq = 0; uint32_t seq = 0;
bool isDMA = false;
std::vector<std::unique_ptr<SBuffer>> buffers; std::vector<std::unique_ptr<SBuffer>> buffers;
}; };
std::unique_ptr<SBuffer> createBuffer(SPWStream* pStream, bool dmabuf); std::unique_ptr<SBuffer> createBuffer(SPWStream* pStream, bool dmabuf);
SPWStream* streamFromSession(CScreencopyPortal::SSession* pSession); SPWStream* streamFromSession(CScreencopyPortal::SSession* pSession);
uint32_t buildFormatsFor(spa_pod_builder* b[2], const spa_pod* params[2], SPWStream* stream);
void updateStreamParam(SPWStream* pStream);
private: private:
std::vector<std::unique_ptr<SPWStream>> m_vStreams; std::vector<std::unique_ptr<SPWStream>> m_vStreams;
uint32_t buildFormatsFor(spa_pod_builder* b[2], const spa_pod* params[2], SPWStream* stream);
bool buildModListFor(SPWStream* stream, uint32_t drmFmt, uint64_t** mods, uint32_t* modCount); bool buildModListFor(SPWStream* stream, uint32_t drmFmt, uint64_t** mods, uint32_t* modCount);
pw_context* m_pContext = nullptr; pw_context* m_pContext = nullptr;

View file

@ -118,7 +118,7 @@ std::string getRandName(std::string prefix) {
(int)(std::rand() % 10)); (int)(std::rand() % 10));
} }
spa_video_format xdph_format_pw_strip_alpha(spa_video_format format) { spa_video_format pwStripAlpha(spa_video_format format) {
switch (format) { switch (format) {
case SPA_VIDEO_FORMAT_BGRA: return SPA_VIDEO_FORMAT_BGRx; case SPA_VIDEO_FORMAT_BGRA: return SPA_VIDEO_FORMAT_BGRx;
case SPA_VIDEO_FORMAT_ABGR: return SPA_VIDEO_FORMAT_xBGR; case SPA_VIDEO_FORMAT_ABGR: return SPA_VIDEO_FORMAT_xBGR;
@ -154,7 +154,7 @@ spa_pod* build_buffer(spa_pod_builder* b, uint32_t blocks, uint32_t size, uint32
spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier) { spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier) {
spa_pod_frame f[1]; spa_pod_frame f[1];
spa_video_format format_without_alpha = xdph_format_pw_strip_alpha(format); spa_video_format format_without_alpha = pwStripAlpha(format);
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
@ -182,7 +182,7 @@ spa_pod* build_format(spa_pod_builder* b, spa_video_format format, uint32_t widt
spa_pod_frame f[2]; spa_pod_frame f[2];
int i, c; int i, c;
spa_video_format format_without_alpha = xdph_format_pw_strip_alpha(format); spa_video_format format_without_alpha = pwStripAlpha(format);
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);

View file

@ -41,6 +41,7 @@ SSelectionData promptForScreencopySelection();
uint32_t drmFourccFromSHM(wl_shm_format format); uint32_t drmFourccFromSHM(wl_shm_format format);
spa_video_format pwFromDrmFourcc(uint32_t format); spa_video_format pwFromDrmFourcc(uint32_t format);
wl_shm_format wlSHMFromDrmFourcc(uint32_t format); wl_shm_format wlSHMFromDrmFourcc(uint32_t format);
spa_video_format pwStripAlpha(spa_video_format format);
std::string getRandName(std::string prefix); std::string getRandName(std::string prefix);
spa_pod* build_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifiers, int modifier_count); spa_pod* build_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifiers, int modifier_count);
spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier); spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier);