DRM: preliminary atomic support

doesn't fucking work yet
This commit is contained in:
Vaxry 2024-06-27 21:48:01 +02:00
parent 511600133c
commit 55ac96271f
7 changed files with 370 additions and 3 deletions

View file

@ -118,6 +118,12 @@ namespace Aquamarine {
int gammaSize = 0; int gammaSize = 0;
} legacy; } legacy;
struct {
bool ownModeID = false;
uint32_t modeID = 0;
uint32_t gammaLut = 0;
} atomic;
Hyprutils::Memory::CSharedPointer<SDRMPlane> primary; Hyprutils::Memory::CSharedPointer<SDRMPlane> primary;
Hyprutils::Memory::CSharedPointer<SDRMPlane> cursor; Hyprutils::Memory::CSharedPointer<SDRMPlane> cursor;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend; Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
@ -219,6 +225,13 @@ namespace Aquamarine {
drmModeModeInfo fallbackModeInfo; drmModeModeInfo fallbackModeInfo;
struct {
uint32_t modeID = 0;
uint32_t gammaLut = 0;
uint32_t fbDamage = 0;
bool vrrEnabled = false;
} atomic;
union UDRMConnectorProps { union UDRMConnectorProps {
struct { struct {
uint32_t edid; uint32_t edid;
@ -315,5 +328,7 @@ namespace Aquamarine {
friend class CDRMOutput; friend class CDRMOutput;
friend struct SDRMPageFlip; friend struct SDRMPageFlip;
friend class CDRMLegacyImpl; friend class CDRMLegacyImpl;
friend class CDRMAtomicImpl;
friend class CDRMAtomicRequest;
}; };
}; };

View file

@ -0,0 +1,45 @@
#pragma once
#include "../DRM.hpp"
namespace Aquamarine {
class CDRMAtomicImpl : public IDRMImplementation {
public:
CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
virtual bool reset(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
private:
bool prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
friend class CDRMAtomicRequest;
};
class CDRMAtomicRequest {
public:
CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer<CDRMBackend> backend);
~CDRMAtomicRequest();
void addConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data);
bool commit(uint32_t flagssss);
void add(uint32_t id, uint32_t prop, uint64_t val);
void planeProps(Hyprutils::Memory::CSharedPointer<SDRMPlane> plane, Hyprutils::Memory::CSharedPointer<CDRMFB> fb, uint32_t crtc, Hyprutils::Math::Vector2D pos);
void rollback();
void apply();
bool failed = false;
private:
void destroyBlob(uint32_t id);
void commitBlob(uint32_t* current, uint32_t next);
void rollbackBlob(uint32_t* current, uint32_t next);
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
drmModeAtomicReq* req = nullptr;
Hyprutils::Memory::CSharedPointer<SDRMConnector> conn;
};
};

View file

@ -69,7 +69,7 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
if (attrs.format == DRM_FORMAT_INVALID) { if (attrs.format == DRM_FORMAT_INVALID) {
attrs.format = guessFormatFrom(FORMATS, CURSOR).drmFormat; attrs.format = guessFormatFrom(FORMATS, CURSOR).drmFormat;
if (attrs.format != DRM_FORMAT_INVALID) if (attrs.format != DRM_FORMAT_INVALID)
allocator->backend->log(AQ_LOG_WARNING, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format))); allocator->backend->log(AQ_LOG_DEBUG, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format)));
} }
if (attrs.format == DRM_FORMAT_INVALID) { if (attrs.format == DRM_FORMAT_INVALID) {

View file

@ -1,5 +1,6 @@
#include <aquamarine/backend/DRM.hpp> #include <aquamarine/backend/DRM.hpp>
#include <aquamarine/backend/drm/Legacy.hpp> #include <aquamarine/backend/drm/Legacy.hpp>
#include <aquamarine/backend/drm/Atomic.hpp>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <deque> #include <deque>
@ -16,6 +17,7 @@ extern "C" {
#include "Props.hpp" #include "Props.hpp"
#include "FormatUtils.hpp" #include "FormatUtils.hpp"
#include "Shared.hpp"
using namespace Aquamarine; using namespace Aquamarine;
using namespace Hyprutils::Memory; using namespace Hyprutils::Memory;
@ -287,11 +289,22 @@ bool Aquamarine::CDRMBackend::checkFeatures() {
drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; 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; drmProps.supportsAddFb2Modifiers = drmGetCap(gpu->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap) == 0 && cap == 1;
// FIXME: move to NO_ATOMIC when this piece of shit works
if (!envEnabled("AQ_ATOMIC")) {
backend->log(AQ_LOG_WARNING, "drm: AQ_ATOMIC not enabled, using the legacy drm iface");
impl = makeShared<CDRMLegacyImpl>(self.lock());
} else if (drmSetClientCap(gpu->fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
backend->log(AQ_LOG_WARNING, "drm: failed to set DRM_CLIENT_CAP_ATOMIC, falling back to legacy");
impl = makeShared<CDRMLegacyImpl>(self.lock());
} else {
backend->log(AQ_LOG_DEBUG, "drm: Atomic supported, using atomic for modesetting");
impl = makeShared<CDRMAtomicImpl>(self.lock());
drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &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.supportsAsyncCommit: {}", drmProps.supportsAsyncCommit));
backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAddFb2Modifiers: {}", drmProps.supportsAddFb2Modifiers)); backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAddFb2Modifiers: {}", drmProps.supportsAddFb2Modifiers));
impl = makeShared<CDRMLegacyImpl>(self.lock());
// TODO: allow no-modifiers? // TODO: allow no-modifiers?
return true; return true;

View file

@ -0,0 +1,283 @@
#include <aquamarine/backend/drm/Atomic.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::CDRMAtomicRequest::CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_) : backend(backend_) {
req = drmModeAtomicAlloc();
if (!req)
failed = true;
}
Aquamarine::CDRMAtomicRequest::~CDRMAtomicRequest() {
if (req)
drmModeAtomicFree(req);
}
void Aquamarine::CDRMAtomicRequest::add(uint32_t id, uint32_t prop, uint64_t val) {
if (failed)
return;
if (id == 0 || prop == 0) {
backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop: id / prop == 0");
return;
}
if (drmModeAtomicAddProperty(req, id, prop, val) < 0) {
backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop");
failed = true;
}
}
void Aquamarine::CDRMAtomicRequest::planeProps(Hyprutils::Memory::CSharedPointer<SDRMPlane> plane, Hyprutils::Memory::CSharedPointer<CDRMFB> fb, uint32_t crtc,
Hyprutils::Math::Vector2D pos) {
if (failed)
return;
if (!fb || !crtc) {
// Disable the plane
backend->log(AQ_LOG_TRACE, std::format("atomic planeProps: disabling plane {}", plane->id));
add(plane->id, plane->props.fb_id, 0);
add(plane->id, plane->props.crtc_id, 0);
return;
}
backend->log(AQ_LOG_TRACE,
std::format("atomic planeProps: prop blobs: src_x {}, src_y {}, src_w {}, src_h {}, crtc_w {}, crtc_h {}, fb_id {}, crtc_id {}, crtc_x {}, crtc_y {}",
plane->props.src_x, plane->props.src_y, plane->props.src_w, plane->props.src_h, plane->props.crtc_w, plane->props.crtc_h, plane->props.fb_id,
plane->props.crtc_id, plane->props.crtc_x, plane->props.crtc_y));
// src_ are 16.16 fixed point (lol)
add(plane->id, plane->props.src_x, 0);
add(plane->id, plane->props.src_y, 0);
add(plane->id, plane->props.src_w, ((uint64_t)fb->buffer->size.x) << 16);
add(plane->id, plane->props.src_h, ((uint64_t)fb->buffer->size.y) << 16);
add(plane->id, plane->props.crtc_w, (uint32_t)fb->buffer->size.x);
add(plane->id, plane->props.crtc_h, (uint32_t)fb->buffer->size.y);
add(plane->id, plane->props.fb_id, fb->id);
add(plane->id, plane->props.crtc_id, crtc);
add(plane->id, plane->props.crtc_x, (uint64_t)pos.x);
add(plane->id, plane->props.crtc_y, (uint64_t)pos.y);
}
void Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
const auto& STATE = connector->output->state->state();
const bool enable = STATE.enabled && data.mainFB;
backend->log(AQ_LOG_TRACE,
std::format("atomic addConnector blobs: mode_id {}, active {}, crtc_id {}, link_status {}, content_type {}", connector->crtc->props.mode_id,
connector->crtc->props.active, connector->props.crtc_id, connector->props.link_status, connector->props.content_type));
add(connector->id, connector->props.crtc_id, enable ? connector->crtc->id : 0);
if (data.modeset && enable && connector->props.link_status)
add(connector->id, connector->props.link_status, DRM_MODE_LINK_STATUS_GOOD);
// TODO: allow to send aq a content type, maybe? Wayland has a protocol for this.
if (enable && connector->props.content_type)
add(connector->id, connector->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS);
if (data.modeset && enable && connector->props.max_bpc && connector->maxBpcBounds.at(1))
add(connector->id, connector->props.max_bpc, 8); // FIXME: this isnt always 8
add(connector->crtc->id, connector->crtc->props.mode_id, connector->atomic.modeID);
add(connector->crtc->id, connector->crtc->props.active, enable);
if (enable) {
// TODO: gamma
if (connector->crtc->props.vrr_enabled)
add(connector->crtc->id, connector->crtc->props.vrr_enabled, (uint64_t)STATE.adaptiveSync);
planeProps(connector->crtc->primary, data.mainFB, connector->crtc->id, {});
if (connector->crtc->primary->props.fb_damage_clips)
add(connector->crtc->primary->id, connector->crtc->primary->props.fb_damage_clips, connector->atomic.fbDamage);
if (connector->crtc->cursor) {
if (!connector->output->cursorVisible)
planeProps(connector->crtc->cursor, nullptr, 0, {});
else
planeProps(connector->crtc->cursor, data.cursorFB, connector->crtc->id, connector->output->cursorPos - connector->output->cursorHotspot);
}
} else {
planeProps(connector->crtc->primary, nullptr, 0, {});
if (connector->crtc->cursor)
planeProps(connector->crtc->cursor, nullptr, 0, {});
}
conn = connector;
}
bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) {
static auto flagsToStr = [](uint32_t flags) {
std::string result;
if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
result += "ATOMIC_ALLOW_MODESET ";
if (flags & DRM_MODE_ATOMIC_NONBLOCK)
result += "ATOMIC_NONBLOCK ";
if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
result += "ATOMIC_TEST_ONLY ";
if (flags & DRM_MODE_PAGE_FLIP_EVENT)
result += "PAGE_FLIP_EVENT ";
if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
result += "PAGE_FLIP_ASYNC ";
if (flags & (~DRM_MODE_ATOMIC_FLAGS))
result += " + invalid...";
return result;
};
if (failed) {
backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR, std::format("atomic drm request: failed to commit, failed flag set to true"));
return false;
}
if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, &conn->pendingPageFlip); ret) {
backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR,
std::format("atomic drm request: failed to commit: {}, flags: {}", strerror(-ret), flagsToStr(flagssss)));
return false;
}
return true;
}
void Aquamarine::CDRMAtomicRequest::destroyBlob(uint32_t id) {
if (!id)
return;
if (drmModeDestroyPropertyBlob(backend->gpu->fd, id))
backend->log(AQ_LOG_ERROR, "atomic drm request: failed to destroy a blob");
}
void Aquamarine::CDRMAtomicRequest::commitBlob(uint32_t* current, uint32_t next) {
if (*current == next)
return;
destroyBlob(*current);
*current = next;
}
void Aquamarine::CDRMAtomicRequest::rollbackBlob(uint32_t* current, uint32_t next) {
if (*current == next)
return;
destroyBlob(next);
}
void Aquamarine::CDRMAtomicRequest::rollback() {
conn->crtc->atomic.ownModeID = true;
rollbackBlob(&conn->crtc->atomic.modeID, conn->atomic.modeID);
// TODO: gamma
//rollbackBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut);
destroyBlob(conn->atomic.fbDamage);
}
void Aquamarine::CDRMAtomicRequest::apply() {
if (!conn->crtc->atomic.ownModeID)
conn->crtc->atomic.modeID = 0;
conn->crtc->atomic.ownModeID = true;
commitBlob(&conn->crtc->atomic.modeID, conn->atomic.modeID);
// TODO: gamma
//commitBlob(&conn->crtc->atomic.gammaLut, conn->atomic.gammaLut);
destroyBlob(conn->atomic.fbDamage);
}
Aquamarine::CDRMAtomicImpl::CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_) : backend(backend_) {
;
}
bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
const auto& STATE = connector->output->state->state();
const bool enable = STATE.enabled;
if (data.modeset) {
if (!enable)
connector->atomic.modeID = 0;
else {
if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, (drmModeModeInfo*)&data.modeInfo, sizeof(drmModeModeInfo), &connector->atomic.modeID)) {
connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a modeset blob");
return false;
}
}
}
// TODO: gamma
if (STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE && connector->crtc->primary->props.fb_damage_clips) {
if (STATE.damage.empty())
connector->atomic.fbDamage = 0;
else {
std::vector<pixman_box32_t> rects = STATE.damage.getRects();
if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, rects.data(), sizeof(pixman_box32_t) * rects.size(), &connector->atomic.fbDamage)) {
connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a damage blob");
return false;
}
}
}
return true;
}
bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, const SDRMConnectorCommitData& data) {
if (!prepareConnector(connector, data))
return false;
CDRMAtomicRequest request(backend);
request.addConnector(connector, data);
uint32_t flags = data.flags;
if (data.test)
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
if (data.modeset)
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
if (!data.blocking && !data.test)
flags |= DRM_MODE_ATOMIC_NONBLOCK;
const bool ok = request.commit(flags);
if (ok)
request.apply();
else
request.rollback();
return ok;
}
bool Aquamarine::CDRMAtomicImpl::reset(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) {
CDRMAtomicRequest request(backend);
for (auto& crtc : backend->crtcs) {
request.add(crtc->id, crtc->props.mode_id, 0);
request.add(crtc->id, crtc->props.active, 0);
}
for (auto& conn : backend->connectors) {
request.add(conn->id, conn->props.crtc_id, 0);
}
for (auto& plane : backend->planes) {
request.planeProps(plane, nullptr, 0, {});
}
return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET);
}
bool Aquamarine::CDRMAtomicImpl::moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) {
if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
return true;
CDRMAtomicRequest request(backend);
request.planeProps(connector->crtc->cursor, connector->crtc->cursor->front, connector->crtc->id, connector->output->cursorPos - connector->output->cursorHotspot);
return request.commit(DRM_MODE_ATOMIC_NONBLOCK);
return true;
}

View file

@ -4,6 +4,10 @@
#include <format> #include <format>
#include <signal.h> #include <signal.h>
namespace Aquamarine {
bool envEnabled(const std::string& env);
};
#define RASSERT(expr, reason, ...) \ #define RASSERT(expr, reason, ...) \
if (!(expr)) { \ if (!(expr)) { \
std::cout << std::format("\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ std::cout << std::format("\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \

7
src/utils/Shared.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "Shared.hpp"
#include <cstdlib>
bool Aquamarine::envEnabled(const std::string& env) {
auto e = getenv(env.c_str());
return e && e == std::string{"1"};
}