core: GPU hotplug support (#130)

This commit is contained in:
maround95 2025-01-10 20:39:08 +02:00 committed by GitHub
parent c2369bc3ab
commit 03677f8561
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 123 additions and 7 deletions

View file

@ -26,5 +26,6 @@ namespace Aquamarine {
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend() = 0;
virtual int drmFD() = 0;
virtual eAllocatorType type() = 0;
virtual void destroyBuffers();
};
};

View file

@ -46,6 +46,7 @@ namespace Aquamarine {
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend();
virtual int drmFD();
virtual eAllocatorType type();
virtual void destroyBuffers();
//
Hyprutils::Memory::CWeakPointer<CGBMAllocator> self;

View file

@ -117,6 +117,9 @@ namespace Aquamarine {
// utils
int reopenDRMNode(int drmFD, bool allowRenderNode = true);
// called when a new DRM card is hotplugged
void onNewGpu(std::string path);
struct {
Hyprutils::Signal::CSignal newOutput;
Hyprutils::Signal::CSignal newPointer;
@ -126,6 +129,8 @@ namespace Aquamarine {
Hyprutils::Signal::CSignal newTablet;
Hyprutils::Signal::CSignal newTabletTool;
Hyprutils::Signal::CSignal newTabletPad;
Hyprutils::Signal::CSignal pollFDsChanged;
} events;
Hyprutils::Memory::CSharedPointer<IAllocator> primaryAllocator;
@ -159,5 +164,7 @@ namespace Aquamarine {
std::mutex loopRequestMutex;
std::mutex eventLock;
} m_sEventLoopInternals;
friend class CDRMBackend;
};
};

View file

@ -389,6 +389,9 @@ namespace Aquamarine {
CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);
static std::vector<Hyprutils::Memory::CSharedPointer<CDRMBackend>> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend);
static Hyprutils::Memory::CSharedPointer<CDRMBackend> fromGpu(std::string path, Hyprutils::Memory::CSharedPointer<CBackend> backend,
Hyprutils::Memory::CSharedPointer<CDRMBackend> primary);
bool registerGPU(Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu_, Hyprutils::Memory::CSharedPointer<CDRMBackend> primary_ = {});
bool checkFeatures();
bool initResources();

View file

@ -0,0 +1,3 @@
#include <aquamarine/allocator/Allocator.hpp>
void Aquamarine::IAllocator::destroyBuffers() {}

View file

