mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-23 22:55:58 +01:00
sharing works (only dma piece of shit maybe obs is wonk)
This commit is contained in:
parent
9081ca2592
commit
5fca07636b
8 changed files with 440 additions and 36 deletions
|
@ -29,7 +29,7 @@ add_subdirectory(subprojects/sdbus-cpp)
|
|||
find_package(Threads 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")
|
||||
add_executable(xdg-desktop-portal-hyprland ${SRCFILES})
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
|
@ -78,6 +80,117 @@ inline const zwp_linux_dmabuf_v1_listener dmabufListener = {
|
|||
.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) {
|
||||
|
@ -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.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)
|
||||
|
@ -212,3 +325,45 @@ SOutput* CPortalManager::getOutputFromName(const std::string& name) {
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "../portals/Screencopy.hpp"
|
||||
#include "../helpers/Timer.hpp"
|
||||
#include <gbm.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
|
@ -46,12 +48,21 @@ class CPortalManager {
|
|||
void* linuxDmabuf = nullptr;
|
||||
void* linuxDmabufFeedback = 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;
|
||||
|
||||
std::vector<SDMABUFModifier> m_vDMABUFMods;
|
||||
|
||||
std::vector<std::unique_ptr<CTimer>> m_vTimers;
|
||||
|
||||
gbm_device* createGBMDevice(drmDevice* dev);
|
||||
|
||||
private:
|
||||
std::unique_ptr<sdbus::IConnection> m_pConnection;
|
||||
std::vector<std::unique_ptr<SOutput>> m_vOutputs;
|
||||
|
|
|
@ -13,6 +13,17 @@ enum eLogLevel
|
|||
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 {
|
||||
template <typename... Args>
|
||||
void log(eLogLevel level, const std::string& fmt, Args&&... args) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <protocols/linux-dmabuf-unstable-v1-protocol.h>
|
||||
|
||||
// --------------- 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_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);
|
||||
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] 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) {
|
||||
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
|
||||
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_roundtrip(g_pPortalManager->m_sWaylandConnection.display);
|
||||
|
||||
if (pSession->sharingData.frameInfoSHM.fmt == DRM_FORMAT_INVALID) {
|
||||
Debug::log(ERR, "[screencopy] Couldn't obtain a format from shm");
|
||||
if (pSession->sharingData.frameInfoDMA.fmt == DRM_FORMAT_INVALID) {
|
||||
Debug::log(ERR, "[screencopy] Couldn't obtain a format from dma");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -368,9 +380,9 @@ void CScreencopyPortal::startSharing(CScreencopyPortal::SSession* pSession) {
|
|||
|
||||
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) {
|
||||
|
@ -408,6 +420,11 @@ void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
|
|||
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) {
|
||||
for (auto& s : m_vSessions) {
|
||||
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_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;
|
||||
if ((prop_modifier = spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) {
|
||||
Debug::log(ERR, "[pipewire] pw requested dmabuf");
|
||||
if ((prop_modifier = spa_pod_find_prop(param, nullptr, SPA_FORMAT_VIDEO_modifier))) {
|
||||
Debug::log(TRACE, "[pipewire] pw requested dmabuf");
|
||||
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, ¶ms[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:");
|
||||
|
@ -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] | modifier: {}", PSTREAM->pwVideoInfo.modifier);
|
||||
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 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);
|
||||
|
||||
|
@ -556,6 +642,7 @@ static void pwStreamAddBuffer(void* data, pw_buffer* buffer) {
|
|||
|
||||
if ((spaData[0].type & (1u << SPA_DATA_MemFd)) > 0) {
|
||||
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) {
|
||||
type = SPA_DATA_DmaBuf;
|
||||
} else {
|
||||
|
@ -679,15 +766,73 @@ void CPipewireConnection::destroyStream(CScreencopyPortal::SSession* pSession) {
|
|||
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 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 {
|
||||
Debug::log(LOG, "[pw] Building modifiers for shm");
|
||||
|
||||
paramCount = 1;
|
||||
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;
|
||||
|
@ -716,16 +861,16 @@ void CPipewireConnection::enqueue(CScreencopyPortal::SSession* pSession) {
|
|||
}
|
||||
|
||||
spa_buffer* spaBuf = PSTREAM->currentPWBuffer->pwBuffer->buffer;
|
||||
bool corrupt = PSTREAM->pSession->sharingData.status != FRAME_READY;
|
||||
if (corrupt)
|
||||
const bool CORRUPT = PSTREAM->pSession->sharingData.status != FRAME_READY;
|
||||
if (CORRUPT)
|
||||
Debug::log(TRACE, "[pw] buffer corrupt");
|
||||
|
||||
Debug::log(TRACE, "[pw] Enqueue data:");
|
||||
|
||||
spa_meta_header* header;
|
||||
if ((header = (spa_meta_header*)spa_buffer_find_meta_data(spaBuf, SPA_META_Header, sizeof(*header)))) {
|
||||
spa_meta_header* header = (spa_meta_header*)spa_buffer_find_meta_data(spaBuf, SPA_META_Header, sizeof(*header));
|
||||
if (header) {
|
||||
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->dts_offset = 0;
|
||||
Debug::log(TRACE, "[pw] | seq {}", header->seq);
|
||||
|
@ -734,10 +879,10 @@ void CPipewireConnection::enqueue(CScreencopyPortal::SSession* pSession) {
|
|||
|
||||
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++) {
|
||||
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] | fd {}", datas[plane].fd);
|
||||
|
@ -776,14 +921,75 @@ void CPipewireConnection::dequeue(CScreencopyPortal::SSession* pSession) {
|
|||
std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::SPWStream* pStream, bool dmabuf) {
|
||||
std::unique_ptr<SBuffer> pBuffer = std::make_unique<SBuffer>();
|
||||
|
||||
pBuffer->isDMABUF = dmabuf;
|
||||
|
||||
Debug::log(TRACE, "[pw] createBuffer: type {}", dmabuf ? "dma" : "shm");
|
||||
|
||||
if (dmabuf) {
|
||||
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 {
|
||||
|
||||
pBuffer->w = pStream->pSession->sharingData.frameInfoSHM.w;
|
||||
pBuffer->h = pStream->pSession->sharingData.frameInfoSHM.h;
|
||||
pBuffer->fmt = pStream->pSession->sharingData.frameInfoSHM.fmt;
|
||||
pBuffer->isDMABUF = dmabuf;
|
||||
|
||||
if (dmabuf) {
|
||||
// todo
|
||||
} else {
|
||||
|
||||
pBuffer->planeCount = 1;
|
||||
pBuffer->size[0] = pStream->pSession->sharingData.frameInfoSHM.size;
|
||||
|
@ -811,3 +1017,20 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
|
|||
|
||||
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]);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "../shared/ScreencopyShared.hpp"
|
||||
#include <gbm.h>
|
||||
|
||||
#define FRAMERATE 60
|
||||
|
||||
enum cursorModes
|
||||
{
|
||||
HIDDEN = 1,
|
||||
|
@ -28,6 +26,7 @@ enum frameStatus
|
|||
FRAME_QUEUED,
|
||||
FRAME_READY,
|
||||
FRAME_FAILED,
|
||||
FRAME_RENEG,
|
||||
};
|
||||
|
||||
struct pw_context;
|
||||
|
@ -78,6 +77,7 @@ class CScreencopyPortal {
|
|||
uint32_t tvNsec = 0;
|
||||
uint64_t tvTimestampNs = 0;
|
||||
uint32_t nodeID = 0;
|
||||
uint32_t framerate = 60;
|
||||
|
||||
struct {
|
||||
uint32_t w = 0, h = 0, size = 0, stride = 0, fmt = 0;
|
||||
|
@ -93,6 +93,7 @@ class CScreencopyPortal {
|
|||
};
|
||||
|
||||
void startFrameCopy(SSession* pSession);
|
||||
void queueNextShareFrame(SSession* pSession);
|
||||
|
||||
std::unique_ptr<CPipewireConnection> m_pPipewire;
|
||||
|
||||
|
@ -134,17 +135,19 @@ class CPipewireConnection {
|
|||
SBuffer* currentPWBuffer = nullptr;
|
||||
spa_video_info_raw pwVideoInfo;
|
||||
uint32_t seq = 0;
|
||||
bool isDMA = false;
|
||||
|
||||
std::vector<std::unique_ptr<SBuffer>> buffers;
|
||||
};
|
||||
|
||||
std::unique_ptr<SBuffer> createBuffer(SPWStream* pStream, bool dmabuf);
|
||||
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:
|
||||
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);
|
||||
|
||||
pw_context* m_pContext = nullptr;
|
||||
|
|
|
@ -118,7 +118,7 @@ std::string getRandName(std::string prefix) {
|
|||
(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) {
|
||||
case SPA_VIDEO_FORMAT_BGRA: return SPA_VIDEO_FORMAT_BGRx;
|
||||
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_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_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];
|
||||
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_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
|
||||
|
|
|
@ -41,6 +41,7 @@ SSelectionData promptForScreencopySelection();
|
|||
uint32_t drmFourccFromSHM(wl_shm_format format);
|
||||
spa_video_format pwFromDrmFourcc(uint32_t format);
|
||||
wl_shm_format wlSHMFromDrmFourcc(uint32_t format);
|
||||
spa_video_format pwStripAlpha(spa_video_format format);
|
||||
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* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier);
|
||||
|
|
Loading…
Reference in a new issue