Compare commits

...

58 Commits
0.1.1 ... main

Author SHA1 Message Date
Ikalco 561f50bfae
drm: don't connect before crtcs are guaranteed (#68) 2024-08-29 15:51:04 +02:00
Vaxry 23c7925dd3 gbm: improve automatic format selection 2024-08-28 15:01:37 +02:00
Tom Englund 7cc3d3179c
misc: make for loops const reference (#66)
we all like the dreams and prayers that compilers can optimize things
further with const.
2024-08-27 20:04:26 +02:00
Ikalco abb3c81c59
drm: recheck crtcs before connecting on udev event (#65) 2024-08-26 20:36:16 +02:00
Vaxry 3989aa9b2f wayland: fix possible out-of-range crash in setCursor 2024-08-26 10:30:29 +02:00
Tom Englund ddf6987b53
backend: dont overflow on 32bit builds (#64)
make the definition long long as its used as long long later, only the
value was being calculated TIMESPEC_NSEC_PER_SEC * 240 and rolls over
before assigning to the variable.
2024-08-25 17:48:53 +02:00
Mihai Fufezan 31e692b20d
Nix: add note about wayland-scanner 2024-08-22 14:31:09 +03:00
romanstingler 9a3161ad4c
drm: fix typo in schedule (#62)
Co-authored-by: Roman Stingler <roman.stingler@waterdrop.com>
2024-08-21 21:59:51 +01:00
Tom Englund a17f9218d9
drm: add missing destructor (#63)
add missing default constructor to base class IDRMImplentation
2024-08-21 21:59:19 +01:00
Colt-M16A1 41d842669b
drm: request page flip events after commit (#60)
Apparently fixes stuff.
2024-08-21 20:36:34 +01:00
vaxerski 1c3256287c drm: verify output format in commit 2024-08-21 12:17:50 +02:00
vaxerski 2cd1f78241 GBM: fixup good() return value 2024-08-21 12:17:34 +02:00
Tom Englund b02d4f4d72
output: avoid crashing on no crtc (#58)
dual gpus can init with no crtc and later rescan for them, however
getRenderFormats might be called before that happends and null ptr
deref, guard against it.
2024-08-20 20:10:13 +01:00
vaxerski b474796cdb drm: reconfigure swapchain props after creating 2024-08-20 20:58:07 +02:00
vaxerski 1e43d9a719 gbm: improve trace logging around allocs 2024-08-20 19:09:03 +02:00
vaxerski 8079098326 drm: fix crash on null crtc setCursor
ref #54
2024-08-20 18:44:18 +02:00
vaxerski 00d51a053c props: bump version to 0.3.3 2024-08-19 20:21:51 +02:00
vaxerski 696a5ad4ec gbm: guard for drm renderer being present before clearing
ref #56
2024-08-19 18:26:38 +02:00
Florian Klink 71da3e00a0
CMakeLists: wayland.xml is in wayland-scanner pkgdatadir (#55)
See 6c4a695045/meson.build (L129-136)
2024-08-19 14:02:14 +01:00
vaxerski 7f8df01d42 props: bump version to 0.3.2 2024-08-18 20:36:13 +02:00
Vaxry 2bff4a4521
drm: don't schedule new frame events on disabled outputs (#52)
* drm: don't schedule new frame events on disabled outputs

* e

* test
2024-08-18 19:24:52 +01:00
vaxerski 2d29c86d69 drm: use modelines in vt restore 2024-08-18 20:15:46 +02:00
Vaxry 71d49670fe
drm: clear buffer on creation for scanout DRM buffers (#51) 2024-08-17 19:42:51 +01:00
vaxerski cd152140fd drm: don't report pointer capability when no HW cursor plane is present
ref https://github.com/hyprwm/Hyprland/issues/7364
2024-08-17 19:18:46 +02:00
Tom Englund 05f4efca81
buffer: align size to stride (#50)
calculate the size to the stride we get from gb_bo_map to better align
it.
2024-08-17 17:47:57 +01:00
vaxerski 4f6642808b drm: avoid crashes on connecting a null crtc 2024-08-16 09:06:27 +02:00
vaxerski e947af7894 drm/atomic: clip damage to pixel size
ref https://github.com/hyprwm/Hyprland/issues/7356
2024-08-15 22:34:45 +02:00
Vaxry 589346162f DRM: Allow checking no-crtc connectors (#49) 2024-08-15 18:01:37 +02:00
vaxerski 9312aa2827 docs: document AQ_DRM_DEVICES
fixes #47
2024-08-11 21:43:58 +02:00
Ziyao 9b33a38f86
cmake: link libOpenGL instead of legacy libGL (#44)
From cmake documentation, Linux-specific chapter:

  Projects may use the ``OpenGL::GL`` target (or ``OPENGL_LIBRARIES``
  variable) to use legacy GL interfaces.  These will use the legacy GL
  library located by ``OPENGL_gl_LIBRARY``, if available.  If
  ``OPENGL_gl_LIBRARY`` is empty or not found and GLVND is available,
  the ``OpenGL::GL`` target will use GLVND ``OpenGL::OpenGL`` and
  ``OpenGL::GLX`` (and the ``OPENGL_LIBRARIES`` variable will use the
  corresponding libraries).  Thus, for non-EGL-based Linux targets, the
  ``OpenGL::GL`` target is most portable.

which means linking with OpenGL::GL makes cmake find legacy libGL.so or
GLX libraries, and as for now, the former contains GLX symbols and cannot
be built without X libraries. Since we are working with EGL, it wouldn't
provide extra portability, either.

This patch switches to link aquamarine with modern OpenGL::OpenGL, which
contains core OpenGL API only, makes it possible to build on a
wayland-only system. Tested on eweOS, which is a distro without X
libraries.

Link: https://os.ewe.moe/

Signed-off-by: Yao Zi <ziyao@disroot.org>
2024-08-10 17:22:37 +01:00
Vaxry 295d37df17 props: bump version to 0.3.1 2024-08-09 20:32:21 +02:00
Vaxry daf96f0da7 drm: fixup modeline calculations 2024-08-09 19:20:25 +02:00
Vaxry c94060b22c drm: log calculated modeline for custom modes 2024-08-09 19:06:34 +02:00
Vaxry 1a7ca38fa3 drm: fix custom mode check 2024-08-09 18:02:03 +02:00
Jan Beich 131ed05f99
drm: add missing include to fix BSD (#42)
src/backend/drm/Renderer.cpp:504:13: error: use of undeclared identifier 'close'
  504 |             close(dupFd);
      |             ^
src/backend/drm/Renderer.cpp:532:13: error: use of undeclared identifier 'close'
  532 |             close(egl.lastBlitSyncFD);
      |             ^
2024-08-06 22:06:30 +02:00
Vaxry 940980244e version: bump to 0.3.0 2024-08-06 16:29:49 +02:00
Vaxry 21f9c44789 drm: don't use explicit in blit without explicit requested from user 2024-08-06 13:19:53 +02:00
Vaxry 8a8afd3896 drm: add AQ_MGPU_NO_EXPLICIT 2024-08-05 16:42:03 +02:00
Vaxry 6f5adc0568 drm: s
upport explicit sync with multi-gpu destinations

will break o
n mgpu nvidia before 560 driver
2024-08-05 00:27:09 +02:00
Vaxry a70fc6a2fd drm: avoid testing with a pf event 2024-08-03 19:10:09 +02:00
Vaxry 18c6a8ccaf drm: immediately send presentation events for tearing 2024-08-03 18:23:24 +02:00
toamz 339337cc7b
libinput: Update switch state in libinput event (#38) 2024-08-03 12:57:30 +01:00
Vaxry 7c3565f9be drm: don't rollback cursors on test 2024-07-30 15:55:39 +02:00
Vaxry 9ccb4411ee utils: fix missing include 2024-07-29 22:04:50 +02:00
Vaxry 544395c1b0 version/cmake: bump version to 0.2.0 2024-07-29 20:37:28 +02:00
Mihai Fufezan 4918e57979
Nix: fix cross-compilation 2024-07-27 20:21:53 +03:00
Samuel Cobb f95d150937
drm: Follow symlinks for AQ_DRM_DEVICES (#34) 2024-07-26 12:17:06 +01:00
Ikalco 0ab8ffa67d
output: fix destroying wl and headless outputs (#32) 2024-07-25 22:14:05 +01:00
moetayuko a7d77c60ee
gbm: manually set modifier for implicit bo allocation (#30)
Ported from wlroots

Fixes https://github.com/hyprwm/Hyprland/issues/7001
2024-07-25 21:41:46 +01:00
UjinT34 0720a5cbae gbm: Fix cursor bo for nvidia (#29) 2024-07-25 00:18:17 +02:00
Vaxry 353dc1b729 drm: conform to both renderable and scanoutable formats in scanout buffers
fixes #28
2024-07-24 22:41:58 +02:00
Vaxry 744a383a52 gbm: log scanout flag state in trace 2024-07-24 21:59:40 +02:00
Khiet Tam Nguyen e569340c6b
drm: ignore primary argument when using evdi drivers (#25)
* fix: ignore primary if using evdi drivers for displaylink

* style: removed curly braces from evdi if-check
2024-07-24 20:14:29 +02:00
Vaxry 4a1424e85b gbm: use flags in create_with_modifiers
amdeeznuts
2024-07-24 20:11:55 +02:00
Vaxry 07eb70afb1
gbm: Nvidia fixes for scanout gbm allocation (#27)
* nv fixes

* fix
2024-07-24 18:41:13 +02:00
Vaxry 4c72cd4d0b session/input: add missing pos to touch down events 2024-07-23 19:18:57 +02:00
Vaxry 601f6cf95c wayland: drop required zwp_linux_dmabuf_v1 version to 4
compatibility reasons, we don't really need 5
2024-07-21 16:22:23 +02:00
UjinT34 7a84686b4a
output: remove redundant needsFrame, allow skip on mouse event (#21) 2024-07-20 20:56:18 +02:00
27 changed files with 620 additions and 210 deletions

View File

@ -59,8 +59,8 @@ target_include_directories(
PUBLIC "./include"
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
set_target_properties(aquamarine PROPERTIES VERSION ${AQUAMARINE_VERSION}
SOVERSION 0)
target_link_libraries(aquamarine OpenGL::EGL OpenGL::GL PkgConfig::deps)
SOVERSION 2)
target_link_libraries(aquamarine OpenGL::EGL OpenGL::OpenGL PkgConfig::deps)
check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
@ -71,8 +71,8 @@ endif()
# Protocols
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_CLIENT_DIR wayland-client pkgdatadir)
message(STATUS "Found wayland-client at ${WAYLAND_CLIENT_DIR}")
pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
function(protocolNew protoPath protoName external)
if(external)
@ -94,7 +94,7 @@ function(protocolWayland)
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums --client
${WAYLAND_CLIENT_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(aquamarine PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
endfunction()

View File

@ -1 +1 @@
0.1.0
0.3.3

View File

@ -4,7 +4,9 @@ Unless specified otherwise, a variable is enabled if and only if it's set to `1`
### DRM
`AQ_DRM_DEVICES` -> Set an explicit list of DRM devices (GPUs) to use. It's a colon-separated list of paths, with the first being the primary. E.g. `/dev/dri/card1:/dev/dri/card0`
`AQ_NO_ATOMIC` -> Disables drm atomic modesetting
`AQ_MGPU_NO_EXPLICIT` -> Disables explicit syncing on mgpu buffers
### Debugging

View File

@ -31,6 +31,12 @@
localSystem.system = system;
overlays = with self.overlays; [aquamarine];
});
pkgsCrossFor = eachSystem (system: crossSystem:
import nixpkgs {
localSystem = system;
crossSystem = crossSystem;
overlays = with self.overlays; [aquamarine];
});
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
@ -72,6 +78,7 @@
packages = eachSystem (system: {
default = self.packages.${system}.aquamarine;
inherit (pkgsFor.${system}) aquamarine aquamarine-with-tests;
aquamarine-cross = (pkgsCrossFor.${system} "aarch64-linux").aquamarine;
});
formatter = eachSystem (system: pkgsFor.${system}.alejandra);

View File

@ -14,11 +14,16 @@ namespace Aquamarine {
bool scanout = false, cursor = false, multigpu = false;
};
enum eAllocatorType {
AQ_ALLOCATOR_TYPE_GBM = 0,
};
class IAllocator {
public:
virtual ~IAllocator() = default;
virtual Hyprutils::Memory::CSharedPointer<IBuffer> acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain) = 0;
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend() = 0;
virtual int drmFD() = 0;
virtual eAllocatorType type() = 0;
};
};

View File

@ -45,6 +45,7 @@ namespace Aquamarine {
virtual Hyprutils::Memory::CSharedPointer<IBuffer> acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain_);
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend();
virtual int drmFD();
virtual eAllocatorType type();
//
Hyprutils::Memory::CWeakPointer<CGBMAllocator> self;

View File

@ -77,6 +77,7 @@ namespace Aquamarine {
virtual std::vector<SDRMFormat> getCursorFormats() = 0;
virtual bool createOutput(const std::string& name = "") = 0; // "" means auto
virtual Hyprutils::Memory::CSharedPointer<IAllocator> preferredAllocator() = 0;
virtual std::vector<SDRMFormat> getRenderableFormats(); // empty = use getRenderFormats
};
class CBackend {

View File

@ -152,7 +152,7 @@ namespace Aquamarine {
struct SDRMCRTC {
uint32_t id = 0;
std::vector<SDRMLayer> layers;
int32_t refresh = 0;
int32_t refresh = 0; // unused
struct {
int gammaSize = 0;
@ -194,7 +194,7 @@ namespace Aquamarine {
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 moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule = false);
virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
virtual void setCursorVisible(bool visible);
virtual Hyprutils::Math::Vector2D cursorPlaneSize();
@ -209,6 +209,8 @@ namespace Aquamarine {
Hyprutils::Math::Vector2D cursorPos; // without hotspot
Hyprutils::Math::Vector2D cursorHotspot;
bool enabledState = true; // actual enabled state. Should be synced with state->state().enabled after a new frame
private:
CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, Hyprutils::Memory::CSharedPointer<SDRMConnector> connector_);
@ -265,6 +267,7 @@ namespace Aquamarine {
void applyCommit(const SDRMConnectorCommitData& data);
void rollbackCommit(const SDRMConnectorCommitData& data);
void onPresent();
void recheckCRTCProps();
Hyprutils::Memory::CSharedPointer<CDRMOutput> output;
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
@ -320,11 +323,12 @@ namespace Aquamarine {
class IDRMImplementation {
public:
virtual ~IDRMImplementation() = default;
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) = 0;
virtual bool reset() = 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;
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipSchedule = false) = 0;
};
class CDRMBackend : public IBackendImplementation {
@ -342,6 +346,7 @@ namespace Aquamarine {
virtual std::vector<SDRMFormat> getCursorFormats();
virtual bool createOutput(const std::string& name = "");
virtual Hyprutils::Memory::CSharedPointer<IAllocator> preferredAllocator();
virtual std::vector<SDRMFormat> getRenderableFormats();
Hyprutils::Memory::CWeakPointer<CDRMBackend> self;
@ -362,20 +367,20 @@ namespace Aquamarine {
bool initMgpu();
bool grabFormats();
bool shouldBlit();
void scanConnectors();
void scanConnectors(bool allowConnect = true);
void scanLeases();
void restoreAfterVT();
void recheckCRTCs();
void buildGlFormats(const std::vector<SGLFormat>& fmts);
Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu;
Hyprutils::Memory::CSharedPointer<IDRMImplementation> impl;
Hyprutils::Memory::CWeakPointer<CDRMBackend> primary;
// multigpu state, only present if this backend is not primary, aka if this->primary != nullptr
struct {
Hyprutils::Memory::CSharedPointer<IAllocator> allocator;
Hyprutils::Memory::CSharedPointer<CDRMRenderer> renderer;
} mgpu;
Hyprutils::Memory::CSharedPointer<CDRMRenderer> renderer; // may be null if creation fails
} rendererState;
Hyprutils::Memory::CWeakPointer<CBackend> backend;
@ -383,6 +388,7 @@ namespace Aquamarine {
std::vector<Hyprutils::Memory::CSharedPointer<SDRMPlane>> planes;
std::vector<Hyprutils::Memory::CSharedPointer<SDRMConnector>> connectors;
std::vector<SDRMFormat> formats;
std::vector<SDRMFormat> glFormats;
bool atomic = false;
@ -411,5 +417,6 @@ namespace Aquamarine {
friend class CDRMAtomicImpl;
friend class CDRMAtomicRequest;
friend class CDRMLease;
friend class CGBMBuffer;
};
};

View File

@ -4,6 +4,12 @@
#include <vector>
namespace Aquamarine {
struct SGLFormat {
uint32_t drmFormat = 0;
uint64_t modifier = 0;
bool external = false;
};
struct SDRMFormat {
uint32_t drmFormat = 0; /* DRM_FORMAT_INVALID */
std::vector<uint64_t> modifiers;

View File

@ -45,7 +45,7 @@ namespace Aquamarine {
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 moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule = false);
virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
virtual Hyprutils::Math::Vector2D cursorPlaneSize();
virtual bool destroy();

View File

@ -8,7 +8,7 @@ namespace Aquamarine {
CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
virtual bool reset();
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipSchedule = false);
private:
bool prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);

View File

@ -8,12 +8,11 @@ namespace Aquamarine {
CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
virtual bool reset();
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipSchedule = false);
private:
bool commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
bool testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
bool commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
bool testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
};

View File

@ -113,6 +113,8 @@ namespace Aquamarine {
AQ_SCHEDULE_NEW_MONITOR,
AQ_SCHEDULE_RENDER_MONITOR,
AQ_SCHEDULE_NEEDS_FRAME,
AQ_SCHEDULE_ANIMATION,
AQ_SCHEDULE_ANIMATION_DAMAGE,
};
virtual bool commit() = 0;
@ -121,7 +123,7 @@ namespace Aquamarine {
virtual std::vector<SDRMFormat> getRenderFormats() = 0;
virtual Hyprutils::Memory::CSharedPointer<SOutputMode> preferredMode();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
virtual void moveCursor(const Hyprutils::Math::Vector2D& coord); // includes the hotspot
virtual void moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule = false); // includes the hotspot
virtual void setCursorVisible(bool visible); // moving the cursor will make it visible again without this util
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error
virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);

View File

@ -17,6 +17,7 @@
udev,
wayland,
wayland-protocols,
# wayland-scanner,
version ? "git",
doCheck ? false,
}:
@ -25,13 +26,18 @@ stdenv.mkDerivation {
inherit version doCheck;
src = ../.;
strictDeps = true;
nativeBuildInputs = [
cmake
hyprwayland-scanner
pkg-config
# re-add after https://github.com/NixOS/nixpkgs/pull/214906 hits nixos-unstable
# wayland-scanner
];
buildInputs = [
hwdata
hyprutils
libdisplay-info
libdrm
@ -46,10 +52,6 @@ stdenv.mkDerivation {
wayland-protocols
];
depsBuildBuild = [
hwdata
];
outputs = ["out" "dev"];
cmakeBuildType = "RelWithDebInfo";

View File

@ -1,17 +1,19 @@
#include <aquamarine/allocator/GBM.hpp>
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include "FormatUtils.hpp"
#include "Shared.hpp"
#include <xf86drm.h>
#include <gbm.h>
#include <unistd.h>
#include "../backend/drm/Renderer.hpp"
using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer
static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor) {
static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor, bool scanout) {
if (formats.empty())
return SDRMFormat{};
@ -20,20 +22,29 @@ static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor)
Try to find 10bpp formats first, as they offer better color precision.
For cursors, don't, as these almost never support that.
*/
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB2101010; }); it != formats.end())
return *it;
if (!scanout) {
if (auto it =
std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB2101010 || f.drmFormat == DRM_FORMAT_ABGR2101010; });
it != formats.end())
return *it;
}
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB2101010; }); it != formats.end())
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB2101010 || f.drmFormat == DRM_FORMAT_XBGR2101010; });
it != formats.end())
return *it;
}
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB8888; }); it != formats.end())
if (!scanout) {
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB8888 || f.drmFormat == DRM_FORMAT_ABGR8888; });
it != formats.end())
return *it;
}
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB8888 || f.drmFormat == DRM_FORMAT_XBGR8888; });
it != formats.end())
return *it;
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB8888; }); it != formats.end())
return *it;
for (auto& f : formats) {
for (auto const& f : formats) {
auto name = fourccToName(f.drmFormat);
/* 10 bpp RGB */
@ -41,7 +52,7 @@ static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor)
return f;
}
for (auto& f : formats) {
for (auto const& f : formats) {
auto name = fourccToName(f.drmFormat);
/* 8 bpp RGB */
@ -65,14 +76,18 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
const bool MULTIGPU = params.multigpu && params.scanout;
TRACE(allocator->backend->log(AQ_LOG_TRACE,
std::format("GBM: Allocating a buffer: size {}, format {}, cursor: {}, multigpu: {}", attrs.size, fourccToName(attrs.format), CURSOR, MULTIGPU)));
std::format("GBM: Allocating a buffer: size {}, format {}, cursor: {}, multigpu: {}, scanout: {}", attrs.size, fourccToName(attrs.format), CURSOR,
MULTIGPU, params.scanout)));
const auto FORMATS = CURSOR ? swapchain->backendImpl->getCursorFormats() : swapchain->backendImpl->getRenderFormats();
const auto FORMATS = CURSOR ? swapchain->backendImpl->getCursorFormats() : swapchain->backendImpl->getRenderFormats();
const auto RENDERABLE = swapchain->backendImpl->getRenderableFormats();
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Available formats: {}", FORMATS.size())));
std::vector<uint64_t> explicitModifiers;
if (attrs.format == DRM_FORMAT_INVALID) {
attrs.format = guessFormatFrom(FORMATS, CURSOR).drmFormat;
attrs.format = guessFormatFrom(FORMATS, CURSOR, params.scanout).drmFormat;
if (attrs.format != DRM_FORMAT_INVALID)
allocator->backend->log(AQ_LOG_DEBUG, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format)));
}
@ -84,15 +99,34 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
// check if we can use modifiers. If the requested support has any explicit modifier
// supported by the primary backend, we can.
for (auto& f : FORMATS) {
if (f.drmFormat != attrs.format)
continue;
if (!RENDERABLE.empty()) {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Renderable has {} formats, clipping", RENDERABLE.size())));
for (auto& m : f.modifiers) {
if (m == DRM_FORMAT_MOD_INVALID)
for (auto const& f : FORMATS) {
if (f.drmFormat != attrs.format)
continue;
explicitModifiers.push_back(m);
for (auto const& m : f.modifiers) {
if (m == DRM_FORMAT_MOD_INVALID)
continue;
if (params.scanout && !CURSOR && !MULTIGPU) {
// regular scanout plane, check if the format is renderable
auto rformat = std::find_if(RENDERABLE.begin(), RENDERABLE.end(), [f](const auto& e) { return e.drmFormat == f.drmFormat; });
if (rformat == RENDERABLE.end()) {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Dropping format {} as it's not renderable", fourccToName(f.drmFormat))));
break;
}
if (std::find(rformat->modifiers.begin(), rformat->modifiers.end(), m) == rformat->modifiers.end()) {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Dropping modifier 0x{:x} as it's not renderable", m)));
continue;
}
}
explicitModifiers.push_back(m);
}
}
}
@ -116,13 +150,23 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
bo = gbm_bo_create(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, flags);
} else {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Using modifier-based allocation, modifiers: {}", explicitModifiers.size())));
for (auto& mod : explicitModifiers) {
for (auto const& mod : explicitModifiers) {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: | mod 0x{:x}", mod)));
}
bo = gbm_bo_create_with_modifiers(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, explicitModifiers.data(), explicitModifiers.size());
bo = gbm_bo_create_with_modifiers2(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, explicitModifiers.data(), explicitModifiers.size(), flags);
if (!bo && CURSOR) {
// allow non-renderable cursor buffer for nvidia
allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers and flags failed, falling back to modifiers without flags");
bo = gbm_bo_create_with_modifiers(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, explicitModifiers.data(), explicitModifiers.size());
}
if (!bo) {
allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers failed, falling back to implicit");
if (explicitModifiers.size() == 1 && explicitModifiers[0] == DRM_FORMAT_MOD_LINEAR) {
flags |= GBM_BO_USE_LINEAR;
allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers failed, falling back to modifier-less allocation");
} else
allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers failed, falling back to implicit");
bo = gbm_bo_create(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, flags);
}
}
@ -133,7 +177,7 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
}
attrs.planes = gbm_bo_get_plane_count(bo);
attrs.modifier = gbm_bo_get_modifier(bo);
attrs.modifier = (flags & GBM_BO_USE_LINEAR) ? DRM_FORMAT_MOD_LINEAR : gbm_bo_get_modifier(bo);
for (size_t i = 0; i < (size_t)attrs.planes; ++i) {
attrs.strides.at(i) = gbm_bo_get_stride_for_plane(bo, i);
@ -159,6 +203,13 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
modName ? modName : "Unknown"));
free(modName);
if (params.scanout && swapchain->backendImpl->type() == AQ_BACKEND_DRM) {
// clear the buffer using the DRM renderer to avoid uninitialized mem
auto impl = (CDRMBackend*)swapchain->backendImpl.get();
if (impl->rendererState.renderer)
impl->rendererState.renderer->clearBuffer(this);
}
}
Aquamarine::CGBMBuffer::~CGBMBuffer() {
@ -189,7 +240,7 @@ bool Aquamarine::CGBMBuffer::isSynchronous() {
}
bool Aquamarine::CGBMBuffer::good() {
return true;
return bo;
}
SDMABUFAttrs Aquamarine::CGBMBuffer::dmabuf() {
@ -197,13 +248,13 @@ SDMABUFAttrs Aquamarine::CGBMBuffer::dmabuf() {
}
std::tuple<uint8_t*, uint32_t, size_t> Aquamarine::CGBMBuffer::beginDataPtr(uint32_t flags) {
uint32_t dst_stride = 0;
uint32_t stride = 0;
if (boBuffer)
allocator->backend->log(AQ_LOG_ERROR, "beginDataPtr is called a second time without calling endDataPtr first. Returning old mapping");
else
boBuffer = gbm_bo_map(bo, 0, 0, attrs.size.x, attrs.size.y, flags, &dst_stride, &gboMapping);
// FIXME: assumes a 32-bit pixel format
return {(uint8_t*)boBuffer, attrs.format, attrs.size.x * attrs.size.y * 4};
boBuffer = gbm_bo_map(bo, 0, 0, attrs.size.x, attrs.size.y, flags, &stride, &gboMapping);
return {(uint8_t*)boBuffer, attrs.format, stride * attrs.size.y};
}
void Aquamarine::CGBMBuffer::endDataPtr() {
@ -278,3 +329,7 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CGBMAllocator::getBacken
int Aquamarine::CGBMAllocator::drmFD() {
return fd;
}
eAllocatorType Aquamarine::CGBMAllocator::type() {
return AQ_ALLOCATOR_TYPE_GBM;
}

View File

@ -17,7 +17,7 @@ using namespace Hyprutils::Memory;
using namespace Aquamarine;
#define SP CSharedPointer
#define TIMESPEC_NSEC_PER_SEC 1000000000L
#define TIMESPEC_NSEC_PER_SEC 1000000000LL
static void timespecAddNs(timespec* pTimespec, int64_t delta) {
int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC;
@ -67,7 +67,7 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const s
backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!");
for (auto& b : backends) {
for (auto const& b : backends) {
if (b.backendType == AQ_BACKEND_WAYLAND) {
auto ref = SP<CWaylandBackend>(new CWaylandBackend(backend));
backend->implementations.emplace_back(ref);
@ -79,7 +79,7 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const s
continue;
}
for (auto& r : ref) {
for (auto const& r : ref) {
backend->implementations.emplace_back(r);
}
} else if (b.backendType == AQ_BACKEND_HEADLESS) {
@ -109,7 +109,7 @@ bool Aquamarine::CBackend::start() {
int started = 0;
auto optionsForType = [this](eBackendType type) -> SBackendImplementationOptions {
for (auto& o : implementationOptions) {
for (auto const& o : implementationOptions) {
if (o.backendType == type)
return o;
}
@ -145,7 +145,7 @@ bool Aquamarine::CBackend::start() {
});
// TODO: obviously change this when (if) we add different allocators.
for (auto& b : implementations) {
for (auto const& b : implementations) {
if (b->drmFD() >= 0) {
auto fd = reopenDRMNode(b->drmFD());
if (fd < 0) {
@ -162,7 +162,7 @@ bool Aquamarine::CBackend::start() {
return false;
ready = true;
for (auto& b : implementations) {
for (auto const& b : implementations) {
b->onReady();
}
@ -180,15 +180,15 @@ void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) {
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CBackend::getPollFDs() {
std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> result;
for (auto& i : implementations) {
for (auto const& i : implementations) {
auto pollfds = i->pollFDs();
for (auto& p : pollfds) {
for (auto const& p : pollfds) {
log(AQ_LOG_DEBUG, std::format("backend: poll fd {} for implementation {}", p->fd, backendTypeToName(i->type())));
result.emplace_back(p);
}
}
for (auto& sfd : sessionFDs) {
for (auto const& sfd : sessionFDs) {
log(AQ_LOG_DEBUG, std::format("backend: poll fd {} for session", sfd->fd));
result.emplace_back(sfd);
}
@ -200,7 +200,7 @@ std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CBackend::ge
}
int Aquamarine::CBackend::drmFD() {
for (auto& i : implementations) {
for (auto const& i : implementations) {
int fd = i->drmFD();
if (fd < 0)
continue;
@ -215,14 +215,14 @@ bool Aquamarine::CBackend::hasSession() {
}
std::vector<SDRMFormat> Aquamarine::CBackend::getPrimaryRenderFormats() {
for (auto& b : implementations) {
for (auto const& b : implementations) {
if (b->type() != AQ_BACKEND_DRM && b->type() != AQ_BACKEND_WAYLAND)
continue;
return b->getRenderFormats();
}
for (auto& b : implementations) {
for (auto const& b : implementations) {
return b->getRenderFormats();
}
@ -260,7 +260,7 @@ void Aquamarine::CBackend::dispatchIdle() {
auto cpy = idle.pending;
idle.pending.clear();
for (auto& i : cpy) {
for (auto const& i : cpy) {
if (i && *i)
(*i)();
}
@ -328,3 +328,7 @@ int Aquamarine::CBackend::reopenDRMNode(int drmFD, bool allowRenderNode) {
return newFD;
}
std::vector<SDRMFormat> Aquamarine::IBackendImplementation::getRenderableFormats() {
return {};
}

View File

@ -10,7 +10,7 @@ using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer
#define TIMESPEC_NSEC_PER_SEC 1000000000L
#define TIMESPEC_NSEC_PER_SEC 1000000000LL
static void timespecAddNs(timespec* pTimespec, int64_t delta) {
int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC;
@ -73,6 +73,7 @@ void Aquamarine::CHeadlessOutput::scheduleFrame(const scheduleFrameReason reason
}
bool Aquamarine::CHeadlessOutput::destroy() {
events.destroy.emit();
std::erase(backend->outputs, self.lock());
return true;
}
@ -148,7 +149,7 @@ void Aquamarine::CHeadlessBackend::dispatchTimers() {
}
}
for (auto& copy : toFire) {
for (auto const& copy : toFire) {
if (copy.what)
copy.what();
}
@ -161,7 +162,7 @@ void Aquamarine::CHeadlessBackend::updateTimerFD() {
const auto clocknow = std::chrono::steady_clock::now();
bool any = false;
for (auto& t : timers.timers) {
for (auto const& t : timers.timers) {
auto delta = std::chrono::duration_cast<std::chrono::microseconds>(t.when - clocknow).count() * 1000 /* µs -> ns */;
if (delta < lowestNs)

View File

@ -264,7 +264,7 @@ static bool isDRMCard(const char* sysname) {
}
void Aquamarine::CSession::onReady() {
for (auto& d : libinputDevices) {
for (auto const& d : libinputDevices) {
if (d->keyboard)
backend->events.newKeyboard.emit(SP<IKeyboard>(d->keyboard));
if (d->mouse)
@ -278,7 +278,7 @@ void Aquamarine::CSession::onReady() {
if (d->tabletPad)
backend->events.newTabletPad.emit(SP<ITabletPad>(d->tabletPad));
for (auto& t : d->tabletTools) {
for (auto const& t : d->tabletTools) {
backend->events.newTabletTool.emit(SP<ITabletTool>(t));
}
}
@ -306,7 +306,7 @@ void Aquamarine::CSession::dispatchUdevEvents() {
dev_t deviceNum = udev_device_get_devnum(device);
SP<CSessionDevice> sessionDevice;
for (auto& sDev : sessionDevices) {
for (auto const& sDev : sessionDevices) {
if (sDev->dev == deviceNum) {
sessionDevice = sDev;
break;
@ -498,7 +498,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
};
for (auto& axis : LAXES) {
for (auto const& axis : LAXES) {
if (!libinput_event_pointer_has_axis(pe, axis))
continue;
@ -596,6 +596,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
hlDevice->touch->events.down.emit(ITouch::SDownEvent{
.timeMs = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
.touchID = libinput_event_touch_get_seat_slot(te),
.pos = {libinput_event_touch_get_x_transformed(te, 1), libinput_event_touch_get_y_transformed(te, 1)},
});
break;
}
@ -639,6 +640,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
if (ENABLED == hlDevice->switchy->state)
return;
hlDevice->switchy->state = ENABLED;
switch (libinput_event_switch_get_switch(se)) {
case LIBINPUT_SWITCH_LID: hlDevice->switchy->type = ISwitch::AQ_SWITCH_TYPE_LID; break;
@ -858,7 +860,7 @@ Aquamarine::CLibinputDevice::~CLibinputDevice() {
}
SP<CLibinputTabletTool> Aquamarine::CLibinputDevice::toolFrom(libinput_tablet_tool* tool) {
for (auto& t : tabletTools) {
for (auto const& t : tabletTools) {
if (t->libinputTool == tool)
return t;
}

View File

@ -106,9 +106,9 @@ bool Aquamarine::CWaylandBackend::start() {
TRACE(backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 1, id)));
waylandState.shm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_shm_interface, 1));
} else if (NAME == "zwp_linux_dmabuf_v1") {
TRACE(backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 5, id)));
TRACE(backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 4, id)));
waylandState.dmabuf =
makeShared<CCZwpLinuxDmabufV1>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &zwp_linux_dmabuf_v1_interface, 5));
makeShared<CCZwpLinuxDmabufV1>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &zwp_linux_dmabuf_v1_interface, 4));
if (!initDmabuf()) {
backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed");
waylandState.dmabufFailed = true;
@ -168,7 +168,7 @@ bool Aquamarine::CWaylandBackend::dispatchEvents() {
// dispatch frames
if (backend->ready) {
for (auto& f : idleCallbacks) {
for (auto const& f : idleCallbacks) {
f();
}
idleCallbacks.clear();
@ -187,7 +187,7 @@ bool Aquamarine::CWaylandBackend::setCursor(Hyprutils::Memory::CSharedPointer<IB
}
void Aquamarine::CWaylandBackend::onReady() {
for (auto& o : outputs) {
for (auto const& o : outputs) {
o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
if (!o->swapchain) {
backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", o->name));
@ -257,7 +257,7 @@ Aquamarine::CWaylandPointer::CWaylandPointer(SP<CCWlPointer> pointer_, Hyprutils
pointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface, wl_fixed_t x, wl_fixed_t y) {
backend->lastEnterSerial = serial;
for (auto& o : backend->outputs) {
for (auto const& o : backend->outputs) {
if (o->waylandState.surface->resource() != surface)
continue;
@ -269,7 +269,7 @@ Aquamarine::CWaylandPointer::CWaylandPointer(SP<CCWlPointer> pointer_, Hyprutils
});
pointer->setLeave([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface) {
for (auto& o : backend->outputs) {
for (auto const& o : backend->outputs) {
if (o->waylandState.surface->resource() != surface)
continue;
@ -496,6 +496,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
Aquamarine::CWaylandOutput::~CWaylandOutput() {
backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events
events.destroy.emit();
if (waylandState.xdgToplevel)
waylandState.xdgToplevel->sendDestroy();
if (waylandState.xdgSurface)
@ -598,7 +599,7 @@ SP<IBackendImplementation> Aquamarine::CWaylandOutput::getBackend() {
SP<CWaylandBuffer> Aquamarine::CWaylandOutput::wlBufferFromBuffer(SP<IBuffer> buffer) {
std::erase_if(backendState.buffers, [this](const auto& el) { return el.first.expired() || !swapchain->contains(el.first.lock()); });
for (auto& [k, v] : backendState.buffers) {
for (auto const& [k, v] : backendState.buffers) {
if (k != buffer)
continue;
@ -654,7 +655,8 @@ bool Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBu
if (!buffer) {
cursorState.cursorBuffer.reset();
cursorState.cursorWlBuffer.reset();
backend->pointers.at(0)->pointer->sendSetCursor(cursorState.serial, nullptr, cursorState.hotspot.x, cursorState.hotspot.y);
if (!backend->pointers.empty())
backend->pointers.at(0)->pointer->sendSetCursor(cursorState.serial, nullptr, cursorState.hotspot.x, cursorState.hotspot.y);
return true;
}
@ -724,7 +726,7 @@ bool Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBu
return true;
}
void Aquamarine::CWaylandOutput::moveCursor(const Hyprutils::Math::Vector2D& coord) {
void Aquamarine::CWaylandOutput::moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule) {
return;
}

View File

@ -8,6 +8,8 @@
#include <thread>
#include <deque>
#include <cstring>
#include <filesystem>
#include <system_error>
#include <sys/mman.h>
#include <fcntl.h>
@ -135,9 +137,25 @@ static std::vector<SP<CSessionDevice>> scanGPUs(SP<CBackend> backend) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit device list {}", explicitGpus));
Hyprutils::String::CVarList explicitDevices(explicitGpus, 0, ':', true);
// Iterate over GPUs and canonicalize the paths
for (auto& d : explicitDevices) {
std::error_code ec;
auto canonicalFilePath = std::filesystem::canonical(d, ec);
// If there is an error, log and continue.
// TODO: Verify that the path is a valid DRM device. (https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/backend/session/session.c?ref_type=heads#L369-387)
if (ec) {
backend->log(AQ_LOG_ERROR, std::format("drm: Failed to canonicalize path {}", d));
continue;
}
d = canonicalFilePath.string();
}
for (auto const& d : explicitDevices) {
bool found = false;
for (auto& vd : devices) {
for (auto const& vd : devices) {
if (vd->path == d) {
vecDevices.emplace_back(vd);
found = true;
@ -151,7 +169,7 @@ static std::vector<SP<CSessionDevice>> scanGPUs(SP<CBackend> backend) {
backend->log(AQ_LOG_ERROR, std::format("drm: Explicit device {} not found", d));
}
} else {
for (auto& d : devices) {
for (auto const& d : devices) {
vecDevices.push_back(d);
}
}
@ -201,7 +219,7 @@ std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backe
std::vector<SP<CDRMBackend>> backends;
SP<CDRMBackend> newPrimary;
for (auto& gpu : gpus) {
for (auto const& gpu : gpus) {
auto drmBackend = SP<CDRMBackend>(new CDRMBackend(backend));
drmBackend->self = drmBackend;
@ -228,7 +246,7 @@ std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backe
drmBackend->grabFormats();
drmBackend->scanConnectors();
drmBackend->scanConnectors(false);
drmBackend->recheckCRTCs();
@ -261,7 +279,7 @@ bool Aquamarine::CDRMBackend::sessionActive() {
void Aquamarine::CDRMBackend::restoreAfterVT() {
backend->log(AQ_LOG_DEBUG, "drm: Restoring after VT switch");
scanConnectors();
scanConnectors(false);
recheckCRTCs();
backend->log(AQ_LOG_DEBUG, "drm: Rescanned connectors");
@ -271,7 +289,7 @@ void Aquamarine::CDRMBackend::restoreAfterVT() {
std::vector<SP<SDRMConnector>> noMode;
for (auto& c : connectors) {
for (auto const& c : connectors) {
if (!c->crtc || !c->output)
continue;
@ -284,15 +302,16 @@ void Aquamarine::CDRMBackend::restoreAfterVT() {
};
auto& STATE = c->output->state->state();
auto& MODE = STATE.customMode ? STATE.customMode : STATE.mode;
if (!STATE.customMode && !STATE.mode) {
if (!MODE) {
backend->log(AQ_LOG_WARNING, "drm: Connector {} has output but state has no mode, will send a reset state event later.");
noMode.emplace_back(c);
continue;
}
if (STATE.mode && STATE.mode->modeInfo.has_value())
data.modeInfo = *STATE.mode->modeInfo;
if (MODE->modeInfo.has_value())
data.modeInfo = *MODE->modeInfo;
else
data.calculateMode(c);
@ -323,7 +342,7 @@ void Aquamarine::CDRMBackend::restoreAfterVT() {
backend->log(AQ_LOG_ERROR, std::format("drm: crtc {} failed restore", c->crtc->id));
}
for (auto& c : noMode) {
for (auto const& c : noMode) {
if (!c->output)
continue;
@ -475,29 +494,55 @@ bool Aquamarine::CDRMBackend::shouldBlit() {
}
bool Aquamarine::CDRMBackend::initMgpu() {
if (!primary)
return true;
SP<CGBMAllocator> newAllocator;
if (primary || backend->primaryAllocator->type() != AQ_ALLOCATOR_TYPE_GBM) {
newAllocator = CGBMAllocator::create(backend->reopenDRMNode(gpu->fd), backend);
rendererState.allocator = newAllocator;
} else {
newAllocator = ((CGBMAllocator*)backend->primaryAllocator.get())->self.lock();
rendererState.allocator = newAllocator;
}
auto newAllocator = CGBMAllocator::create(backend->reopenDRMNode(gpu->fd), backend);
mgpu.allocator = newAllocator;
if (!mgpu.allocator) {
if (!rendererState.allocator) {
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no allocator");
return false;
}
mgpu.renderer = CDRMRenderer::attempt(newAllocator, backend.lock());
rendererState.renderer = CDRMRenderer::attempt(newAllocator, backend.lock());
if (!mgpu.renderer) {
if (!rendererState.renderer) {
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no renderer");
return false;
}
mgpu.renderer->self = mgpu.renderer;
rendererState.renderer->self = rendererState.renderer;
buildGlFormats(rendererState.renderer->formats);
return true;
}
void Aquamarine::CDRMBackend::buildGlFormats(const std::vector<SGLFormat>& fmts) {
std::vector<SDRMFormat> result;
for (auto const& fmt : fmts) {
if (fmt.external)
continue;
if (auto it = std::find_if(result.begin(), result.end(), [fmt](const auto& e) { return fmt.drmFormat == e.drmFormat; }); it != result.end()) {
it->modifiers.emplace_back(fmt.modifier);
continue;
}
result.emplace_back(SDRMFormat{
fmt.drmFormat,
{fmt.modifier},
});
}
glFormats = result;
}
void Aquamarine::CDRMBackend::recheckCRTCs() {
if (connectors.empty() || crtcs.empty())
return;
@ -505,7 +550,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
backend->log(AQ_LOG_DEBUG, "drm: Rechecking CRTCs");
std::vector<SP<SDRMConnector>> recheck, changed;
for (auto& c : connectors) {
for (auto const& c : connectors) {
if (c->crtc && c->status == DRM_MODE_CONNECTED) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Skipping connector {}, has crtc {} and is connected", c->szName, c->crtc->id));
continue;
@ -517,7 +562,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
for (size_t i = 0; i < crtcs.size(); ++i) {
bool taken = false;
for (auto& c : connectors) {
for (auto const& c : connectors) {
if (c->crtc != crtcs.at(i))
continue;
@ -535,7 +580,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
bool assigned = false;
// try to use a connected connector
for (auto& c : recheck) {
for (auto const& c : recheck) {
if (!(c->possibleCrtcs & (1 << i)))
continue;
@ -561,7 +606,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
backend->log(AQ_LOG_DEBUG, std::format("drm: slot {} crtc {} unassigned", i, crtcs.at(i)->id));
}
for (auto& c : connectors) {
for (auto const& c : connectors) {
if (c->status == DRM_MODE_CONNECTED)
continue;
@ -570,7 +615,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
// if any connectors get a crtc and are connected, we need to rescan to assign them outputs.
bool rescan = false;
for (auto& c : changed) {
for (auto const& c : changed) {
if (!c->output && c->status == DRM_MODE_CONNECTED) {
rescan = true;
continue;
@ -599,8 +644,12 @@ bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBacken
gpuName = drmName;
auto drmVerName = drmVer->name ? drmVer->name : "unknown";
if (std::string_view(drmVerName) == "evdi")
primary = {};
backend->log(AQ_LOG_DEBUG,
std::format("drm: Starting backend for {}, with driver {}{}", drmName ? drmName : "unknown", drmVer->name ? drmVer->name : "unknown",
std::format("drm: Starting backend for {}, with driver {}{}", drmName ? drmName : "unknown", drmVerName,
(primary ? std::format(" with primary {}", primary->gpu->path) : "")));
drmFreeVersion(drmVer);
@ -609,7 +658,7 @@ bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBacken
auto E = std::any_cast<CSessionDevice::SChangeEvent>(d);
if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Got a hotplug event for {}", gpuName));
scanConnectors();
scanConnectors(false);
recheckCRTCs();
} else if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Got a lease event for {}", gpuName));
@ -627,7 +676,7 @@ eBackendType Aquamarine::CDRMBackend::type() {
return eBackendType::AQ_BACKEND_DRM;
}
void Aquamarine::CDRMBackend::scanConnectors() {
void Aquamarine::CDRMBackend::scanConnectors(bool allowConnect) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Scanning connectors for {}", gpu->path));
auto resources = drmModeGetResources(gpu->fd);
@ -668,19 +717,19 @@ void Aquamarine::CDRMBackend::scanConnectors() {
conn->status = drmConn->connection;
if (!conn->crtc) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Ignoring connector {} because it has no CRTC", connectorID));
continue;
}
if (conn->crtc)
conn->recheckCRTCProps();
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state: {}", connectorID, (int)drmConn->connection));
if (conn->status == DRM_MODE_CONNECTED && !conn->output) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
conn->connect(drmConn);
} else if (conn->status != DRM_MODE_CONNECTED && conn->output) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} disconnected", conn->szName));
conn->disconnect();
if (allowConnect) {
if (conn->status == DRM_MODE_CONNECTED && !conn->output) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
conn->connect(drmConn);
} else if (conn->status != DRM_MODE_CONNECTED && conn->output) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} disconnected", conn->szName));
conn->disconnect();
}
}
drmModeFreeConnector(drmConn);
@ -696,7 +745,7 @@ void Aquamarine::CDRMBackend::scanLeases() {
return;
}
for (auto& c : connectors) {
for (auto const& c : connectors) {
if (!c->output || !c->output->lease)
continue;
@ -718,7 +767,7 @@ void Aquamarine::CDRMBackend::scanLeases() {
auto l = c->output->lease;
for (auto& c2 : connectors) {
for (auto const& c2 : connectors) {
if (!c2->output || c2->output->lease != c->output->lease)
continue;
@ -775,7 +824,7 @@ static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, un
.flags = flags,
});
if (BACKEND->sessionActive() && !pageFlip->connector->frameEventScheduled)
if (BACKEND->sessionActive() && !pageFlip->connector->frameEventScheduled && pageFlip->connector->output->enabledState)
pageFlip->connector->output->events.frame.emit();
}
@ -792,6 +841,8 @@ bool Aquamarine::CDRMBackend::dispatchEvents() {
}
uint32_t Aquamarine::CDRMBackend::capabilities() {
if (getCursorFormats().empty())
return 0;
return eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER;
}
@ -802,7 +853,26 @@ 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) {
// init a drm renderer to gather gl formats.
// if we are secondary, initMgpu will have done that
if (!primary) {
auto a = CGBMAllocator::create(backend->reopenDRMNode(gpu->fd), backend);
if (!a)
backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats");
else {
auto r = CDRMRenderer::attempt(a, backend.lock());
if (!r)
backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats");
else {
TRACE(backend->log(AQ_LOG_TRACE, std::format("drm: onReady: gathered {} gl formats", r->formats.size())));
buildGlFormats(r->formats);
r.reset();
a.reset();
}
}
}
for (auto const& c : connectors) {
backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {}", c->id));
if (!c->output)
continue;
@ -824,7 +894,7 @@ void Aquamarine::CDRMBackend::onReady() {
}
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
for (auto& p : planes) {
for (auto const& p : planes) {
if (p->type != DRM_PLANE_TYPE_PRIMARY)
continue;
@ -834,8 +904,12 @@ std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
return {};
}
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderableFormats() {
return glFormats;
}
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() {
for (auto& p : planes) {
for (auto const& p : planes) {
if (p->type != DRM_PLANE_TYPE_CURSOR)
continue;
@ -1089,13 +1163,30 @@ void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
di_info_destroy(info);
}
void Aquamarine::SDRMConnector::recheckCRTCProps() {
if (!crtc || !output)
return;
uint64_t prop = 0;
canDoVrr = props.vrr_capable && crtc->props.vrr_enabled && getDRMProp(backend->gpu->fd, id, props.vrr_capable, &prop) && prop;
output->vrrCapable = canDoVrr;
backend->backend->log(AQ_LOG_DEBUG,
std::format("drm: connector {} crtc is {} of vrr: props.vrr_capable -> {}, crtc->props.vrr_enabled -> {}", szName, (canDoVrr ? "capable" : "incapable"),
props.vrr_capable, crtc->props.vrr_enabled));
output->supportsExplicit = backend->drmProps.supportsTimelines && crtc->props.out_fence_ptr && crtc->primary->props.in_fence_fd;
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit sync {}", output->supportsExplicit ? "supported" : "unsupported"));
}
void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
if (output) {
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not connecting connector {} because it's already connected", szName));
return;
}
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Connecting connector {}, CRTC ID {}", szName, crtc ? crtc->id : -1));
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Connecting connector {}, {}", szName, crtc ? std::format("CRTC ID {}", crtc->id) : "no CRTC"));
output = SP<CDRMOutput>(new CDRMOutput(szName, backend, self.lock()));
output->self = output;
@ -1129,8 +1220,6 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
//uint64_t modeID = 0;
// getDRMProp(backend->gpu->fd, crtc->id, crtc->props.mode_id, &modeID);
crtc->refresh = calculateRefresh(drmMode);
}
backend->backend->log(AQ_LOG_DEBUG,
@ -1138,10 +1227,8 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
aqMode->preferred ? " (preferred)" : ""));
}
if (!currentModeInfo && fallbackMode) {
if (!currentModeInfo && fallbackMode)
output->state->setMode(fallbackMode);
crtc->refresh = calculateRefresh(fallbackMode->modeInfo.value());
}
output->physicalSize = {(double)connector->mmWidth, (double)connector->mmHeight};
@ -1164,13 +1251,6 @@ 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;
backend->backend->log(AQ_LOG_DEBUG,
std::format("drm: crtc is {} of vrr: props.vrr_capable -> {}, crtc->props.vrr_enabled -> {}", (canDoVrr ? "capable" : "incapable"), props.vrr_capable,
crtc->props.vrr_enabled));
maxBpcBounds.fill(0);
if (props.max_bpc && !introspectDRMPropRange(backend->gpu->fd, props.max_bpc, maxBpcBounds.data(), &maxBpcBounds[1]))
@ -1187,23 +1267,24 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
// TODO: subconnectors
output->make = make;
output->model = model;
output->serial = serial;
output->description = std::format("{} {} {} ({})", make, model, serial, szName);
output->needsFrame = true;
output->supportsExplicit = backend->drmProps.supportsTimelines && crtc->props.out_fence_ptr && crtc->primary->props.in_fence_fd;
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit sync {}", output->supportsExplicit ? "supported" : "unsupported"));
output->make = make;
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;
recheckCRTCProps();
if (!backend->backend->ready)
return;
output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock());
output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true, .multigpu = !!backend->primary}); // mark the swapchain for scanout
output->needsFrame = true;
backend->backend->events.newOutput.emit(SP<IOutput>(output));
output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR);
}
@ -1245,10 +1326,16 @@ void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data)
if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
refresh = calculateRefresh(data.modeInfo);
output->enabledState = output->state->state().enabled;
}
void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) {
// cursors are applied regardless.
// cursors are applied regardless,
// unless this was a test
if (data.test)
return;
if (crtc->cursor && data.cursorFB)
crtc->cursor->back = data.cursorFB;
@ -1289,7 +1376,6 @@ bool Aquamarine::CDRMOutput::test() {
void Aquamarine::CDRMOutput::setCursorVisible(bool visible) {
cursorVisible = visible;
needsFrame = true;
scheduleFrame(AQ_SCHEDULE_CURSOR_VISIBLE);
}
@ -1308,12 +1394,33 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
const uint32_t COMMITTED = STATE.committed;
if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED) && STATE.enabled) {
if (!STATE.mode && STATE.customMode) {
if (!STATE.mode && !STATE.customMode) {
backend->backend->log(AQ_LOG_ERROR, "drm: No mode on enable commit");
return false;
}
}
if (STATE.drmFormat == DRM_FORMAT_INVALID) {
backend->backend->log(AQ_LOG_ERROR, "drm: No format for output");
return false;
}
if (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_FORMAT) {
// verify the format is valid for the primary plane
bool ok = false;
for (auto const& f : getRenderFormats()) {
if (f.drmFormat == STATE.drmFormat) {
ok = true;
break;
}
}
if (!ok) {
backend->backend->log(AQ_LOG_ERROR, "drm: Selected format is not supported by the primary KMS plane");
return false;
}
}
if (STATE.adaptiveSync && !connector->canDoVrr) {
backend->backend->log(AQ_LOG_ERROR, "drm: No Adaptive sync support for output");
return false;
@ -1347,7 +1454,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (!MODE) // modeless commits are invalid
return false;
uint32_t flags = 0;
uint32_t flags = 0;
if (!onlyTest) {
if (NEEDS_RECONFIG) {
@ -1385,7 +1492,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (!mgpu.swapchain) {
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No swapchain for blit, creating"));
mgpu.swapchain = CSwapchain::create(backend->mgpu.allocator, backend.lock());
mgpu.swapchain = CSwapchain::create(backend->rendererState.allocator, backend.lock());
}
auto OPTIONS = swapchain->currentOptions();
@ -1401,12 +1508,22 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
return false;
}
auto NEWAQBUF = mgpu.swapchain->next(nullptr);
if (!backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF)) {
auto NEWAQBUF = mgpu.swapchain->next(nullptr);
auto blitResult = backend->rendererState.renderer->blit(
STATE.buffer, NEWAQBUF, (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE) ? STATE.explicitInFence : -1);
if (!blitResult.success) {
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed");
return false;
}
// replace the explicit in fence if the blitting backend returned one, otherwise discard old. Passed fence from the client is wrong.
// if the commit doesn't have an explicit fence, don't use the one we created, just fallback to implicit
static auto NO_EXPLICIT = envEnabled("AQ_MGPU_NO_EXPLICIT");
if (blitResult.syncFD.has_value() && !NO_EXPLICIT && (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE))
state->setExplicitInFence(blitResult.syncFD.value());
else
state->setExplicitInFence(-1);
drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
} else
drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present
@ -1434,6 +1551,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
fourccToName(STATE.drmFormat), fourccToName(params.format)));
state->setFormat(params.format);
formatMismatch = true;
// TODO: reject if tearing? We will miss a frame event!
flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; // we cannot modeset with async pf
}
}
@ -1470,7 +1588,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
// to avoid doing this over and over.
data.modeset = true;
data.blocking = true;
data.flags = DRM_MODE_PAGE_FLIP_EVENT;
data.flags = onlyTest ? 0 : DRM_MODE_PAGE_FLIP_EVENT;
ok = connector->commitState(data);
if (!ok)
@ -1489,6 +1607,28 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (ok)
connector->commitTainted = false;
if (data.flags & DRM_MODE_PAGE_FLIP_ASYNC) {
// for tearing commits, we will send presentation feedback instantly, and rotate
// drm framebuffers to properly send backendRelease events.
// the last FB should already be gone from KMS because it's been immediately replaced
// no completion and no vsync, because tearing
uint32_t flags = IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput::AQ_OUTPUT_PRESENT_ZEROCOPY;
timespec presented;
clock_gettime(CLOCK_MONOTONIC, &presented);
connector->output->events.present.emit(IOutput::SPresentEvent{
.presented = backend->sessionActive(),
.when = &presented,
.seq = 0, /* unknown sequence for tearing */
.refresh = (int)(connector->refresh ? (1000000000000LL / connector->refresh) : 0),
.flags = flags,
});
connector->onPresent();
}
return ok;
}
@ -1502,6 +1642,9 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
return false;
}
if (!connector->crtc)
return false;
if (!buffer)
setCursorVisible(false);
else {
@ -1512,7 +1655,7 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
if (!mgpu.cursorSwapchain) {
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No cursorSwapchain for blit, creating"));
mgpu.cursorSwapchain = CSwapchain::create(backend->mgpu.allocator, backend.lock());
mgpu.cursorSwapchain = CSwapchain::create(backend->rendererState.allocator, backend.lock());
}
auto OPTIONS = mgpu.cursorSwapchain->currentOptions();
@ -1529,7 +1672,7 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
}
auto NEWAQBUF = mgpu.cursorSwapchain->next(nullptr);
if (!backend->mgpu.renderer->blit(buffer, NEWAQBUF)) {
if (!backend->rendererState.renderer->blit(buffer, NEWAQBUF).success) {
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but cursor blit failed");
return false;
}
@ -1552,15 +1695,14 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
cursorVisible = true;
}
needsFrame = true;
scheduleFrame(AQ_SCHEDULE_CURSOR_SHAPE);
return true;
}
void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord) {
cursorPos = coord;
cursorVisible = true;
backend->impl->moveCursor(connector);
void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord, bool skipSchedule) {
cursorPos = coord;
// cursorVisible = true;
backend->impl->moveCursor(connector, skipSchedule);
}
void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) {
@ -1569,7 +1711,7 @@ void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) {
connector->isPageFlipPending, connector->frameEventScheduled)));
needsFrame = true;
if (connector->isPageFlipPending || connector->frameEventScheduled)
if (connector->isPageFlipPending || connector->frameEventScheduled || !enabledState)
return;
connector->frameEventScheduled = true;
@ -1597,6 +1739,10 @@ size_t Aquamarine::CDRMOutput::getGammaSize() {
}
std::vector<SDRMFormat> Aquamarine::CDRMOutput::getRenderFormats() {
if (!connector->crtc || !connector->crtc->primary || connector->crtc->primary->formats.empty()) {
backend->log(AQ_LOG_ERROR, "Can't get formats: no crtc");
return {};
}
return connector->crtc->primary->formats;
}
@ -1816,14 +1962,14 @@ void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CShar
di_cvt_compute(&timing, &options);
uint16_t hsync_start = (int)MODE->pixelSize.y + timing.h_front_porch;
uint16_t hsync_start = (int)MODE->pixelSize.x + 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,
.hdisplay = (uint16_t)MODE->pixelSize.x,
.hsync_start = hsync_start,
.hsync_end = hsync_end,
.htotal = (uint16_t)(hsync_end + timing.h_back_porch),
@ -1835,6 +1981,11 @@ void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CShar
.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);
TRACE(connector->backend->log(AQ_LOG_TRACE,
std::format("drm: calculateMode: modeline dump: {} {} {} {} {} {} {} {} {} {} {}", modeInfo.clock, modeInfo.hdisplay, modeInfo.hsync_start,
modeInfo.hsync_end, modeInfo.htotal, modeInfo.vdisplay, modeInfo.vsync_start, modeInfo.vsync_end, modeInfo.vtotal, modeInfo.vrefresh,
modeInfo.flags)));
}
Aquamarine::CDRMBufferAttachment::CDRMBufferAttachment(SP<CDRMFB> fb_) : fb(fb_) {
@ -1850,7 +2001,7 @@ SP<CDRMLease> Aquamarine::CDRMLease::create(std::vector<SP<IOutput>> outputs) {
auto backend = ((CDRMBackend*)outputs.at(0)->getBackend().get())->self.lock();
for (auto& o : outputs) {
for (auto const& o : outputs) {
if (o->getBackend() != backend) {
backend->log(AQ_LOG_ERROR, "drm lease: Mismatched backends");
return nullptr;
@ -1861,7 +2012,7 @@ SP<CDRMLease> Aquamarine::CDRMLease::create(std::vector<SP<IOutput>> outputs) {
auto lease = SP<CDRMLease>(new CDRMLease);
for (auto& o : outputs) {
for (auto const& o : outputs) {
auto drmo = ((CDRMOutput*)o.get())->self.lock();
backend->log(AQ_LOG_DEBUG, std::format("drm lease: output {}, connector {}", drmo->name, drmo->connector->id));
@ -1890,7 +2041,7 @@ SP<CDRMLease> Aquamarine::CDRMLease::create(std::vector<SP<IOutput>> outputs) {
return nullptr;
}
for (auto& o : lease->outputs) {
for (auto const& o : lease->outputs) {
o->lease = lease;
}

View File

@ -3,6 +3,7 @@
#include <xf86drmMode.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include "Math.hpp"
#include "Shared.hpp"
#include "FormatUtils.hpp"
@ -153,9 +154,9 @@ bool CDRMRenderer::initDRMFormats() {
TRACE(backend->log(AQ_LOG_TRACE, "EGL: Supported formats:"));
std::vector<GLFormat> dmaFormats;
std::vector<SGLFormat> dmaFormats;
for (auto& fmt : formats) {
for (auto const& fmt : formats) {
std::vector<std::pair<uint64_t, bool>> mods;
auto ret = getModsForFormat(fmt);
@ -169,8 +170,8 @@ bool CDRMRenderer::initDRMFormats() {
// EGL can always do implicit modifiers.
mods.push_back({DRM_FORMAT_MOD_INVALID, true});
for (auto& [mod, external] : mods) {
dmaFormats.push_back(GLFormat{
for (auto const& [mod, external] : mods) {
dmaFormats.push_back(SGLFormat{
.drmFormat = (uint32_t)fmt,
.modifier = mod,
.external = external,
@ -178,7 +179,7 @@ bool CDRMRenderer::initDRMFormats() {
}
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: GPU Supports Format {} (0x{:x})", fourccToName((uint32_t)fmt), fmt)));
for (auto& [mod, external] : mods) {
for (auto const& [mod, external] : mods) {
auto modName = drmGetFormatModifierName(mod);
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: | {}with modifier 0x{:x}: {}", (external ? "external only " : ""), mod, modName ? modName : "?unknown?")));
free(modName);
@ -223,6 +224,20 @@ SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAll
loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT");
loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT");
loadGLProc(&renderer->egl.eglDestroySyncKHR, "eglDestroySyncKHR");
loadGLProc(&renderer->egl.eglWaitSyncKHR, "eglWaitSyncKHR");
loadGLProc(&renderer->egl.eglCreateSyncKHR, "eglCreateSyncKHR");
loadGLProc(&renderer->egl.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID");
if (!renderer->egl.eglCreateSyncKHR) {
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglCreateSyncKHR");
return nullptr;
}
if (!renderer->egl.eglDupNativeFenceFDANDROID) {
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglDupNativeFenceFDANDROID");
return nullptr;
}
if (!renderer->egl.eglGetPlatformDisplayEXT) {
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT");
@ -440,7 +455,7 @@ SGLTex CDRMRenderer::glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buffa) {
}
bool external = false;
for (auto& fmt : formats) {
for (auto const& fmt : formats) {
if (fmt.drmFormat != dma.format || fmt.modifier != dma.modifier)
continue;
@ -469,12 +484,140 @@ inline const float fullVerts[] = {
0, 1, // bottom left
};
bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
void CDRMRenderer::waitOnSync(int fd) {
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (waitOnSync): attempting to wait on fd {}", fd)));
std::vector<EGLint> attribs;
int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (dupFd < 0) {
backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to dup fd for wait");
return;
}
attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID);
attribs.push_back(dupFd);
attribs.push_back(EGL_NONE);
EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data());
if (sync == EGL_NO_SYNC_KHR) {
TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to create an egl sync for explicit"));
if (dupFd >= 0)
close(dupFd);
return;
}
// we got a sync, now we just tell egl to wait before sampling
if (egl.eglWaitSyncKHR(egl.display, sync, 0) != EGL_TRUE) {
if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync"));
TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to wait on the sync object"));
return;
}
if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync"));
}
int CDRMRenderer::recreateBlitSync() {
TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): recreating blit sync"));
if (egl.lastBlitSync) {
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): cleaning up old sync (fd {})", egl.lastBlitSyncFD)));
// cleanup last sync
if (egl.eglDestroySyncKHR(egl.display, egl.lastBlitSync) != EGL_TRUE)
TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy old sync"));
if (egl.lastBlitSyncFD >= 0)
close(egl.lastBlitSyncFD);
egl.lastBlitSyncFD = -1;
egl.lastBlitSync = nullptr;
}
EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
if (sync == EGL_NO_SYNC_KHR) {
TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to create an egl sync for explicit"));
return -1;
}
// we need to flush otherwise we might not get a valid fd
glFlush();
int fd = egl.eglDupNativeFenceFDANDROID(egl.display, sync);
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to dup egl fence fd"));
if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy new sync"));
return -1;
}
egl.lastBlitSync = sync;
egl.lastBlitSyncFD = fd;
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): success, new fence exported with fd {}", fd)));
return fd;
}
void CDRMRenderer::clearBuffer(IBuffer* buf) {
setEGL();
auto dmabuf = buf->dmabuf();
GLuint rboID = 0, fboID = 0;
if (!dmabuf.success) {
backend->log(AQ_LOG_ERROR, "EGL (clear): cannot clear a non-dmabuf");
return;
}
auto rboImage = createEGLImage(dmabuf);
if (rboImage == EGL_NO_IMAGE_KHR) {
backend->log(AQ_LOG_ERROR, std::format("EGL (clear): createEGLImage failed: {}", eglGetError()));
return;
}
GLCALL(glGenRenderbuffers(1, &rboID));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
GLCALL(egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GLCALL(glGenFramebuffers(1, &fboID));
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (clear): fbo {} rbo {}", fboID, rboID)));
glClearColor(0.F, 0.F, 0.F, 1.F);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
glDeleteFramebuffers(1, &fboID);
glDeleteRenderbuffers(1, &rboID);
egl.eglDestroyImageKHR(egl.display, rboImage);
restoreEGL();
}
CDRMRenderer::SBlitResult CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to, int waitFD) {
setEGL();
if (from->dmabuf().size != to->dmabuf().size) {
backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched");
return false;
return {};
}
if (waitFD >= 0) {
// wait on a provided explicit fence
waitOnSync(waitFD);
}
// firstly, get a texture from the from buffer
@ -514,7 +657,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
if (!verifyDestinationDMABUF(toDma)) {
backend->log(AQ_LOG_ERROR, "EGL (blit): failed to blit: destination dmabuf unsupported");
return false;
return {};
}
{
@ -533,7 +676,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
rboImage = createEGLImage(toDma);
if (rboImage == EGL_NO_IMAGE_KHR) {
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
return false;
return {};
}
GLCALL(glGenRenderbuffers(1, &rboID));
@ -547,7 +690,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError()));
return false;
return {};
}
// should never remove anything, but JIC. We'll leak an RBO and FBO if this removes anything.
@ -623,15 +766,19 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
GLCALL(glBindTexture(fromTex.target, 0));
// rendered, cleanup
glFlush();
// get an explicit sync fd for the secondary gpu.
// when we pass buffers between gpus we should always use explicit sync,
// as implicit is not guaranteed at all
int explicitFD = recreateBlitSync();
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
restoreEGL();
return true;
return {true, explicitFD == -1 ? std::nullopt : std::optional<int>{explicitFD}};
}
void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {
@ -655,7 +802,7 @@ void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachme
}
bool CDRMRenderer::verifyDestinationDMABUF(const SDMABUFAttrs& attrs) {
for (auto& fmt : formats) {
for (auto const& fmt : formats) {
if (fmt.drmFormat != attrs.format)
continue;

View File

@ -1,6 +1,7 @@
#pragma once
#include <aquamarine/backend/DRM.hpp>
#include "FormatUtils.hpp"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
@ -19,6 +20,7 @@ namespace Aquamarine {
GLuint texid = 0;
GLuint target = GL_TEXTURE_2D;
};
class CDRMRendererBufferAttachment : public IAttachment {
public:
CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer_, Hyprutils::Memory::CSharedPointer<IBuffer> buffer, EGLImageKHR image, GLuint fbo_,
@ -45,12 +47,19 @@ namespace Aquamarine {
int drmFD = -1;
bool blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to);
struct SBlitResult {
bool success = false;
std::optional<int> syncFD;
};
void setEGL();
void restoreEGL();
SBlitResult blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to, int waitFD = -1);
// can't be a SP<> because we call it from buf's ctor...
void clearBuffer(IBuffer* buf);
void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment);
void setEGL();
void restoreEGL();
void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment);
struct {
struct SShader {
@ -60,8 +69,10 @@ namespace Aquamarine {
} gl;
struct {
EGLDisplay display = nullptr;
EGLContext context = nullptr;
EGLDisplay display = nullptr;
EGLContext context = nullptr;
EGLSync lastBlitSync = nullptr;
int lastBlitSyncFD = -1;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr;
@ -70,6 +81,10 @@ namespace Aquamarine {
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr;
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr;
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr;
} egl;
struct {
@ -78,16 +93,10 @@ namespace Aquamarine {
EGLSurface draw = nullptr, read = nullptr;
} savedEGLState;
struct GLFormat {
uint32_t drmFormat = 0;
uint64_t modifier = 0;
bool external = false;
};
SGLTex glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buf);
Hyprutils::Memory::CWeakPointer<CDRMRenderer> self;
std::vector<GLFormat> formats;
std::vector<SGLFormat> formats;
private:
CDRMRenderer() = default;
@ -96,6 +105,8 @@ namespace Aquamarine {
std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
bool initDRMFormats();
bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
void waitOnSync(int fd);
int recreateBlitSync();
bool hasModifiers = false;
Hyprutils::Memory::CWeakPointer<CBackend> backend;

View File

@ -221,6 +221,7 @@ Aquamarine::CDRMAtomicImpl::CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDR
bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
const auto& STATE = connector->output->state->state();
const bool enable = STATE.enabled;
const auto& MODE = STATE.mode ? STATE.mode : STATE.customMode;
if (data.modeset) {
if (!enable)
@ -262,11 +263,12 @@ bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPoin
}
}
if (STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE && connector->crtc->primary->props.fb_damage_clips) {
if ((STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE) && connector->crtc->primary->props.fb_damage_clips && MODE) {
if (STATE.damage.empty())
data.atomic.fbDamage = 0;
else {
std::vector<pixman_box32_t> rects = STATE.damage.getRects();
TRACE(connector->backend->backend->log(AQ_LOG_TRACE, std::format("atomic drm: clipping damage to pixel size {}", MODE->pixelSize)));
std::vector<pixman_box32_t> rects = STATE.damage.copy().intersect(CBox{{}, MODE->pixelSize}).getRects();
if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, rects.data(), sizeof(pixman_box32_t) * rects.size(), &data.atomic.fbDamage)) {
connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a damage blob");
return false;
@ -308,28 +310,30 @@ bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMCo
bool Aquamarine::CDRMAtomicImpl::reset() {
CDRMAtomicRequest request(backend);
for (auto& crtc : backend->crtcs) {
for (auto const& 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) {
for (auto const& conn : backend->connectors) {
request.add(conn->id, conn->props.crtc_id, 0);
}
for (auto& plane : backend->planes) {
for (auto const& plane : backend->planes) {
request.planeProps(plane, nullptr, 0, {});
}
return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET);
}
bool Aquamarine::CDRMAtomicImpl::moveCursor(SP<SDRMConnector> connector) {
bool Aquamarine::CDRMAtomicImpl::moveCursor(SP<SDRMConnector> connector, bool skipSchedule) {
if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
return true;
connector->output->needsFrame = true;
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
if (!skipSchedule) {
TRACE(connector->backend->log(AQ_LOG_TRACE, "atomic moveCursor"));
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
}
return true;
}

View File

@ -14,12 +14,12 @@ Aquamarine::CDRMLegacyImpl::CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDR
;
}
bool Aquamarine::CDRMLegacyImpl::moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) {
bool Aquamarine::CDRMLegacyImpl::moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipSchedule) {
if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
return true;
connector->output->needsFrame = true;
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
if (!skipSchedule)
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
return true;
}
@ -153,7 +153,7 @@ bool Aquamarine::CDRMLegacyImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMCo
bool Aquamarine::CDRMLegacyImpl::reset() {
bool ok = true;
for (auto& connector : backend->connectors) {
for (auto const& connector : backend->connectors) {
if (!connector->crtc)
continue;

View File

@ -5,7 +5,7 @@ using namespace Hyprutils::Memory;
#define SP CSharedPointer
bool Aquamarine::CAttachmentManager::has(eAttachmentType type) {
for (auto& a : attachments) {
for (auto const& a : attachments) {
if (a->type() == type)
return true;
}
@ -13,7 +13,7 @@ bool Aquamarine::CAttachmentManager::has(eAttachmentType type) {
}
SP<IAttachment> Aquamarine::CAttachmentManager::get(eAttachmentType type) {
for (auto& a : attachments) {
for (auto const& a : attachments) {
if (a->type() == type)
return a;
}

View File

@ -3,7 +3,7 @@
using namespace Aquamarine;
Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMode() {
for (auto& m : modes) {
for (auto const& m : modes) {
if (m->preferred)
return m;
}
@ -11,7 +11,7 @@ Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMod
return nullptr;
}
void Aquamarine::IOutput::moveCursor(const Hyprutils::Math::Vector2D& coord) {
void Aquamarine::IOutput::moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule) {
;
}

View File

@ -1,6 +1,7 @@
#include "FormatUtils.hpp"
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <cstdlib>
std::string fourccToName(uint32_t drmFormat) {
auto fmt = drmGetFormatName(drmFormat);