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" PUBLIC "./include"
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}") PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
set_target_properties(aquamarine PROPERTIES VERSION ${AQUAMARINE_VERSION} set_target_properties(aquamarine PROPERTIES VERSION ${AQUAMARINE_VERSION}
SOVERSION 0) SOVERSION 2)
target_link_libraries(aquamarine OpenGL::EGL OpenGL::GL PkgConfig::deps) target_link_libraries(aquamarine OpenGL::EGL OpenGL::OpenGL PkgConfig::deps)
check_include_file("sys/timerfd.h" HAS_TIMERFD) check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim) pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
@ -71,8 +71,8 @@ endif()
# Protocols # Protocols
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_CLIENT_DIR wayland-client pkgdatadir) pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
message(STATUS "Found wayland-client at ${WAYLAND_CLIENT_DIR}") message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
function(protocolNew protoPath protoName external) function(protocolNew protoPath protoName external)
if(external) if(external)
@ -94,7 +94,7 @@ function(protocolWayland)
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums --client 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}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(aquamarine PRIVATE protocols/wayland.cpp protocols/wayland.hpp) target_sources(aquamarine PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
endfunction() 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 ### 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_NO_ATOMIC` -> Disables drm atomic modesetting
`AQ_MGPU_NO_EXPLICIT` -> Disables explicit syncing on mgpu buffers
### Debugging ### Debugging

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -45,7 +45,7 @@ namespace Aquamarine {
virtual bool test(); virtual bool test();
virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend(); virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot); 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 scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
virtual Hyprutils::Math::Vector2D cursorPlaneSize(); virtual Hyprutils::Math::Vector2D cursorPlaneSize();
virtual bool destroy(); virtual bool destroy();

View File

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

View File

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

View File

@ -113,6 +113,8 @@ namespace Aquamarine {
AQ_SCHEDULE_NEW_MONITOR, AQ_SCHEDULE_NEW_MONITOR,
AQ_SCHEDULE_RENDER_MONITOR, AQ_SCHEDULE_RENDER_MONITOR,
AQ_SCHEDULE_NEEDS_FRAME, AQ_SCHEDULE_NEEDS_FRAME,
AQ_SCHEDULE_ANIMATION,
AQ_SCHEDULE_ANIMATION_DAMAGE,
}; };
virtual bool commit() = 0; virtual bool commit() = 0;
@ -121,7 +123,7 @@ namespace Aquamarine {
virtual std::vector<SDRMFormat> getRenderFormats() = 0; virtual std::vector<SDRMFormat> getRenderFormats() = 0;
virtual Hyprutils::Memory::CSharedPointer<SOutputMode> preferredMode(); virtual Hyprutils::Memory::CSharedPointer<SOutputMode> preferredMode();
virtual bool setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot); 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 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 Hyprutils::Math::Vector2D cursorPlaneSize(); // -1, -1 means no set size, 0, 0 means error
virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN); virtual void scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);

View File

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

View File

