DRM: Steady progress on getting a usable session

modesetting, rendering, etc. Hyprland now renders properly, although input devices are yet to be implemented.
This commit is contained in:
Vaxry 2024-06-24 23:22:02 +02:00
parent f888bfb6e4
commit 2e0052a21d
15 changed files with 769 additions and 30 deletions

View File

@ -16,7 +16,7 @@ set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET libseat libinput wayland-client wayland-protocols hyprutils>=0.1.2 pixman-1 wayland-client libdrm gbm libudev)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET libseat libinput wayland-client wayland-protocols hyprutils>=0.1.2 pixman-1 wayland-client libdrm gbm libudev libdisplay-info)
configure_file(aquamarine.pc.in aquamarine.pc @ONLY)

View File

@ -9,7 +9,8 @@ namespace Aquamarine {
struct SAllocatorBufferParams {
Hyprutils::Math::Vector2D size;
uint32_t format = DRM_FORMAT_INVALID;
uint32_t format = DRM_FORMAT_INVALID;
bool scanout = false;
};
class IAllocator {

View File

@ -7,7 +7,8 @@ namespace Aquamarine {
struct SSwapchainOptions {
size_t length = 0;
Hyprutils::Math::Vector2D size;
uint32_t format = DRM_FORMAT_INVALID;
uint32_t format = DRM_FORMAT_INVALID;
bool scanout = false;
};
class CSwapchain {

View File

@ -100,6 +100,9 @@ namespace Aquamarine {
/* Get the primary DRM FD */
int drmFD();
/* Get the render formats the primary backend supports */
std::vector<SDRMFormat> getPrimaryRenderFormats();
struct {
Hyprutils::Signal::CSignal newOutput;
Hyprutils::Signal::CSignal newPointer;

View File

@ -10,16 +10,51 @@
namespace Aquamarine {
class CDRMBackend;
class CDRMFB;
struct SDRMConnector;
struct SDRMFB {
typedef std::function<void(void)> FIdleCallback;
class CDRMBufferUnimportable : public IAttachment {
public:
CDRMBufferUnimportable() {
;
}
virtual ~CDRMBufferUnimportable() {
;
}
virtual eAttachmentType type() {
return AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE;
}
};
class CDRMFB {
public:
~CDRMFB();
static Hyprutils::Memory::CSharedPointer<CDRMFB> create(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_);
void closeHandles();
// drops the buffer from KMS
void drop();
uint32_t id = 0;
Hyprutils::Memory::CSharedPointer<IBuffer> buffer;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
private:
CDRMFB(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_);
uint32_t submitBuffer();
bool dropped = false, handlesClosed = false;
std::array<uint32_t, 4> boHandles = {0};
};
struct SDRMLayer {
Hyprutils::Memory::CSharedPointer<SDRMFB> current /* displayed */, queued /* submitted */, pending /* to be submitted */;
// we expect the consumers to use double-buffering, so we keep the 2 last FBs around. If any of these goes out of
// scope, the DRM FB will be destroyed, but the IBuffer will stay, as long as it's ref'd somewhere.
Hyprutils::Memory::CSharedPointer<CDRMFB> front /* currently displaying */, back;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
};
@ -30,7 +65,7 @@ namespace Aquamarine {
uint32_t id = 0;
uint32_t initialID = 0;
Hyprutils::Memory::CSharedPointer<SDRMFB> current /* displayed */, queued /* submitted */;
Hyprutils::Memory::CSharedPointer<CDRMFB> front /* currently displaying */, back /* submitted */;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
Hyprutils::Memory::CWeakPointer<SDRMPlane> self;
std::vector<SDRMFormat> formats;
@ -108,12 +143,29 @@ namespace Aquamarine {
private:
CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, Hyprutils::Memory::CSharedPointer<SDRMConnector> connector_);
bool commitState(bool onlyTest = false);
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
Hyprutils::Memory::CSharedPointer<SDRMConnector> connector;
friend struct SDRMConnector;
};
struct SDRMPageFlip {
Hyprutils::Memory::CWeakPointer<SDRMConnector> connector;
};
struct SDRMConnectorCommitData {
Hyprutils::Memory::CSharedPointer<CDRMFB> mainFB, cursorFB;
bool modeset = false;
bool blocking = false;
uint32_t flags = 0;
bool test = false;
drmModeModeInfo modeInfo;
void calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
};
struct SDRMConnector {
~SDRMConnector();
@ -123,6 +175,10 @@ namespace Aquamarine {
Hyprutils::Memory::CSharedPointer<SDRMCRTC> getCurrentCRTC(const drmModeConnector* connector);
drmModeModeInfo* getCurrentMode();
void parseEDID(std::vector<uint8_t> data);
bool commitState(const SDRMConnectorCommitData& data);
void applyCommit(const SDRMConnectorCommitData& data);
void rollbackCommit(const SDRMConnectorCommitData& data);
void onPresent();
Hyprutils::Memory::CSharedPointer<CDRMOutput> output;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
@ -135,10 +191,16 @@ namespace Aquamarine {
int32_t refresh = 0;
uint32_t possibleCrtcs = 0;
std::string make, serial, model;
bool canDoVrr = false;
bool cursorEnabled = false;
Hyprutils::Math::Vector2D cursorPos, cursorSize, cursorHotspot;
Hyprutils::Memory::CSharedPointer<SDRMFB> pendingCursorFB;
Hyprutils::Memory::CSharedPointer<CDRMFB> pendingCursorFB;
bool isPageFlipPending = false;
SDRMPageFlip pendingPageFlip;
drmModeModeInfo fallbackModeInfo;
union UDRMConnectorProps {
struct {
@ -162,6 +224,11 @@ namespace Aquamarine {
UDRMConnectorProps props;
};
class IDRMImplementation {
public:
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) = 0;
};
class CDRMBackend : public IBackendImplementation {
public:
virtual ~CDRMBackend();
@ -178,6 +245,11 @@ namespace Aquamarine {
Hyprutils::Memory::CWeakPointer<CDRMBackend> self;
void log(eBackendLogLevel, const std::string&);
bool sessionActive();
std::vector<FIdleCallback> idleCallbacks;
private:
CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);
@ -189,6 +261,7 @@ namespace Aquamarine {
void scanConnectors();
Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu;
Hyprutils::Memory::CSharedPointer<IDRMImplementation> impl;
Hyprutils::Memory::CWeakPointer<CDRMBackend> primary;
Hyprutils::Memory::CWeakPointer<CBackend> backend;
@ -205,10 +278,13 @@ namespace Aquamarine {
} drmProps;
friend class CBackend;
friend struct SDRMFB;
friend class CDRMFB;
friend class CDRMFBAttachment;
friend struct SDRMConnector;
friend struct SDRMCRTC;
friend struct SDRMPlane;
friend struct CDRMOutput;
friend class CDRMOutput;
friend struct SDRMPageFlip;
friend class CDRMLegacyImpl;
};
};

View File

@ -0,0 +1,18 @@
#pragma once
#include "../DRM.hpp"
namespace Aquamarine {
class CDRMLegacyImpl : public IDRMImplementation {
public:
CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
private:
bool commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
bool testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
};
};

View File

@ -4,6 +4,7 @@
#include <tuple>
#include <hyprutils/signal/Signal.hpp>
#include <hyprutils/math/Region.hpp>
#include "../misc/Attachment.hpp"
namespace Aquamarine {
enum eBufferCapability {
@ -62,6 +63,8 @@ namespace Aquamarine {
Hyprutils::Math::Vector2D size;
bool opaque = false;
CAttachmentManager attachments;
struct {
Hyprutils::Signal::CSignal destroy;
} events;

View File

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include <hyprutils/memory/SharedPtr.hpp>
namespace Aquamarine {
enum eAttachmentType {
AQ_ATTACHMENT_DRM_BUFFER = 0,
AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE,
};
class IAttachment {
public:
virtual ~IAttachment() {
;
}
virtual eAttachmentType type() = 0;
};
class CAttachmentManager {
public:
bool has(eAttachmentType type);
Hyprutils::Memory::CSharedPointer<IAttachment> get(eAttachmentType type);
void add(Hyprutils::Memory::CSharedPointer<IAttachment> attachment);
void remove(Hyprutils::Memory::CSharedPointer<IAttachment> attachment);
void removeByType(eAttachmentType type);
private:
std::vector<Hyprutils::Memory::CSharedPointer<IAttachment>> attachments;
};
};

View File

@ -6,6 +6,7 @@
#include <hyprutils/memory/SharedPtr.hpp>
#include <hyprutils/math/Region.hpp>
#include <drm_fourcc.h>
#include <xf86drmMode.h>
#include "../allocator/Swapchain.hpp"
#include "../buffer/Buffer.hpp"
@ -14,9 +15,10 @@ namespace Aquamarine {
class IBackendImplementation;
struct SOutputMode {
Hyprutils::Math::Vector2D pixelSize;
unsigned int refreshRate = 0 /* in mHz */;
bool preferred = false;
Hyprutils::Math::Vector2D pixelSize;
unsigned int refreshRate = 0 /* in mHz */;
bool preferred = false;
std::optional<drmModeModeInfo> modeInfo; // if this is a drm mode, this will be populated.
};
enum eOutputPresentationMode {
@ -83,6 +85,7 @@ namespace Aquamarine {
friend class IOutput;
friend class CWaylandOutput;
friend class CDRMOutput;
};
class IOutput {
@ -106,6 +109,8 @@ namespace Aquamarine {
bool enabled = false;
bool nonDesktop = false;
eSubpixelMode subpixel = AQ_SUBPIXEL_NONE;
bool vrrCapable = false;
bool needsFrame = false;
//
std::vector<Hyprutils::Memory::CSharedPointer<SOutputMode>> modes;
@ -113,10 +118,25 @@ namespace Aquamarine {
Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
//
enum eOutputPresentFlags : uint32_t {
AQ_OUTPUT_PRESENT_VSYNC = (1 << 0),
AQ_OUTPUT_PRESENT_HW_CLOCK = (1 << 1),
AQ_OUTPUT_PRESENT_HW_COMPLETION = (1 << 2),
AQ_OUTPUT_PRESENT_ZEROCOPY = (1 << 3),
};
struct SStateEvent {
Hyprutils::Math::Vector2D size;
};
struct SPresentEvent {
bool presented = true;
timespec* when = nullptr;
unsigned int seq = 0;
int refresh = 0;
uint32_t flags = 0;
};
struct {
Hyprutils::Signal::CSignal destroy;
Hyprutils::Signal::CSignal frame;

View File

@ -15,10 +15,38 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
attrs.size = params.size;
attrs.format = params.format;
// FIXME: proper modifier support? This might implode on some GPUs on the Wayland backend
// for sure.
const auto FORMATS = allocator->backend->getPrimaryRenderFormats();
bo = gbm_bo_create(allocator->gbmDevice, params.size.x, params.size.y, params.format, GBM_BO_USE_RENDERING);
std::vector<uint64_t> explicitModifiers;
// check if we can use modifiers. If the requested support has any explicit modifier
// supported by the primary backend, we can.
allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Searching for modifiers. Format len: {}", FORMATS.size()));
for (auto& f : FORMATS) {
if (f.drmFormat != params.format)
continue;
allocator->backend->log(AQ_LOG_TRACE, "GBM: Format matched");
for (auto& m : f.modifiers) {
if (m == DRM_FORMAT_MOD_LINEAR || m == DRM_FORMAT_MOD_INVALID)
continue;
explicitModifiers.push_back(m);
allocator->backend->log(AQ_LOG_TRACE, "GBM: Modifier matched");
}
}
uint32_t flags = GBM_BO_USE_RENDERING;
if (params.scanout)
flags |= GBM_BO_USE_SCANOUT;
if (explicitModifiers.empty()) {
allocator->backend->log(AQ_LOG_WARNING, "GBM: Using modifier-less allocation");
bo = gbm_bo_create(allocator->gbmDevice, params.size.x, params.size.y, params.format, flags);
} else
bo = gbm_bo_create_with_modifiers2(allocator->gbmDevice, params.size.x, params.size.y, params.format, explicitModifiers.data(), explicitModifiers.size(), flags);
if (!bo) {
allocator->backend->log(AQ_LOG_ERROR, "GBM: Failed to allocate a GBM buffer: bo null");

View File

@ -4,6 +4,7 @@
using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer
Aquamarine::CSwapchain::CSwapchain(SP<IAllocator> allocator_) : allocator(allocator_) {
@ -15,6 +16,14 @@ bool Aquamarine::CSwapchain::reconfigure(const SSwapchainOptions& options_) {
if (!allocator)
return false;
if (options_.size == Vector2D{} || options_.length == 0) {
// clear the swapchain
allocator->getBackend()->log(AQ_LOG_DEBUG, "Swapchain: Clearing");
buffers.clear();
options = options_;
return true;
}
if (options_.format == options.format && options_.size == options.size && options_.length == options.length)
return true; // no need to reconfigure

View File

@ -99,7 +99,12 @@ bool Aquamarine::CBackend::start() {
}
// erase failed impls
std::erase_if(implementations, [](const auto& i) { return i->pollFD() < 0; });
std::erase_if(implementations, [this](const auto& i) {
bool failed = i->pollFD() < 0;
if (failed)
log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type())));
return failed;
});
// TODO: obviously change this when (if) we add different allocators.
for (auto& b : implementations) {
@ -225,6 +230,20 @@ void Aquamarine::CBackend::dispatchEventsAsync() {
}
bool Aquamarine::CBackend::hasSession() {
// TODO:
return false;
return session;
}
std::vector<SDRMFormat> Aquamarine::CBackend::getPrimaryRenderFormats() {
for (auto& b : implementations) {
if (b->type() != AQ_BACKEND_DRM && b->type() != AQ_BACKEND_WAYLAND)
continue;
return b->getRenderFormats();
}
for (auto& b : implementations) {
return b->getRenderFormats();
}
return {};
}

View File

@ -1,17 +1,21 @@
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/backend/drm/Legacy.hpp>
#include <chrono>
#include <thread>
#include <deque>
#include <cstring>
#include <sys/mman.h>
extern "C" {
#include <libseat.h>
#include <libudev.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <libdisplay-info/cvt.h>
}
#include "Props.hpp"
#include "FormatUtils.hpp"
using namespace Aquamarine;
using namespace Hyprutils::Memory;
@ -194,6 +198,14 @@ Aquamarine::CDRMBackend::~CDRMBackend() {
;
}
void Aquamarine::CDRMBackend::log(eBackendLogLevel l, const std::string& s) {
backend->log(l, s);
}
bool Aquamarine::CDRMBackend::sessionActive() {
return backend->session->active;
}
bool Aquamarine::CDRMBackend::checkFeatures() {
uint64_t curW = 0, curH = 0;
if (drmGetCap(gpu->fd, DRM_CAP_CURSOR_WIDTH, &curW))
@ -227,6 +239,11 @@ bool Aquamarine::CDRMBackend::checkFeatures() {
drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
drmProps.supportsAddFb2Modifiers = drmGetCap(gpu->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap) == 0 && cap == 1;
backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAsyncCommit: {}", drmProps.supportsAsyncCommit));
backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAddFb2Modifiers: {}", drmProps.supportsAddFb2Modifiers));
impl = makeShared<CDRMLegacyImpl>(self.lock());
// TODO: allow no-modifiers?
return true;
@ -303,11 +320,14 @@ bool Aquamarine::CDRMBackend::initResources() {
return false;
}
planes.emplace_back(aqPlane);
drmModeFreePlane(plane);
}
drmModeFreePlaneResources(planeResources);
drmModeFreeResources(resources);
return true;
}
@ -372,7 +392,9 @@ void Aquamarine::CDRMBackend::scanConnectors() {
} else
conn = *it;
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state:", (int)drmConn->connection));
backend->log(AQ_LOG_DEBUG, std::format("drm: Connectors size {}", connectors.size()));
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state: {}", connectorID, (int)drmConn->connection));
if (conn->status == DRM_MODE_DISCONNECTED && drmConn->connection == DRM_MODE_CONNECTED) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
@ -389,8 +411,6 @@ void Aquamarine::CDRMBackend::scanConnectors() {
}
bool Aquamarine::CDRMBackend::start() {
scanConnectors();
return true;
}
@ -403,7 +423,38 @@ int Aquamarine::CDRMBackend::drmFD() {
}
static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) {
// FIXME:
auto pageFlip = (SDRMPageFlip*)data;
if (!pageFlip->connector)
return;
pageFlip->connector->isPageFlipPending = false;
const auto& BACKEND = pageFlip->connector->backend;
BACKEND->log(AQ_LOG_TRACE, std::format("drm: pf event seq {} sec {} usec {} crtc {}", seq, tv_sec, tv_usec, crtc_id));
if (pageFlip->connector->status != DRM_MODE_CONNECTED || !pageFlip->connector->crtc) {
BACKEND->log(AQ_LOG_DEBUG, "drm: Ignoring a pf event from a disabled crtc / connector");
return;
}
pageFlip->connector->onPresent();
uint32_t flags = IOutput::AQ_OUTPUT_PRESENT_VSYNC | IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION | IOutput::AQ_OUTPUT_PRESENT_ZEROCOPY;
timespec presented = {.tv_sec = tv_sec, .tv_nsec = tv_usec * 1000};
pageFlip->connector->output->events.present.emit(IOutput::SPresentEvent{
.presented = BACKEND->sessionActive(),
.when = &presented,
.seq = seq,
.refresh = (int)(pageFlip->connector->refresh ? (1000000000000LL / pageFlip->connector->refresh) : 0),
.flags = flags,
});
if (BACKEND->sessionActive())
pageFlip->connector->output->events.frame.emit();
}
bool Aquamarine::CDRMBackend::dispatchEvents() {
@ -415,6 +466,13 @@ bool Aquamarine::CDRMBackend::dispatchEvents() {
if (drmHandleEvent(gpu->fd, &event) != 0)
backend->log(AQ_LOG_ERROR, std::format("drm: Failed to handle event on fd {}", gpu->fd));
if (!idleCallbacks.empty()) {
for (auto& c : idleCallbacks) {
c();
}
idleCallbacks.clear();
}
return true;
}
@ -427,7 +485,22 @@ bool Aquamarine::CDRMBackend::setCursor(SP<IBuffer> buffer, const Hyprutils::Mat
}
void Aquamarine::CDRMBackend::onReady() {
;
backend->log(AQ_LOG_DEBUG, std::format("drm: Connectors size2 {}", connectors.size()));
for (auto& c : connectors) {
backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {}", c->id));
if (!c->output)
continue;
backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {} has output name {}", c->id, c->output->name));
// swapchain has to be created here because allocator is absent in connect if not ready
c->output->swapchain = makeShared<CSwapchain>(backend->allocator);
c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true}); // mark the swapchain for scanout
c->output->needsFrame = true;
backend->events.newOutput.emit(SP<IOutput>(c->output));
}
}
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
@ -463,26 +536,40 @@ bool Aquamarine::SDRMPlane::init(drmModePlane* plane) {
initialID = id;
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Plane {} has type {}", id, (int)type));
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Plane {} has {} formats", id, plane->count_formats));
for (size_t i = 0; i < plane->count_formats; ++i) {
if (type != DRM_PLANE_TYPE_CURSOR)
formats.emplace_back(SDRMFormat{.drmFormat = plane->formats[i], .modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID}});
else
formats.emplace_back(SDRMFormat{.drmFormat = plane->formats[i], .modifiers = {DRM_FORMAT_MOD_LINEAR}});
backend->backend->log(AQ_LOG_TRACE, std::format("drm: | Format {}", fourccToName(plane->formats[i])));
}
if (props.in_formats && backend->drmProps.supportsAddFb2Modifiers) {
backend->backend->log(AQ_LOG_DEBUG, "drm: Plane: checking for modifiers");
uint64_t blobID = 0;
if (!getDRMProp(backend->gpu->fd, id, props.in_formats, &blobID))
if (!getDRMProp(backend->gpu->fd, id, props.in_formats, &blobID)) {
backend->backend->log(AQ_LOG_ERROR, "drm: Plane: No blob id");
return false;
}
auto blob = drmModeGetPropertyBlob(backend->gpu->fd, blobID);
if (!blob)
if (!blob) {
backend->backend->log(AQ_LOG_ERROR, "drm: Plane: No property");
return false;
}
drmModeFormatModifierIterator iter = {0};
while (drmModeFormatModifierBlobIterNext(blob, &iter)) {
auto it = std::find_if(formats.begin(), formats.end(), [iter](const auto& e) { return e.drmFormat == iter.fmt; });
backend->backend->log(AQ_LOG_TRACE, std::format("drm: | Modifier {} with format {}", iter.mod, fourccToName(iter.fmt)));
if (it == formats.end())
formats.emplace_back(SDRMFormat{.drmFormat = iter.fmt, .modifiers = {iter.mod}});
else
@ -543,7 +630,8 @@ SP<SDRMCRTC> Aquamarine::SDRMConnector::getCurrentCRTC(const drmModeConnector* c
}
bool Aquamarine::SDRMConnector::init(drmModeConnector* connector) {
id = connector->connector_id;
id = connector->connector_id;
pendingPageFlip.connector = self.lock();
if (!getDRMConnectorProps(backend->gpu->fd, id, &props))
return false;
@ -640,10 +728,14 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
continue;
}
if (i == 1)
fallbackModeInfo = drmMode;
auto aqMode = makeShared<SOutputMode>();
aqMode->pixelSize = {drmMode.hdisplay, drmMode.vdisplay};
aqMode->refreshRate = calculateRefresh(drmMode);
aqMode->preferred = (drmMode.type & DRM_MODE_TYPE_PREFERRED);
aqMode->modeInfo = drmMode;
output->modes.emplace_back(aqMode);
@ -682,6 +774,9 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
output->nonDesktop = prop;
}
canDoVrr = props.vrr_capable && crtc->props.vrr_enabled && !getDRMProp(backend->gpu->fd, id, props.vrr_capable, &prop) && prop;
output->vrrCapable = canDoVrr;
maxBpcBounds.fill(0);
if (props.max_bpc && !introspectDRMPropRange(backend->gpu->fd, props.max_bpc, maxBpcBounds.data(), &maxBpcBounds[1]))
@ -702,12 +797,18 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
output->model = model;
output->serial = serial;
output->description = std::format("{} {} {} ({})", make, model, serial, szName);
output->needsFrame = true;
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Description {}", output->description));
status = DRM_MODE_CONNECTED;
if (!backend->backend->ready)
return;
output->swapchain = makeShared<CSwapchain>(backend->backend->allocator);
backend->backend->events.newOutput.emit(output);
output->scheduleFrame();
}
void Aquamarine::SDRMConnector::disconnect() {
@ -722,16 +823,162 @@ void Aquamarine::SDRMConnector::disconnect() {
status = DRM_MODE_DISCONNECTED;
}
bool Aquamarine::SDRMConnector::commitState(const SDRMConnectorCommitData& data) {
const bool ok = backend->impl->commit(self.lock(), data);
if (ok && !data.test)
applyCommit(data);
else
rollbackCommit(data);
return ok;
}
void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data) {
crtc->primary->back = crtc->primary->front;
crtc->primary->front = data.mainFB;
if (crtc->cursor) {
crtc->cursor->back = crtc->cursor->front;
crtc->cursor->front = data.cursorFB;
}
pendingCursorFB.reset();
if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
refresh = calculateRefresh(data.modeInfo);
}
void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) {
;
}
void Aquamarine::SDRMConnector::onPresent() {
;
}
Aquamarine::CDRMOutput::~CDRMOutput() {
;
}
bool Aquamarine::CDRMOutput::commit() {
return true;
return commitState();
}
bool Aquamarine::CDRMOutput::test() {
return true;
return commitState(true);
}
bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (!backend->backend->session->active) {
backend->backend->log(AQ_LOG_ERROR, "drm: Session inactive");
return false;
}
if (!connector->crtc) {
backend->backend->log(AQ_LOG_ERROR, "drm: No CRTC attached to output");
return false;
}
const auto& STATE = state->state();
const uint32_t COMMITTED = STATE.committed;
if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED) && STATE.enabled) {
if (!STATE.mode && STATE.customMode) {
backend->backend->log(AQ_LOG_ERROR, "drm: No mode on enable commit");
return false;
}
}
if (STATE.adaptiveSync && !connector->canDoVrr) {
backend->backend->log(AQ_LOG_ERROR, "drm: No Adaptive sync support for output");
return false;
}
if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE && !backend->drmProps.supportsAsyncCommit) {
backend->backend->log(AQ_LOG_ERROR, "drm: No Immediate presentation support in the backend");
return false;
}
if (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER && !STATE.buffer) {
backend->backend->log(AQ_LOG_ERROR, "drm: No buffer committed");
return false;
}
// If we are changing the rendering format, we may need to reconfigure the output (aka modeset)
// which may result in some glitches
const bool NEEDS_RECONFIG = COMMITTED &
(COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED | COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_FORMAT |
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_MODE);
const bool BLOCKING = NEEDS_RECONFIG || !(COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER);
const auto MODE = STATE.mode ? STATE.mode : STATE.customMode;
uint32_t flags = 0;
if (!onlyTest) {
if (NEEDS_RECONFIG) {
if (STATE.enabled)
backend->backend->log(AQ_LOG_DEBUG,
std::format("drm: Modesetting {} with {}x{}@{:.2f}Hz", name, (int)MODE->pixelSize.x, (int)MODE->pixelSize.y, MODE->refreshRate / 1000.F));
else
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Disabling output {}", name));
}
if (!BLOCKING && connector->isPageFlipPending) {
backend->backend->log(AQ_LOG_ERROR, "drm: Cannot commit when a page-flip is awaiting");
return false;
}
if (STATE.enabled)
flags |= DRM_MODE_PAGE_FLIP_EVENT;
if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE)
flags |= DRM_MODE_PAGE_FLIP_ASYNC;
}
SDRMConnectorCommitData data;
if (STATE.buffer) {
backend->backend->log(AQ_LOG_TRACE, "drm: Committed a buffer, updating state");
SP<CDRMFB> drmFB;
auto buf = STATE.buffer;
// try to find the buffer in its layer
if (connector->crtc->primary->back && connector->crtc->primary->back->buffer == buf) {
backend->backend->log(AQ_LOG_TRACE, "drm: CRTC's back buffer matches committed :D");
drmFB = connector->crtc->primary->back;
} else if (connector->crtc->primary->front && connector->crtc->primary->front->buffer == buf) {
backend->backend->log(AQ_LOG_TRACE, "drm: CRTC's front buffer matches committed");
drmFB = connector->crtc->primary->front;
}
if (!drmFB)
drmFB = CDRMFB::create(buf, backend);
if (!drmFB) {
backend->backend->log(AQ_LOG_ERROR, "drm: Buffer failed to import to KMS");
return false;
}
data.mainFB = drmFB;
}
data.blocking = BLOCKING;
data.modeset = NEEDS_RECONFIG;
data.flags = flags;
data.test = onlyTest;
if (MODE->modeInfo.has_value())
data.modeInfo = *MODE->modeInfo;
else
data.calculateMode(connector);
bool ok = connector->commitState(data);
events.commit.emit();
state->onCommit();
return ok;
}
SP<IBackendImplementation> Aquamarine::CDRMOutput::getBackend() {
@ -747,14 +994,175 @@ void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord) {
}
void Aquamarine::CDRMOutput::scheduleFrame() {
;
if (connector->isPageFlipPending)
return;
backend->idleCallbacks.emplace_back([this]() { events.frame.emit(); });
}
Vector2D Aquamarine::CDRMOutput::maxCursorSize() {
return backend->drmProps.cursorSize;
}
Aquamarine::CDRMOutput::CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, Hyprutils::Memory::CSharedPointer<SDRMConnector> connector_) :
Aquamarine::CDRMOutput::CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, SP<SDRMConnector> connector_) :
backend(backend_), connector(connector_) {
name = name_;
}
SP<CDRMFB> Aquamarine::CDRMFB::create(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_) {
auto fb = SP<CDRMFB>(new CDRMFB(buffer_, backend_));
if (!fb->id)
return nullptr;
return fb;
}
Aquamarine::CDRMFB::CDRMFB(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_) : buffer(buffer_), backend(backend_) {
auto attrs = buffer->dmabuf();
if (!attrs.success) {
backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted has no dmabuf");
return;
}
if (buffer->attachments.has(AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE)) {
backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted is unimportable");
return;
}
// TODO: check format
for (int i = 0; i < attrs.planes; ++i) {
int ret = drmPrimeFDToHandle(backend->gpu->fd, attrs.fds.at(i), &boHandles.at(i));
if (ret) {
backend->backend->log(AQ_LOG_ERROR, "drm: drmPrimeFDToHandle failed");
drop();
return;
}
backend->backend->log(AQ_LOG_TRACE, std::format("drm: CDRMFB: plane {} has fd {}, got handle {}", i, attrs.fds.at(i), boHandles.at(i)));
}
id = submitBuffer();
if (!id) {
backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer to KMS");
buffer->attachments.add(makeShared<CDRMBufferUnimportable>());
drop();
return;
}
backend->backend->log(AQ_LOG_TRACE, std::format("drm: new buffer {}", id));
// FIXME: wlroots does this, I am unsure why, but if I do, the gpu driver will kill us.
// closeHandles();
}
Aquamarine::CDRMFB::~CDRMFB() {
drop();
}
void Aquamarine::CDRMFB::closeHandles() {
if (handlesClosed)
return;
handlesClosed = true;
for (auto& h : boHandles) {
if (h == 0)
continue;
if (drmCloseBufferHandle(backend->gpu->fd, h))
backend->backend->log(AQ_LOG_ERROR, "drm: drmCloseBufferHandle failed");
h = 0;
}
}
void Aquamarine::CDRMFB::drop() {
if (dropped)
return;
dropped = true;
if (!id)
return;
backend->backend->log(AQ_LOG_TRACE, std::format("drm: dropping buffer {}", id));
int ret = drmModeCloseFB(backend->gpu->fd, id);
if (ret == -EINVAL)
ret = drmModeRmFB(backend->gpu->fd, id);
if (ret)
backend->backend->log(AQ_LOG_ERROR, std::format("drm: Failed to close a buffer: {}", strerror(-ret)));
}
uint32_t Aquamarine::CDRMFB::submitBuffer() {
auto attrs = buffer->dmabuf();
uint32_t newID = 0;
std::array<uint64_t, 4> mods = {0};
for (size_t i = 0; i < attrs.planes; ++i) {
mods.at(i) = attrs.modifier;
}
if (backend->drmProps.supportsAddFb2Modifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) {
backend->backend->log(AQ_LOG_TRACE,
std::format("drm: Using drmModeAddFB2WithModifiers to import buffer into KMS: Size {} with format {} and mod {}", attrs.size,
fourccToName(attrs.format), attrs.modifier));
if (drmModeAddFB2WithModifiers(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), mods.data(),
&newID, DRM_MODE_FB_MODIFIERS)) {
backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with AddFB2");
return 0;
}
} else {
if (attrs.modifier != DRM_FORMAT_MOD_INVALID && attrs.modifier != DRM_FORMAT_MOD_LINEAR) {
backend->backend->log(AQ_LOG_ERROR, "drm: drmModeAddFB2WithModifiers unsupported and buffer has explicit modifiers");
return 0;
}
backend->backend->log(
AQ_LOG_TRACE,
std::format("drm: Using drmModeAddFB2 to import buffer into KMS: Size {} with format {} and mod {}", attrs.size, fourccToName(attrs.format), attrs.modifier));
if (drmModeAddFB2(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), &newID, 0)) {
backend->backend->log(AQ_LOG_ERROR, "drm: drmModeAddFB2 failed");
return 0;
}
}
return newID;
}
void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) {
const auto& STATE = connector->output->state->state();
const auto MODE = STATE.mode ? STATE.mode : STATE.customMode;
di_cvt_options options = {
.red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE,
.h_pixels = (int)MODE->pixelSize.x,
.v_lines = (int)MODE->pixelSize.y,
.ip_freq_rqd = MODE->refreshRate ? MODE->refreshRate / 1000.0 : 60.0,
};
di_cvt_timing timing;
di_cvt_compute(&timing, &options);
uint16_t hsync_start = (int)MODE->pixelSize.y + timing.h_front_porch;
uint16_t vsync_start = timing.v_lines_rnd + timing.v_front_porch;
uint16_t hsync_end = hsync_start + timing.h_sync;
uint16_t vsync_end = vsync_start + timing.v_sync;
modeInfo = (drmModeModeInfo){
.clock = (uint32_t)std::round(timing.act_pixel_freq * 1000),
.hdisplay = (uint16_t)MODE->pixelSize.y,
.hsync_start = hsync_start,
.hsync_end = hsync_end,
.htotal = (uint16_t)(hsync_end + timing.h_back_porch),
.vdisplay = (uint16_t)timing.v_lines_rnd,
.vsync_start = vsync_start,
.vsync_end = vsync_end,
.vtotal = (uint16_t)(vsync_end + timing.v_back_porch),
.vrefresh = (uint32_t)std::round(timing.act_frame_rate),
.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
};
snprintf(modeInfo.name, sizeof(modeInfo.name), "%dx%d", (int)MODE->pixelSize.x, (int)MODE->pixelSize.y);
}

View File

@ -0,0 +1,88 @@
#include <aquamarine/backend/drm/Legacy.hpp>
#include <cstring>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <sys/mman.h>
using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer
Aquamarine::CDRMLegacyImpl::CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_) : backend(backend_) {
;
}
bool Aquamarine::CDRMLegacyImpl::commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
const auto& STATE = connector->output->state->state();
SP<CDRMFB> mainFB;
bool enable = STATE.enabled;
if (enable) {
if (!data.mainFB)
connector->backend->backend->log(AQ_LOG_WARNING, "legacy drm: No buffer, will fall back to only modeset (if present)");
else
mainFB = data.mainFB;
}
if (data.modeset) {
connector->backend->backend->log(AQ_LOG_DEBUG, std::format("legacy drm: Modesetting CRTC {}", connector->crtc->id));
uint32_t dpms = enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
if (drmModeConnectorSetProperty(connector->backend->gpu->fd, connector->id, connector->props.dpms, dpms)) {
connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: Failed to set dpms");
return false;
}
std::vector<uint32_t> connectors;
drmModeModeInfo* mode = nullptr;
if (enable) {
connectors.push_back(connector->id);
mode = (drmModeModeInfo*)&data.modeInfo;
}
connector->backend->backend->log(AQ_LOG_DEBUG, std::format("legacy drm: Modesetting CRTC, connectors: {}", connectors.size()));
connector->backend->backend->log(
AQ_LOG_DEBUG,
std::format("legacy drm: Modesetting CRTC, mode: clock {} hdisplay {} vdisplay {} vrefresh {}", mode->clock, mode->hdisplay, mode->vdisplay, mode->vrefresh));
if (auto ret = drmModeSetCrtc(connector->backend->gpu->fd, connector->crtc->id, mainFB ? mainFB->id : -1, 0, 0, connectors.data(), connectors.size(), mode); ret) {
connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModeSetCrtc failed: {}", strerror(-ret)));
return false;
}
}
// TODO: gamma
// TODO: Adaptive sync
// TODO: cursor plane
if (drmModeSetCursor(connector->backend->gpu->fd, connector->crtc->id, 0, 0, 0))
connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: cursor null failed");
if (!enable)
return true;
if (!(data.flags & DRM_MODE_PAGE_FLIP_EVENT))
return true;
if (int ret = drmModePageFlip(connector->backend->gpu->fd, connector->crtc->id, mainFB ? mainFB->id : -1, data.flags, &connector->pendingPageFlip); ret) {
connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModePageFlip failed: {}", strerror(-ret)));
return false;
}
connector->isPageFlipPending = true;
return true;
}
bool Aquamarine::CDRMLegacyImpl::testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
return true; // TODO: lol
}
bool Aquamarine::CDRMLegacyImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
if (!testInternal(connector, data))
return false;
return commitInternal(connector, data);
}

33
src/misc/Attachment.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <aquamarine/misc/Attachment.hpp>
using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer
bool Aquamarine::CAttachmentManager::has(eAttachmentType type) {
for (auto& a : attachments) {
if (a->type() == type)
return true;
}
return false;
}
SP<IAttachment> Aquamarine::CAttachmentManager::get(eAttachmentType type) {
for (auto& a : attachments) {
if (a->type() == type)
return a;
}
return nullptr;
}
void Aquamarine::CAttachmentManager::add(SP<IAttachment> attachment) {
attachments.emplace_back(attachment);
}
void Aquamarine::CAttachmentManager::remove(SP<IAttachment> attachment) {
std::erase(attachments, attachment);
}
void Aquamarine::CAttachmentManager::removeByType(eAttachmentType type) {
std::erase_if(attachments, [type](const auto& e) { return e->type() == type; });
}