@ -228,14 +228,16 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
}
Aquamarine::CGBMBuffer::~CGBMBuffer() {
for (size_t i = 0; i < (size_t)attrs.planes; i++) {
close(attrs.fds.at(i));
}
events.destroy.emit();
if (bo) {
if (gboMapping)
gbm_bo_unmap(bo, gboMapping); // FIXME: is it needed before destroy?
gbm_bo_destroy(bo);
}
for (size_t i = 0; i < (size_t)attrs.planes; i++)
close(attrs.fds.at(i));
}
eBufferCapability Aquamarine::CGBMBuffer::caps() {
@ -280,9 +282,23 @@ void Aquamarine::CGBMBuffer::endDataPtr() {
}
}
void CGBMAllocator::destroyBuffers() {
for (auto& buf : buffers) {
buf.reset();
}
}
CGBMAllocator::~CGBMAllocator() {
if (gbmDevice)
gbm_device_destroy(gbmDevice);
if (!gbmDevice)
return;
int fd = gbm_device_get_fd(gbmDevice);
gbm_device_destroy(gbmDevice);
if (fd < 0)
return;
close(fd);
}
SP<CGBMAllocator> Aquamarine::CGBMAllocator::create(int drmfd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_) {

View file

@ -271,6 +271,27 @@ void Aquamarine::CBackend::dispatchIdle() {
updateIdleTimer();
}
void Aquamarine::CBackend::onNewGpu(std::string path) {
const auto primary = std::ranges::find_if(implementations, [](SP<IBackendImplementation> value) { return value->type() == Aquamarine::AQ_BACKEND_DRM; });
const auto primaryDrm = primary != implementations.end() ? ((Aquamarine::CDRMBackend*)(*primary).get())->self.lock() : nullptr;
auto ref = CDRMBackend::fromGpu(path, self.lock(), primaryDrm);
if (!ref) {
log(AQ_LOG_ERROR, std::format("DRM Backend failed for device {}", path));
return;
}
if (!ref->start()) {
log(AQ_LOG_ERROR, std::format("Couldn't start DRM Backend for device {}", path));
return;
}
implementations.emplace_back(ref);
events.pollFDsChanged.emit();
ref->onReady(); // Renderer created here
ref->recheckOutputs(); // Now we can recheck outputs
}
// Yoinked from wlroots, render/allocator/allocator.c
// Ref-counting reasons, see https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
int Aquamarine::CBackend::reopenDRMNode(int drmFD, bool allowRenderNode) {

View file

@ -296,6 +296,12 @@ void Aquamarine::CSession::dispatchUdevEvents() {
}
}
if (!sessionDevice && action == std::string{"add"}) {
backend->onNewGpu(devnode);
udev_device_unref(device);
return;
}
if (!sessionDevice) {
udev_device_unref(device);
return;
@ -330,6 +336,7 @@ void Aquamarine::CSession::dispatchUdevEvents() {
} else if (action == std::string{"remove"}) {
backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} removed", sysname ? sysname : "unknown"));
sessionDevice->events.remove.emit();
std::erase_if(sessionDevices, [sessionDevice](const auto& sd) { return sd == sessionDevice; });
}
udev_device_unref(device);

View file

@ -181,6 +181,43 @@ static std::vector<SP<CSessionDevice>> scanGPUs(SP<CBackend> backend) {
return vecDevices;
}
SP<CDRMBackend> Aquamarine::CDRMBackend::fromGpu(std::string path, SP<CBackend> backend, SP<CDRMBackend> primary) {
auto gpu = CSessionDevice::openIfKMS(backend->session, path);
if (!gpu) {
return nullptr;
}
auto drmBackend = SP<CDRMBackend>(new CDRMBackend(backend));
drmBackend->self = drmBackend;
if (!drmBackend->registerGPU(gpu, primary)) {
backend->log(AQ_LOG_ERROR, std::format("drm: Failed to register gpu {}", gpu->path));
return nullptr;
} else
backend->log(AQ_LOG_DEBUG, std::format("drm: Registered gpu {}", gpu->path));
if (!drmBackend->checkFeatures()) {
backend->log(AQ_LOG_ERROR, "drm: Failed checking features");
return nullptr;
}
if (!drmBackend->initResources()) {
backend->log(AQ_LOG_ERROR, "drm: Failed initializing resources");
return nullptr;
}
backend->log(AQ_LOG_DEBUG, std::format("drm: Basic init pass for gpu {}", gpu->path));
drmBackend->grabFormats();
drmBackend->dumbAllocator = CDRMDumbAllocator::create(gpu->fd, backend);
// so that session can handle udev change/remove events for this gpu
backend->session->sessionDevices.push_back(gpu);
return drmBackend;
}
std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backend) {
if (!backend->session)
backend->session = CSession::attempt(backend);
@ -269,7 +306,15 @@ std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backe
}
Aquamarine::CDRMBackend::~CDRMBackend() {
;
for (auto conn : connectors) {
conn->disconnect();
conn.reset();
}
rendererState.allocator->destroyBuffers();
rendererState.renderer.reset();
rendererState.allocator.reset();
}
void Aquamarine::CDRMBackend::log(eBackendLogLevel l, const std::string& s) {
@ -663,8 +708,10 @@ bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBacken
}
});
listeners.gpuRemove = gpu->events.remove.registerListener(
[this](std::any d) { backend->log(AQ_LOG_ERROR, std::format("drm: !!!!FIXME: Got a remove event for {}, this is not handled properly!!!!!", gpuName)); });
listeners.gpuRemove = gpu->events.remove.registerListener([this](std::any d) {
std::erase_if(backend->implementations, [this](const auto& impl) { return impl->drmFD() == this->drmFD(); });
backend->events.pollFDsChanged.emit();
});
return true;
}

View file

@ -197,6 +197,15 @@ bool CDRMRenderer::initDRMFormats() {
return true;
}
Aquamarine::CDRMRenderer::~CDRMRenderer() {
eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(egl.display, egl.context);
eglTerminate(egl.display);
eglReleaseThread();
}
SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, SP<CBackend> backend_) {
SP<CDRMRenderer> renderer = SP<CDRMRenderer>(new CDRMRenderer());
renderer->drmFD = allocator_->drmFD();

View file

@ -42,6 +42,7 @@ namespace Aquamarine {
class CDRMRenderer {
public:
~CDRMRenderer();
static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_,
Hyprutils::Memory::CSharedPointer<CBackend> backend_);