@ -1,17 +1,19 @@
#include <aquamarine/allocator/GBM.hpp> #include <aquamarine/allocator/GBM.hpp>
#include <aquamarine/backend/Backend.hpp> #include <aquamarine/backend/Backend.hpp>
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/allocator/Swapchain.hpp> #include <aquamarine/allocator/Swapchain.hpp>
#include "FormatUtils.hpp" #include "FormatUtils.hpp"
#include "Shared.hpp" #include "Shared.hpp"
#include <xf86drm.h> #include <xf86drm.h>
#include <gbm.h> #include <gbm.h>
#include <unistd.h> #include <unistd.h>
#include "../backend/drm/Renderer.hpp"
using namespace Aquamarine; using namespace Aquamarine;
using namespace Hyprutils::Memory; using namespace Hyprutils::Memory;
#define SP CSharedPointer #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()) if (formats.empty())
return SDRMFormat{}; 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. Try to find 10bpp formats first, as they offer better color precision.
For cursors, don't, as these almost never support that. 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()) if (!scanout) {
return *it; 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; 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; return *it;
if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB8888; }); it != formats.end()) for (auto const& f : formats) {
return *it;
for (auto& f : formats) {
auto name = fourccToName(f.drmFormat); auto name = fourccToName(f.drmFormat);
/* 10 bpp RGB */ /* 10 bpp RGB */
@ -41,7 +52,7 @@ static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor)
return f; return f;
} }
for (auto& f : formats) { for (auto const& f : formats) {
auto name = fourccToName(f.drmFormat); auto name = fourccToName(f.drmFormat);
/* 8 bpp RGB */ /* 8 bpp RGB */
@ -65,14 +76,18 @@ Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hypruti
const bool MULTIGPU = params.multigpu && params.scanout; const bool MULTIGPU = params.multigpu && params.scanout;
TRACE(allocator->backend->log(AQ_LOG_TRACE, 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; std::vector<uint64_t> explicitModifiers;
if (attrs.format == DRM_FORMAT_INVALID) { 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) 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))); 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 // check if we can use modifiers. If the requested support has any explicit modifier
// supported by the primary backend, we can. // supported by the primary backend, we can.
for (auto& f : FORMATS) { if (!RENDERABLE.empty()) {
if (f.drmFormat != attrs.format) TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Renderable has {} formats, clipping", RENDERABLE.size())));
continue;
for (auto& m : f.modifiers) { for (auto const& f : FORMATS) {
if (m == DRM_FORMAT_MOD_INVALID) if (f.drmFormat != attrs.format)
continue; 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); bo = gbm_bo_create(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, flags);
} else { } else {
TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Using modifier-based allocation, modifiers: {}", explicitModifiers.size()))); 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))); 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) { 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); 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.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) { for (size_t i = 0; i < (size_t)attrs.planes; ++i) {
attrs.strides.at(i) = gbm_bo_get_stride_for_plane(bo, 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")); modName ? modName : "Unknown"));
free(modName); 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() { Aquamarine::CGBMBuffer::~CGBMBuffer() {
@ -189,7 +240,7 @@ bool Aquamarine::CGBMBuffer::isSynchronous() {
} }
bool Aquamarine::CGBMBuffer::good() { bool Aquamarine::CGBMBuffer::good() {
return true; return bo;
} }
SDMABUFAttrs Aquamarine::CGBMBuffer::dmabuf() { 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) { 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) if (boBuffer)
allocator->backend->log(AQ_LOG_ERROR, "beginDataPtr is called a second time without calling endDataPtr first. Returning old mapping"); allocator->backend->log(AQ_LOG_ERROR, "beginDataPtr is called a second time without calling endDataPtr first. Returning old mapping");
else else
boBuffer = gbm_bo_map(bo, 0, 0, attrs.size.x, attrs.size.y, flags, &dst_stride, &gboMapping); boBuffer = gbm_bo_map(bo, 0, 0, attrs.size.x, attrs.size.y, flags, &stride, &gboMapping);
// FIXME: assumes a 32-bit pixel format
return {(uint8_t*)boBuffer, attrs.format, attrs.size.x * attrs.size.y * 4}; return {(uint8_t*)boBuffer, attrs.format, stride * attrs.size.y};
} }
void Aquamarine::CGBMBuffer::endDataPtr() { void Aquamarine::CGBMBuffer::endDataPtr() {
@ -278,3 +329,7 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CGBMAllocator::getBacken
int Aquamarine::CGBMAllocator::drmFD() { int Aquamarine::CGBMAllocator::drmFD() {
return fd; return fd;
} }
eAllocatorType Aquamarine::CGBMAllocator::type() {
return AQ_ALLOCATOR_TYPE_GBM;
}

View File

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

View File

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

View File

@ -264,7 +264,7 @@ static bool isDRMCard(const char* sysname) {
} }
void Aquamarine::CSession::onReady() { void Aquamarine::CSession::onReady() {
for (auto& d : libinputDevices) { for (auto const& d : libinputDevices) {
if (d->keyboard) if (d->keyboard)
backend->events.newKeyboard.emit(SP<IKeyboard>(d->keyboard)); backend->events.newKeyboard.emit(SP<IKeyboard>(d->keyboard));
if (d->mouse) if (d->mouse)
@ -278,7 +278,7 @@ void Aquamarine::CSession::onReady() {
if (d->tabletPad) if (d->tabletPad)
backend->events.newTabletPad.emit(SP<ITabletPad>(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)); backend->events.newTabletTool.emit(SP<ITabletTool>(t));
} }
} }
@ -306,7 +306,7 @@ void Aquamarine::CSession::dispatchUdevEvents() {
dev_t deviceNum = udev_device_get_devnum(device); dev_t deviceNum = udev_device_get_devnum(device);
SP<CSessionDevice> sessionDevice; SP<CSessionDevice> sessionDevice;
for (auto& sDev : sessionDevices) { for (auto const& sDev : sessionDevices) {
if (sDev->dev == deviceNum) { if (sDev->dev == deviceNum) {
sessionDevice = sDev; sessionDevice = sDev;
break; break;
@ -498,7 +498,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
}; };
for (auto& axis : LAXES) { for (auto const& axis : LAXES) {
if (!libinput_event_pointer_has_axis(pe, axis)) if (!libinput_event_pointer_has_axis(pe, axis))
continue; continue;
@ -596,6 +596,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
hlDevice->touch->events.down.emit(ITouch::SDownEvent{ hlDevice->touch->events.down.emit(ITouch::SDownEvent{
.timeMs = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000), .timeMs = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
.touchID = libinput_event_touch_get_seat_slot(te), .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; break;
} }
@ -639,6 +640,7 @@ void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
if (ENABLED == hlDevice->switchy->state) if (ENABLED == hlDevice->switchy->state)
return; return;
hlDevice->switchy->state = ENABLED;
switch (libinput_event_switch_get_switch(se)) { switch (libinput_event_switch_get_switch(se)) {
case LIBINPUT_SWITCH_LID: hlDevice->switchy->type = ISwitch::AQ_SWITCH_TYPE_LID; break; 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) { SP<CLibinputTabletTool> Aquamarine::CLibinputDevice::toolFrom(libinput_tablet_tool* tool) {
for (auto& t : tabletTools) { for (auto const& t : tabletTools) {
if (t->libinputTool == tool) if (t->libinputTool == tool)
return t; 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))); 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)); 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") { } 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 = 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()) { if (!initDmabuf()) {
backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed"); backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed");
waylandState.dmabufFailed = true; waylandState.dmabufFailed = true;
@ -168,7 +168,7 @@ bool Aquamarine::CWaylandBackend::dispatchEvents() {
// dispatch frames // dispatch frames
if (backend->ready) { if (backend->ready) {
for (auto& f : idleCallbacks) { for (auto const& f : idleCallbacks) {
f(); f();
} }
idleCallbacks.clear(); idleCallbacks.clear();
@ -187,7 +187,7 @@ bool Aquamarine::CWaylandBackend::setCursor(Hyprutils::Memory::CSharedPointer<IB
} }
void Aquamarine::CWaylandBackend::onReady() { void Aquamarine::CWaylandBackend::onReady() {
for (auto& o : outputs) { for (auto const& o : outputs) {
o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock()); o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
if (!o->swapchain) { if (!o->swapchain) {
backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", o->name)); 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) { pointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface, wl_fixed_t x, wl_fixed_t y) {
backend->lastEnterSerial = serial; backend->lastEnterSerial = serial;
for (auto& o : backend->outputs) { for (auto const& o : backend->outputs) {
if (o->waylandState.surface->resource() != surface) if (o->waylandState.surface->resource() != surface)
continue; continue;
@ -269,7 +269,7 @@ Aquamarine::CWaylandPointer::CWaylandPointer(SP<CCWlPointer> pointer_, Hyprutils
}); });
pointer->setLeave([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface) { 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) if (o->waylandState.surface->resource() != surface)
continue; continue;
@ -496,6 +496,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
Aquamarine::CWaylandOutput::~CWaylandOutput() { Aquamarine::CWaylandOutput::~CWaylandOutput() {
backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events
events.destroy.emit();
if (waylandState.xdgToplevel) if (waylandState.xdgToplevel)
waylandState.xdgToplevel->sendDestroy(); waylandState.xdgToplevel->sendDestroy();
if (waylandState.xdgSurface) if (waylandState.xdgSurface)
@ -598,7 +599,7 @@ SP<IBackendImplementation> Aquamarine::CWaylandOutput::getBackend() {
SP<CWaylandBuffer> Aquamarine::CWaylandOutput::wlBufferFromBuffer(SP<IBuffer> buffer) { 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()); }); 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) if (k != buffer)
continue; continue;
@ -654,7 +655,8 @@ bool Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBu
if (!buffer) { if (!buffer) {
cursorState.cursorBuffer.reset(); cursorState.cursorBuffer.reset();
cursorState.cursorWlBuffer.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; return true;
} }
@ -724,7 +726,7 @@ bool Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBu
return true; return true;
} }
void Aquamarine::CWaylandOutput::moveCursor(const Hyprutils::Math::Vector2D& coord) { void Aquamarine::CWaylandOutput::moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipSchedule) {
return; return;
} }

View File

@ -8,6 +8,8 @@
#include <thread> #include <thread>
#include <deque> #include <deque>
#include <cstring> #include <cstring>
#include <filesystem>
#include <system_error>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.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)); backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit device list {}", explicitGpus));
Hyprutils::String::CVarList explicitDevices(explicitGpus, 0, ':', true); Hyprutils::String::CVarList explicitDevices(explicitGpus, 0, ':', true);
// Iterate over GPUs and canonicalize the paths
for (auto& d : explicitDevices) { 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; bool found = false;
for (auto& vd : devices) { for (auto const& vd : devices) {
if (vd->path == d) { if (vd->path == d) {
vecDevices.emplace_back(vd); vecDevices.emplace_back(vd);
found = true; 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)); backend->log(AQ_LOG_ERROR, std::format("drm: Explicit device {} not found", d));
} }
} else { } else {
for (auto& d : devices) { for (auto const& d : devices) {
vecDevices.push_back(d); vecDevices.push_back(d);
} }
} }
@ -201,7 +219,7 @@ std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backe
std::vector<SP<CDRMBackend>> backends; std::vector<SP<CDRMBackend>> backends;
SP<CDRMBackend> newPrimary; SP<CDRMBackend> newPrimary;
for (auto& gpu : gpus) { for (auto const& gpu : gpus) {
auto drmBackend = SP<CDRMBackend>(new CDRMBackend(backend)); auto drmBackend = SP<CDRMBackend>(new CDRMBackend(backend));
drmBackend->self = drmBackend; drmBackend->self = drmBackend;
@ -228,7 +246,7 @@ std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backe
drmBackend->grabFormats(); drmBackend->grabFormats();
drmBackend->scanConnectors(); drmBackend->scanConnectors(false);
drmBackend->recheckCRTCs(); drmBackend->recheckCRTCs();
@ -261,7 +279,7 @@ bool Aquamarine::CDRMBackend::sessionActive() {
void Aquamarine::CDRMBackend::restoreAfterVT() { void Aquamarine::CDRMBackend::restoreAfterVT() {
backend->log(AQ_LOG_DEBUG, "drm: Restoring after VT switch"); backend->log(AQ_LOG_DEBUG, "drm: Restoring after VT switch");
scanConnectors(); scanConnectors(false);
recheckCRTCs(); recheckCRTCs();
backend->log(AQ_LOG_DEBUG, "drm: Rescanned connectors"); backend->log(AQ_LOG_DEBUG, "drm: Rescanned connectors");
@ -271,7 +289,7 @@ void Aquamarine::CDRMBackend::restoreAfterVT() {
std::vector<SP<SDRMConnector>> noMode; std::vector<SP<SDRMConnector>> noMode;
for (auto& c : connectors) { for (auto const& c : connectors) {
if (!c->crtc || !c->output) if (!c->crtc || !c->output)
continue; continue;
@ -284,15 +302,16 @@ void Aquamarine::CDRMBackend::restoreAfterVT() {
}; };
auto& STATE = c->output->state->state(); 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."); 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); noMode.emplace_back(c);
continue; continue;
} }
if (STATE.mode && STATE.mode->modeInfo.has_value()) if (MODE->modeInfo.has_value())
data.modeInfo = *STATE.mode->modeInfo; data.modeInfo = *MODE->modeInfo;
else else
data.calculateMode(c); 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)); 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) if (!c->output)
continue; continue;
@ -475,29 +494,55 @@ bool Aquamarine::CDRMBackend::shouldBlit() {
} }
bool Aquamarine::CDRMBackend::initMgpu() { bool Aquamarine::CDRMBackend::initMgpu() {
if (!primary) SP<CGBMAllocator> newAllocator;
return true; 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); if (!rendererState.allocator) {
mgpu.allocator = newAllocator;
if (!mgpu.allocator) {
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no allocator"); backend->log(AQ_LOG_ERROR, "drm: initMgpu: no allocator");
return false; 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"); backend->log(AQ_LOG_ERROR, "drm: initMgpu: no renderer");
return false; return false;
} }
mgpu.renderer->self = mgpu.renderer; rendererState.renderer->self = rendererState.renderer;
buildGlFormats(rendererState.renderer->formats);
return true; 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() { void Aquamarine::CDRMBackend::recheckCRTCs() {
if (connectors.empty() || crtcs.empty()) if (connectors.empty() || crtcs.empty())
return; return;
@ -505,7 +550,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
backend->log(AQ_LOG_DEBUG, "drm: Rechecking CRTCs"); backend->log(AQ_LOG_DEBUG, "drm: Rechecking CRTCs");
std::vector<SP<SDRMConnector>> recheck, changed; std::vector<SP<SDRMConnector>> recheck, changed;
for (auto& c : connectors) { for (auto const& c : connectors) {
if (c->crtc && c->status == DRM_MODE_CONNECTED) { 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)); backend->log(AQ_LOG_DEBUG, std::format("drm: Skipping connector {}, has crtc {} and is connected", c->szName, c->crtc->id));
continue; continue;
@ -517,7 +562,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
for (size_t i = 0; i < crtcs.size(); ++i) { for (size_t i = 0; i < crtcs.size(); ++i) {
bool taken = false; bool taken = false;
for (auto& c : connectors) { for (auto const& c : connectors) {
if (c->crtc != crtcs.at(i)) if (c->crtc != crtcs.at(i))
continue; continue;
@ -535,7 +580,7 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
bool assigned = false; bool assigned = false;
// try to use a connected connector // try to use a connected connector
for (auto& c : recheck) { for (auto const& c : recheck) {
if (!(c->possibleCrtcs & (1 << i))) if (!(c->possibleCrtcs & (1 << i)))
continue; 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)); 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) if (c->status == DRM_MODE_CONNECTED)
continue; 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. // if any connectors get a crtc and are connected, we need to rescan to assign them outputs.
bool rescan = false; bool rescan = false;
for (auto& c : changed) { for (auto const& c : changed) {
if (!c->output && c->status == DRM_MODE_CONNECTED) { if (!c->output && c->status == DRM_MODE_CONNECTED) {
rescan = true; rescan = true;
continue; continue;
@ -599,8 +644,12 @@ bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBacken
gpuName = drmName; gpuName = drmName;
auto drmVerName = drmVer->name ? drmVer->name : "unknown";
if (std::string_view(drmVerName) == "evdi")
primary = {};
backend->log(AQ_LOG_DEBUG, 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) : ""))); (primary ? std::format(" with primary {}", primary->gpu->path) : "")));
drmFreeVersion(drmVer); drmFreeVersion(drmVer);
@ -609,7 +658,7 @@ bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBacken
auto E = std::any_cast<CSessionDevice::SChangeEvent>(d); auto E = std::any_cast<CSessionDevice::SChangeEvent>(d);
if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG) { if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Got a hotplug event for {}", gpuName)); backend->log(AQ_LOG_DEBUG, std::format("drm: Got a hotplug event for {}", gpuName));
scanConnectors(); scanConnectors(false);
recheckCRTCs(); recheckCRTCs();
} else if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE) { } else if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Got a lease event for {}", gpuName)); 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; 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)); backend->log(AQ_LOG_DEBUG, std::format("drm: Scanning connectors for {}", gpu->path));
auto resources = drmModeGetResources(gpu->fd); auto resources = drmModeGetResources(gpu->fd);
@ -668,19 +717,19 @@ void Aquamarine::CDRMBackend::scanConnectors() {
conn->status = drmConn->connection; conn->status = drmConn->connection;
if (!conn->crtc) { if (conn->crtc)
backend->log(AQ_LOG_DEBUG, std::format("drm: Ignoring connector {} because it has no CRTC", connectorID)); conn->recheckCRTCProps();
continue;
}
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state: {}", connectorID, (int)drmConn->connection)); backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state: {}", connectorID, (int)drmConn->connection));
if (conn->status == DRM_MODE_CONNECTED && !conn->output) { if (allowConnect) {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName)); if (conn->status == DRM_MODE_CONNECTED && !conn->output) {
conn->connect(drmConn); backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
} else if (conn->status != DRM_MODE_CONNECTED && conn->output) { conn->connect(drmConn);
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} disconnected", conn->szName)); } else if (conn->status != DRM_MODE_CONNECTED && conn->output) {
conn->disconnect(); backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} disconnected", conn->szName));
conn->disconnect();
}
} }
drmModeFreeConnector(drmConn); drmModeFreeConnector(drmConn);
@ -696,7 +745,7 @@ void Aquamarine::CDRMBackend::scanLeases() {
return; return;
} }
for (auto& c : connectors) { for (auto const& c : connectors) {
if (!c->output || !c->output->lease) if (!c->output || !c->output->lease)
continue; continue;
@ -718,7 +767,7 @@ void Aquamarine::CDRMBackend::scanLeases() {
auto l = c->output->lease; auto l = c->output->lease;
for (auto& c2 : connectors) { for (auto const& c2 : connectors) {
if (!c2->output || c2->output->lease != c->output->lease) if (!c2->output || c2->output->lease != c->output->lease)
continue; continue;
@ -775,7 +824,7 @@ static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, un
.flags = flags, .flags = flags,
}); });
if (BACKEND->sessionActive() && !pageFlip->connector->frameEventScheduled) if (BACKEND->sessionActive() && !pageFlip->connector->frameEventScheduled && pageFlip->connector->output->enabledState)
pageFlip->connector->output->events.frame.emit(); pageFlip->connector->output->events.frame.emit();
} }
@ -792,6 +841,8 @@ bool Aquamarine::CDRMBackend::dispatchEvents() {
} }
uint32_t Aquamarine::CDRMBackend::capabilities() { uint32_t Aquamarine::CDRMBackend::capabilities() {
if (getCursorFormats().empty())
return 0;
return eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER; return eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER;
} }
@ -802,7 +853,26 @@ bool Aquamarine::CDRMBackend::setCursor(SP<IBuffer> buffer, const Hyprutils::Mat
void Aquamarine::CDRMBackend::onReady() { void Aquamarine::CDRMBackend::onReady() {
backend->log(AQ_LOG_DEBUG, std::format("drm: Connectors size2 {}", connectors.size())); 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)); backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {}", c->id));
if (!c->output) if (!c->output)
continue; continue;
@ -824,7 +894,7 @@ void Aquamarine::CDRMBackend::onReady() {
} }
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() { std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
for (auto& p : planes) { for (auto const& p : planes) {
if (p->type != DRM_PLANE_TYPE_PRIMARY) if (p->type != DRM_PLANE_TYPE_PRIMARY)
continue; continue;
@ -834,8 +904,12 @@ std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
return {}; return {};
} }
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderableFormats() {
return glFormats;
}
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() { std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() {
for (auto& p : planes) { for (auto const& p : planes) {
if (p->type != DRM_PLANE_TYPE_CURSOR) if (p->type != DRM_PLANE_TYPE_CURSOR)
continue; continue;
@ -1089,13 +1163,30 @@ void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
di_info_destroy(info); 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) { void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
if (output) { if (output) {
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not connecting connector {} because it's already connected", szName)); backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not connecting connector {} because it's already connected", szName));
return; 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 = SP<CDRMOutput>(new CDRMOutput(szName, backend, self.lock()));
output->self = output; output->self = output;
@ -1129,8 +1220,6 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
//uint64_t modeID = 0; //uint64_t modeID = 0;
// getDRMProp(backend->gpu->fd, crtc->id, crtc->props.mode_id, &modeID); // getDRMProp(backend->gpu->fd, crtc->id, crtc->props.mode_id, &modeID);
crtc->refresh = calculateRefresh(drmMode);
} }
backend->backend->log(AQ_LOG_DEBUG, backend->backend->log(AQ_LOG_DEBUG,
@ -1138,10 +1227,8 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
aqMode->preferred ? " (preferred)" : "")); aqMode->preferred ? " (preferred)" : ""));
} }
if (!currentModeInfo && fallbackMode) { if (!currentModeInfo && fallbackMode)
output->state->setMode(fallbackMode); output->state->setMode(fallbackMode);
crtc->refresh = calculateRefresh(fallbackMode->modeInfo.value());
}
output->physicalSize = {(double)connector->mmWidth, (double)connector->mmHeight}; output->physicalSize = {(double)connector->mmWidth, (double)connector->mmHeight};
@ -1164,13 +1251,6 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
output->nonDesktop = prop; 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); maxBpcBounds.fill(0);
if (props.max_bpc && !introspectDRMPropRange(backend->gpu->fd, props.max_bpc, maxBpcBounds.data(), &maxBpcBounds[1])) 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 // TODO: subconnectors
output->make = make; output->make = make;
output->model = model; output->model = model;
output->serial = serial; output->serial = serial;
output->description = std::format("{} {} {} ({})", make, model, serial, szName); output->description = std::format("{} {} {} ({})", make, model, serial, szName);
output->needsFrame = true; 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"));
backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Description {}", output->description)); backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Description {}", output->description));
status = DRM_MODE_CONNECTED; status = DRM_MODE_CONNECTED;
recheckCRTCProps();
if (!backend->backend->ready) if (!backend->backend->ready)
return; return;
output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock()); 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)); backend->backend->events.newOutput.emit(SP<IOutput>(output));
output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR); 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) if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
refresh = calculateRefresh(data.modeInfo); refresh = calculateRefresh(data.modeInfo);
output->enabledState = output->state->state().enabled;
} }
void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) { 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) if (crtc->cursor && data.cursorFB)
crtc->cursor->back = data.cursorFB; crtc->cursor->back = data.cursorFB;
@ -1289,7 +1376,6 @@ bool Aquamarine::CDRMOutput::test() {
void Aquamarine::CDRMOutput::setCursorVisible(bool visible) { void Aquamarine::CDRMOutput::setCursorVisible(bool visible) {
cursorVisible = visible; cursorVisible = visible;
needsFrame = true;
scheduleFrame(AQ_SCHEDULE_CURSOR_VISIBLE); scheduleFrame(AQ_SCHEDULE_CURSOR_VISIBLE);
} }
@ -1308,12 +1394,33 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
const uint32_t COMMITTED = STATE.committed; const uint32_t COMMITTED = STATE.committed;
if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED) && STATE.enabled) { 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"); backend->backend->log(AQ_LOG_ERROR, "drm: No mode on enable commit");
return false; 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) { if (STATE.adaptiveSync && !connector->canDoVrr) {
backend->backend->log(AQ_LOG_ERROR, "drm: No Adaptive sync support for output"); backend->backend->log(AQ_LOG_ERROR, "drm: No Adaptive sync support for output");
return false; return false;
@ -1347,7 +1454,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (!MODE) // modeless commits are invalid if (!MODE) // modeless commits are invalid
return false; return false;
uint32_t flags = 0; uint32_t flags = 0;
if (!onlyTest) { if (!onlyTest) {
if (NEEDS_RECONFIG) { if (NEEDS_RECONFIG) {
@ -1385,7 +1492,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (!mgpu.swapchain) { if (!mgpu.swapchain) {
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No swapchain for blit, creating")); 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(); auto OPTIONS = swapchain->currentOptions();
@ -1401,12 +1508,22 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
return false; return false;
} }
auto NEWAQBUF = mgpu.swapchain->next(nullptr); auto NEWAQBUF = mgpu.swapchain->next(nullptr);
if (!backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF)) { 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"); backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed");
return false; 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 drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
} else } else
drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present 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))); fourccToName(STATE.drmFormat), fourccToName(params.format)));
state->setFormat(params.format); state->setFormat(params.format);
formatMismatch = true; formatMismatch = true;
// TODO: reject if tearing? We will miss a frame event!
flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; // we cannot modeset with async pf 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. // to avoid doing this over and over.
data.modeset = true; data.modeset = true;
data.blocking = true; data.blocking = true;
data.flags = DRM_MODE_PAGE_FLIP_EVENT; data.flags = onlyTest ? 0 : DRM_MODE_PAGE_FLIP_EVENT;
ok = connector->commitState(data); ok = connector->commitState(data);
if (!ok) if (!ok)
@ -1489,6 +1607,28 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
if (ok) if (ok)
connector->commitTainted = false; 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; return ok;
} }
@ -1502,6 +1642,9 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
return false; return false;
} }
if (!connector->crtc)
return false;
if (!buffer) if (!buffer)
setCursorVisible(false); setCursorVisible(false);
else { else {
@ -1512,7 +1655,7 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
if (!mgpu.cursorSwapchain) { if (!mgpu.cursorSwapchain) {
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No cursorSwapchain for blit, creating")); 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(); 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); 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"); backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but cursor blit failed");
return false; return false;
} }
@ -1552,15 +1695,14 @@ bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotsp
cursorVisible = true; cursorVisible = true;
} }
needsFrame = true;
scheduleFrame(AQ_SCHEDULE_CURSOR_SHAPE); scheduleFrame(AQ_SCHEDULE_CURSOR_SHAPE);
return true; return true;
} }
void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord) { void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord, bool skipSchedule) {
cursorPos = coord; cursorPos = coord;
cursorVisible = true; // cursorVisible = true;
backend->impl->moveCursor(connector); backend->impl->moveCursor(connector, skipSchedule);
} }
void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) { void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) {
@ -1569,7 +1711,7 @@ void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) {
connector->isPageFlipPending, connector->frameEventScheduled))); connector->isPageFlipPending, connector->frameEventScheduled)));
needsFrame = true; needsFrame = true;
if (connector->isPageFlipPending || connector->frameEventScheduled) if (connector->isPageFlipPending || connector->frameEventScheduled || !enabledState)
return; return;
connector->frameEventScheduled = true; connector->frameEventScheduled = true;
@ -1597,6 +1739,10 @@ size_t Aquamarine::CDRMOutput::getGammaSize() {
} }
std::vector<SDRMFormat> Aquamarine::CDRMOutput::getRenderFormats() { 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; return connector->crtc->primary->formats;
} }
@ -1816,14 +1962,14 @@ void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CShar
di_cvt_compute(&timing, &options); 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 vsync_start = timing.v_lines_rnd + timing.v_front_porch;
uint16_t hsync_end = hsync_start + timing.h_sync; uint16_t hsync_end = hsync_start + timing.h_sync;
uint16_t vsync_end = vsync_start + timing.v_sync; uint16_t vsync_end = vsync_start + timing.v_sync;
modeInfo = (drmModeModeInfo){ modeInfo = (drmModeModeInfo){
.clock = (uint32_t)std::round(timing.act_pixel_freq * 1000), .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_start = hsync_start,
.hsync_end = hsync_end, .hsync_end = hsync_end,
.htotal = (uint16_t)(hsync_end + timing.h_back_porch), .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, .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); 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_) { 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(); auto backend = ((CDRMBackend*)outputs.at(0)->getBackend().get())->self.lock();
for (auto& o : outputs) { for (auto const& o : outputs) {
if (o->getBackend() != backend) { if (o->getBackend() != backend) {
backend->log(AQ_LOG_ERROR, "drm lease: Mismatched backends"); backend->log(AQ_LOG_ERROR, "drm lease: Mismatched backends");
return nullptr; return nullptr;
@ -1861,7 +2012,7 @@ SP<CDRMLease> Aquamarine::CDRMLease::create(std::vector<SP<IOutput>> outputs) {
auto lease = SP<CDRMLease>(new CDRMLease); auto lease = SP<CDRMLease>(new CDRMLease);
for (auto& o : outputs) { for (auto const& o : outputs) {
auto drmo = ((CDRMOutput*)o.get())->self.lock(); auto drmo = ((CDRMOutput*)o.get())->self.lock();
backend->log(AQ_LOG_DEBUG, std::format("drm lease: output {}, connector {}", drmo->name, drmo->connector->id)); 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; return nullptr;
} }
for (auto& o : lease->outputs) { for (auto const& o : lease->outputs) {
o->lease = lease; o->lease = lease;
} }

View File

@ -3,6 +3,7 @@
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include <cstring> #include <cstring>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include "Math.hpp" #include "Math.hpp"
#include "Shared.hpp" #include "Shared.hpp"
#include "FormatUtils.hpp" #include "FormatUtils.hpp"
@ -153,9 +154,9 @@ bool CDRMRenderer::initDRMFormats() {
TRACE(backend->log(AQ_LOG_TRACE, "EGL: Supported formats:")); 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; std::vector<std::pair<uint64_t, bool>> mods;
auto ret = getModsForFormat(fmt); auto ret = getModsForFormat(fmt);
@ -169,8 +170,8 @@ bool CDRMRenderer::initDRMFormats() {
// EGL can always do implicit modifiers. // EGL can always do implicit modifiers.
mods.push_back({DRM_FORMAT_MOD_INVALID, true}); mods.push_back({DRM_FORMAT_MOD_INVALID, true});
for (auto& [mod, external] : mods) { for (auto const& [mod, external] : mods) {
dmaFormats.push_back(GLFormat{ dmaFormats.push_back(SGLFormat{
.drmFormat = (uint32_t)fmt, .drmFormat = (uint32_t)fmt,
.modifier = mod, .modifier = mod,
.external = external, .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))); 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); auto modName = drmGetFormatModifierName(mod);
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: | {}with modifier 0x{:x}: {}", (external ? "external only " : ""), mod, modName ? modName : "?unknown?"))); TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: | {}with modifier 0x{:x}: {}", (external ? "external only " : ""), mod, modName ? modName : "?unknown?")));
free(modName); free(modName);
@ -223,6 +224,20 @@ SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAll
loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT");
loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); 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) { if (!renderer->egl.eglGetPlatformDisplayEXT) {
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no 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; bool external = false;
for (auto& fmt : formats) { for (auto const& fmt : formats) {
if (fmt.drmFormat != dma.format || fmt.modifier != dma.modifier) if (fmt.drmFormat != dma.format || fmt.modifier != dma.modifier)
continue; continue;
@ -469,12 +484,140 @@ inline const float fullVerts[] = {
0, 1, // bottom left 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(); setEGL();
if (from->dmabuf().size != to->dmabuf().size) { if (from->dmabuf().size != to->dmabuf().size) {
backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched"); 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 // firstly, get a texture from the from buffer
@ -514,7 +657,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
if (!verifyDestinationDMABUF(toDma)) { if (!verifyDestinationDMABUF(toDma)) {
backend->log(AQ_LOG_ERROR, "EGL (blit): failed to blit: destination dmabuf unsupported"); 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); rboImage = createEGLImage(toDma);
if (rboImage == EGL_NO_IMAGE_KHR) { if (rboImage == EGL_NO_IMAGE_KHR) {
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError())); backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
return false; return {};
} }
GLCALL(glGenRenderbuffers(1, &rboID)); GLCALL(glGenRenderbuffers(1, &rboID));
@ -547,7 +690,7 @@ bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError())); 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. // 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)); GLCALL(glBindTexture(fromTex.target, 0));
// rendered, cleanup // rendered, cleanup
glFlush(); 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(glBindFramebuffer(GL_FRAMEBUFFER, 0));
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
restoreEGL(); restoreEGL();
return true; return {true, explicitFD == -1 ? std::nullopt : std::optional<int>{explicitFD}};
} }
void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) { void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {
@ -655,7 +802,7 @@ void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachme
} }
bool CDRMRenderer::verifyDestinationDMABUF(const SDMABUFAttrs& attrs) { bool CDRMRenderer::verifyDestinationDMABUF(const SDMABUFAttrs& attrs) {
for (auto& fmt : formats) { for (auto const& fmt : formats) {
if (fmt.drmFormat != attrs.format) if (fmt.drmFormat != attrs.format)
continue; continue;

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <aquamarine/backend/DRM.hpp> #include <aquamarine/backend/DRM.hpp>
#include "FormatUtils.hpp"
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
@ -19,6 +20,7 @@ namespace Aquamarine {
GLuint texid = 0; GLuint texid = 0;
GLuint target = GL_TEXTURE_2D; GLuint target = GL_TEXTURE_2D;
}; };
class CDRMRendererBufferAttachment : public IAttachment { class CDRMRendererBufferAttachment : public IAttachment {
public: public:
CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer_, Hyprutils::Memory::CSharedPointer<IBuffer> buffer, EGLImageKHR image, GLuint fbo_, CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer_, Hyprutils::Memory::CSharedPointer<IBuffer> buffer, EGLImageKHR image, GLuint fbo_,
@ -45,12 +47,19 @@ namespace Aquamarine {
int drmFD = -1; 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(); SBlitResult blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to, int waitFD = -1);
void restoreEGL(); // 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 {
struct SShader { struct SShader {
@ -60,8 +69,10 @@ namespace Aquamarine {
} gl; } gl;
struct { struct {
EGLDisplay display = nullptr; EGLDisplay display = nullptr;
EGLContext context = nullptr; EGLContext context = nullptr;
EGLSync lastBlitSync = nullptr;
int lastBlitSyncFD = -1;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr;
@ -70,6 +81,10 @@ namespace Aquamarine {
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr; PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr;
PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr;
PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr;
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr;
} egl; } egl;
struct { struct {
@ -78,16 +93,10 @@ namespace Aquamarine {
EGLSurface draw = nullptr, read = nullptr; EGLSurface draw = nullptr, read = nullptr;
} savedEGLState; } savedEGLState;
struct GLFormat {
uint32_t drmFormat = 0;
uint64_t modifier = 0;
bool external = false;
};
SGLTex glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buf); SGLTex glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buf);
Hyprutils::Memory::CWeakPointer<CDRMRenderer> self; Hyprutils::Memory::CWeakPointer<CDRMRenderer> self;
std::vector<GLFormat> formats; std::vector<SGLFormat> formats;
private: private:
CDRMRenderer() = default; CDRMRenderer() = default;
@ -96,6 +105,8 @@ namespace Aquamarine {
std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format); std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
bool initDRMFormats(); bool initDRMFormats();
bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs); bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
void waitOnSync(int fd);
int recreateBlitSync();
bool hasModifiers = false; bool hasModifiers = false;
Hyprutils::Memory::CWeakPointer<CBackend> backend; 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) { bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
const auto& STATE = connector->output->state->state(); const auto& STATE = connector->output->state->state();
const bool enable = STATE.enabled; const bool enable = STATE.enabled;
const auto& MODE = STATE.mode ? STATE.mode : STATE.customMode;
if (data.modeset) { if (data.modeset) {
if (!enable) 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()) if (STATE.damage.empty())
data.atomic.fbDamage = 0; data.atomic.fbDamage = 0;
else { 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)) { 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"); connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a damage blob");
return false; return false;
@ -308,28 +310,30 @@ bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMCo
bool Aquamarine::CDRMAtomicImpl::reset() { bool Aquamarine::CDRMAtomicImpl::reset() {
CDRMAtomicRequest request(backend); 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.mode_id, 0);
request.add(crtc->id, crtc->props.active, 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); 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, {}); request.planeProps(plane, nullptr, 0, {});
} }
return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET); 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) if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
return true; return true;
connector->output->needsFrame = true; if (!skipSchedule) {
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE); TRACE(connector->backend->log(AQ_LOG_TRACE, "atomic moveCursor"));
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
}
return true; 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) if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
return true; return true;
connector->output->needsFrame = true; if (!skipSchedule)
connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE); connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
return true; return true;
} }
@ -153,7 +153,7 @@ bool Aquamarine::CDRMLegacyImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMCo
bool Aquamarine::CDRMLegacyImpl::reset() { bool Aquamarine::CDRMLegacyImpl::reset() {
bool ok = true; bool ok = true;
for (auto& connector : backend->connectors) { for (auto const& connector : backend->connectors) {
if (!connector->crtc) if (!connector->crtc)
continue; continue;

View File

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

View File

@ -3,7 +3,7 @@
using namespace Aquamarine; using namespace Aquamarine;
Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMode() { Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMode() {
for (auto& m : modes) { for (auto const& m : modes) {
if (m->preferred) if (m->preferred)
return m; return m;
} }
@ -11,7 +11,7 @@ Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMod
return nullptr; 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 "FormatUtils.hpp"
#include <drm_fourcc.h> #include <drm_fourcc.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <cstdlib>
std::string fourccToName(uint32_t drmFormat) { std::string fourccToName(uint32_t drmFormat) {
auto fmt = drmGetFormatName(drmFormat); auto fmt = drmGetFormatName(drmFormat);