aquamarine/include/aquamarine/backend/DRM.hpp

339 lines
14 KiB
C++

#pragma once
#include "./Backend.hpp"
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include "../input/Input.hpp"
#include <hyprutils/memory/WeakPtr.hpp>
#include <wayland-client.h>
#include <xf86drmMode.h>
namespace Aquamarine {
class CDRMBackend;
class CDRMFB;
struct SDRMConnector;
typedef std::function<void(void)> FIdleCallback;
class CDRMBufferAttachment : public IAttachment {
public:
CDRMBufferAttachment(Hyprutils::Memory::CSharedPointer<CDRMFB> fb_);
virtual ~CDRMBufferAttachment() {
;
}
virtual eAttachmentType type() {
return AQ_ATTACHMENT_DRM_BUFFER;
}
Hyprutils::Memory::CSharedPointer<CDRMFB> fb;
};
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::CWeakPointer<IBuffer> buffer;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
std::array<uint32_t, 4> boHandles = {0, 0, 0, 0};
private:
CDRMFB(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_);
uint32_t submitBuffer();
bool dropped = false, handlesClosed = false;
};
struct SDRMLayer {
// 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 /* submitted */, last /* keep just in case */;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
};
struct SDRMPlane {
bool init(drmModePlane* plane);
uint64_t type = 0;
uint32_t id = 0;
uint32_t initialID = 0;
Hyprutils::Memory::CSharedPointer<CDRMFB> front /* currently displaying */, back /* submitted */, last /* keep just in case */;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
Hyprutils::Memory::CWeakPointer<SDRMPlane> self;
std::vector<SDRMFormat> formats;
union UDRMPlaneProps {
struct {
uint32_t type;
uint32_t rotation; // Not guaranteed to exist
uint32_t in_formats; // Not guaranteed to exist
// atomic-modesetting only
uint32_t src_x;
uint32_t src_y;
uint32_t src_w;
uint32_t src_h;
uint32_t crtc_x;
uint32_t crtc_y;
uint32_t crtc_w;
uint32_t crtc_h;
uint32_t fb_id;
uint32_t crtc_id;
uint32_t fb_damage_clips;
uint32_t hotspot_x;
uint32_t hotspot_y;
};
uint32_t props[16] = {0};
};
UDRMPlaneProps props;
};
struct SDRMCRTC {
uint32_t id = 0;
std::vector<SDRMLayer> layers;
int32_t refresh = 0;
struct {
int gammaSize = 0;
} legacy;
struct {
bool ownModeID = false;
uint32_t modeID = 0;
uint32_t gammaLut = 0;
} atomic;
Hyprutils::Memory::CSharedPointer<SDRMPlane> primary;
Hyprutils::Memory::CSharedPointer<SDRMPlane> cursor;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
Hyprutils::Memory::CSharedPointer<CDRMFB> pendingCursor;
union UDRMCRTCProps {
struct {
// None of these are guaranteed to exist
uint32_t vrr_enabled;
uint32_t gamma_lut;
uint32_t gamma_lut_size;
// atomic-modesetting only
uint32_t active;
uint32_t mode_id;
};
uint32_t props[6] = {0};
};
UDRMCRTCProps props;
};
class CDRMOutput : public IOutput {
public:
virtual ~CDRMOutput();
virtual bool commit();
virtual bool test();
virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void moveCursor(const Hyprutils::Math::Vector2D& coord);
virtual void scheduleFrame();
virtual void setCursorVisible(bool visible);
virtual Hyprutils::Math::Vector2D cursorPlaneSize();
Hyprutils::Memory::CWeakPointer<CDRMOutput> self;
bool cursorVisible = true;
Hyprutils::Math::Vector2D cursorPos; // without hotspot
Hyprutils::Math::Vector2D cursorHotspot;
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;
struct {
uint32_t gammaLut = 0;
uint32_t fbDamage = 0;
uint32_t modeBlob = 0;
} atomic;
void calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
};
struct SDRMConnector {
~SDRMConnector();
bool init(drmModeConnector* connector);
void connect(drmModeConnector* connector);
void disconnect();
Hyprutils::Memory::CSharedPointer<SDRMCRTC> getCurrentCRTC(const drmModeConnector* connector);
drmModeModeInfo* getCurrentMode();
void parseEDID(std::vector<uint8_t> data);
bool commitState(SDRMConnectorCommitData& data);
void applyCommit(const SDRMConnectorCommitData& data);
void rollbackCommit(const SDRMConnectorCommitData& data);
void onPresent();
Hyprutils::Memory::CSharedPointer<CDRMOutput> output;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
Hyprutils::Memory::CWeakPointer<SDRMConnector> self;
std::string szName;
drmModeConnection status = DRM_MODE_DISCONNECTED;
uint32_t id = 0;
std::array<uint64_t, 2> maxBpcBounds = {0, 0};
Hyprutils::Memory::CSharedPointer<SDRMCRTC> crtc;
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<CDRMFB> pendingCursorFB;
bool isPageFlipPending = false;
SDRMPageFlip pendingPageFlip;
bool frameEventScheduled = false;
drmModeModeInfo fallbackModeInfo;
struct {
bool vrrEnabled = false;
} atomic;
union UDRMConnectorProps {
struct {
uint32_t edid;
uint32_t dpms;
uint32_t link_status; // not guaranteed to exist
uint32_t path;
uint32_t vrr_capable; // not guaranteed to exist
uint32_t subconnector; // not guaranteed to exist
uint32_t non_desktop;
uint32_t panel_orientation; // not guaranteed to exist
uint32_t content_type; // not guaranteed to exist
uint32_t max_bpc; // not guaranteed to exist
// atomic-modesetting only
uint32_t crtc_id;
};
uint32_t props[4] = {0};
};
UDRMConnectorProps props;
};
class IDRMImplementation {
public:
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) = 0;
virtual bool reset(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) = 0;
// moving a cursor IIRC is almost instant on most hardware so we don't have to wait for a commit.
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) = 0;
};
class CDRMBackend : public IBackendImplementation {
public:
virtual ~CDRMBackend();
virtual eBackendType type();
virtual bool start();
virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
virtual int drmFD();
virtual bool dispatchEvents();
virtual uint32_t capabilities();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void onReady();
virtual std::vector<SDRMFormat> getRenderFormats();
virtual std::vector<SDRMFormat> getCursorFormats();
Hyprutils::Memory::CWeakPointer<CDRMBackend> self;
void log(eBackendLogLevel, const std::string&);
bool sessionActive();
std::vector<FIdleCallback> idleCallbacks;
private:
CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);
static std::vector<Hyprutils::Memory::CSharedPointer<CDRMBackend>> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend);
bool registerGPU(Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu_, Hyprutils::Memory::CSharedPointer<CDRMBackend> primary_ = {});
bool checkFeatures();
bool initResources();
bool grabFormats();
void scanConnectors();
void restoreAfterVT();
Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu;
Hyprutils::Memory::CSharedPointer<IDRMImplementation> impl;
Hyprutils::Memory::CWeakPointer<CDRMBackend> primary;
std::string gpuName;
Hyprutils::Memory::CWeakPointer<CBackend> backend;
std::vector<Hyprutils::Memory::CSharedPointer<SDRMCRTC>> crtcs;
std::vector<Hyprutils::Memory::CSharedPointer<SDRMPlane>> planes;
std::vector<Hyprutils::Memory::CSharedPointer<SDRMConnector>> connectors;
std::vector<SDRMFormat> formats;
struct {
Hyprutils::Math::Vector2D cursorSize;
bool supportsAsyncCommit = false;
bool supportsAddFb2Modifiers = false;
} drmProps;
struct {
Hyprutils::Signal::CHyprSignalListener sessionActivate;
Hyprutils::Signal::CHyprSignalListener gpuChange;
Hyprutils::Signal::CHyprSignalListener gpuRemove;
} listeners;
friend class CBackend;
friend class CDRMFB;
friend class CDRMFBAttachment;
friend struct SDRMConnector;
friend struct SDRMCRTC;
friend struct SDRMPlane;
friend class CDRMOutput;
friend struct SDRMPageFlip;
friend class CDRMLegacyImpl;
friend class CDRMAtomicImpl;
friend class CDRMAtomicRequest;
};
};