Hyprland/src/protocols/MesaDRM.cpp
Tom Englund b329ea8e96
syncobj: use rendernode for timelines (#11087)
* syncobj: use rendernode for timelines

use rendernode for timelines instead of the drmfd, some devices dont
support to use the drmfd for this.

* opengl: use rendernode if available

use rendernode if available for CHyprOpenglImpl

* MesaDRM: use the m_drmRenderNodeFD if it exist

try use the rendernode we got from AQ if it exist.

* linuxdmabuf: use rendernode if available

use the rendernode if available already from AQ

* syncobj: prefer rendernode over displaynode

prefer the rendernode over the displaynode, and log a error if
attempting to use the protocol without explicit sync support on any of
the nodes.

* syncobj: check support on both nodes always

check support on both nodes always so it can be used later for
preferring rendernode if possible in syncobj protocol.

* syncobj: remove old var in non linux if else case

remove old m_bDrmSyncobjTimelineSupported from non linux if else case
that will fail to compile on non linux. the nodes sets support by
default to false, and if non linux it wont check for support and set it
to true.

* build: bump aq requirement

bump to 0.9.3 where rendernode support got added.

* flake.lock: update

* renderer: glfinish on software renderer

software renderers apparently bug out on implicit sync, use glfinish as
with nvidia case on implicit paths.

* flake.lock: update

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
2025-08-24 22:32:13 +02:00

155 lines
5.7 KiB
C++

#include "MesaDRM.hpp"
#include <algorithm>
#include <xf86drm.h>
#include "../Compositor.hpp"
#include "types/WLBuffer.hpp"
#include "../render/OpenGL.hpp"
CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs_) {
LOGM(LOG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes);
for (int i = 0; i < attrs_.planes; ++i) {
LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]);
}
m_buffer = makeShared<CDMABuffer>(id, client, attrs_);
m_buffer->m_resource->m_buffer = m_buffer;
m_listeners.bufferResourceDestroy = m_buffer->events.destroy.listen([this] {
m_listeners.bufferResourceDestroy.reset();
PROTO::mesaDRM->destroyResource(this);
});
if (!m_buffer->m_success)
LOGM(ERR, "Possibly compositor bug: buffer failed to create");
}
CMesaDRMBufferResource::~CMesaDRMBufferResource() {
if (m_buffer && m_buffer->m_resource)
m_buffer->m_resource->sendRelease();
m_buffer.reset();
m_listeners.bufferResourceDestroy.reset();
}
bool CMesaDRMBufferResource::good() {
return m_buffer && m_buffer->good();
}
CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : m_resource(resource_) {
if UNLIKELY (!good())
return;
m_resource->setOnDestroy([this](CWlDrm* r) { PROTO::mesaDRM->destroyResource(this); });
m_resource->setAuthenticate([this](CWlDrm* r, uint32_t token) {
// we don't need this
m_resource->sendAuthenticated();
});
m_resource->setCreateBuffer(
[](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, uint32_t) { r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); });
m_resource->setCreatePlanarBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) {
r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead");
});
m_resource->setCreatePrimeBuffer(
[this](CWlDrm* r, uint32_t id, int32_t nameFd, int32_t w, int32_t h, uint32_t fmt, int32_t off0, int32_t str0, int32_t off1, int32_t str1, int32_t off2, int32_t str2) {
if (off0 < 0 || w <= 0 || h <= 0) {
r->error(WL_DRM_ERROR_INVALID_FORMAT, "Invalid w, h, or offset");
return;
}
uint64_t mod = DRM_FORMAT_MOD_INVALID;
auto fmts = g_pHyprOpenGL->getDRMFormats();
for (auto const& f : fmts) {
if (f.drmFormat != fmt)
continue;
for (auto const& m : f.modifiers) {
if (m == DRM_FORMAT_MOD_LINEAR)
continue;
mod = m;
break;
}
break;
}
Aquamarine::SDMABUFAttrs attrs;
attrs.success = true;
attrs.size = {w, h};
attrs.modifier = mod;
attrs.planes = 1;
attrs.offsets[0] = off0;
attrs.strides[0] = str0;
attrs.fds[0] = nameFd;
attrs.format = fmt;
const auto RESOURCE = PROTO::mesaDRM->m_buffers.emplace_back(makeShared<CMesaDRMBufferResource>(id, m_resource->client(), attrs));
if UNLIKELY (!RESOURCE->good()) {
r->noMemory();
PROTO::mesaDRM->m_buffers.pop_back();
return;
}
// append instance so that buffer knows its owner
RESOURCE->m_buffer->m_resource->m_buffer = RESOURCE->m_buffer;
});
m_resource->sendDevice(PROTO::mesaDRM->m_nodeName.c_str());
m_resource->sendCapabilities(WL_DRM_CAPABILITY_PRIME);
auto fmts = g_pHyprOpenGL->getDRMFormats();
for (auto const& fmt : fmts) {
m_resource->sendFormat(fmt.drmFormat);
}
}
bool CMesaDRMResource::good() {
return m_resource->resource();
}
CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
drmDevice* dev = nullptr;
int drmFD = g_pCompositor->m_drmRenderNode.fd >= 0 ? g_pCompositor->m_drmRenderNode.fd : g_pCompositor->m_drm.fd;
if (drmGetDevice2(drmFD, 0, &dev) != 0) {
LOGM(ERR, "Failed to get device from fd {}, disabling MesaDRM", drmFD);
removeGlobal();
return;
}
if (dev->available_nodes & (1 << DRM_NODE_RENDER) && dev->nodes[DRM_NODE_RENDER]) {
m_nodeName = dev->nodes[DRM_NODE_RENDER];
} else if (dev->available_nodes & (1 << DRM_NODE_PRIMARY) && dev->nodes[DRM_NODE_PRIMARY]) {
LOGM(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]);
m_nodeName = dev->nodes[DRM_NODE_PRIMARY];
} else {
LOGM(ERR, "No usable DRM node (render or primary) found, disabling MesaDRM");
drmFreeDevice(&dev);
removeGlobal();
return;
}
drmFreeDevice(&dev);
}
void CMesaDRMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_managers.emplace_back(makeShared<CMesaDRMResource>(makeShared<CWlDrm>(client, ver, id)));
if UNLIKELY (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_managers.pop_back();
return;
}
}
void CMesaDRMProtocol::destroyResource(CMesaDRMResource* resource) {
std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; });
}
void CMesaDRMProtocol::destroyResource(CMesaDRMBufferResource* resource) {
std::erase_if(m_buffers, [&](const auto& other) { return other.get() == resource; });
}