mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-12-22 11:39:49 +01:00
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:
parent
f888bfb6e4
commit
2e0052a21d
15 changed files with 769 additions and 30 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
18
include/aquamarine/backend/drm/Legacy.hpp
Normal file
18
include/aquamarine/backend/drm/Legacy.hpp
Normal 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;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
|
|
32
include/aquamarine/misc/Attachment.hpp
Normal file
32
include/aquamarine/misc/Attachment.hpp
Normal 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;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
88
src/backend/drm/impl/Legacy.cpp
Normal file
88
src/backend/drm/impl/Legacy.cpp
Normal 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
33
src/misc/Attachment.cpp
Normal 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; });
|
||||
}
|
Loading…
Reference in a new issue