From c788a59dac3647aaa973635112f234c06914a05d Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 19 Jul 2024 14:20:54 +0200 Subject: [PATCH] basic gpu stuff --- CMakeLists.txt | 3 + README.md | 1 + src/Hyprpaper.cpp | 323 +++-------------------- src/Hyprpaper.hpp | 36 +-- src/helpers/Monitor.cpp | 2 + src/helpers/Monitor.hpp | 5 +- src/helpers/PoolBuffer.hpp | 18 -- src/main.cpp | 9 +- src/render/Buffer.cpp | 202 ++++++++++++++ src/render/Buffer.hpp | 51 ++++ src/render/Egl.cpp | 162 ++++++++++++ src/render/Egl.hpp | 38 +++ src/render/LayerSurface.cpp | 5 + src/render/LayerSurface.hpp | 9 +- src/render/Math.cpp | 207 +++++++++++++++ src/render/Math.hpp | 18 ++ src/render/Renderer.cpp | 467 +++++++++++++++++++++++++++++++++ src/render/Renderer.hpp | 52 ++++ src/render/WallpaperTarget.cpp | 20 +- src/render/WallpaperTarget.hpp | 16 +- 20 files changed, 1312 insertions(+), 332 deletions(-) delete mode 100644 src/helpers/PoolBuffer.hpp create mode 100644 src/render/Buffer.cpp create mode 100644 src/render/Buffer.hpp create mode 100644 src/render/Egl.cpp create mode 100644 src/render/Egl.hpp create mode 100644 src/render/Math.cpp create mode 100644 src/render/Math.hpp create mode 100644 src/render/Renderer.cpp create mode 100644 src/render/Renderer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c3b0f1e..a398016 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,9 @@ pkg_check_modules( deps REQUIRED IMPORTED_TARGET + gbm + egl + libdrm wayland-client wayland-protocols cairo diff --git a/README.md b/README.md index 1c6fec6..460cc58 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Hyprpaper is a blazing fast wallpaper utility for Hyprland with the ability to d - Per-output wallpapers - fill or contain modes - fractional scaling support + - gpu acceleration - IPC for blazing fast wallpaper switches - preloading targets into memory diff --git a/src/Hyprpaper.cpp b/src/Hyprpaper.cpp index ca9d1ae..8e6fcad 100644 --- a/src/Hyprpaper.cpp +++ b/src/Hyprpaper.cpp @@ -1,8 +1,17 @@ #include "Hyprpaper.hpp" +#include "render/Renderer.hpp" #include #include #include #include +#include "render/Egl.hpp" + +#include "protocols/wayland.hpp" +#include "protocols/linux-dmabuf-v1.hpp" +#include "protocols/wlr-layer-shell-unstable-v1.hpp" +#include "protocols/fractional-scale-v1.hpp" +#include "protocols/viewporter.hpp" +#include "protocols/cursor-shape-v1.hpp" CHyprpaper::CHyprpaper() = default; @@ -33,6 +42,18 @@ static void handleGlobal(CCWlRegistry* registry, uint32_t name, const char* inte } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { g_pHyprpaper->m_pCursorShape = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)registry->resource(), name, &wp_cursor_shape_manager_v1_interface, 1)); + } else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { + g_pHyprpaper->m_pLinuxDmabuf = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)registry->resource(), name, &zwp_linux_dmabuf_v1_interface, 3)); + g_pHyprpaper->m_pLinuxDmabuf->setModifier([](CCZwpLinuxDmabufV1* r, uint32_t fmt, uint32_t modHi, uint32_t modLo) { + g_pHyprpaper->m_vDmabufFormats.emplace_back(SDMABUFFormat{ + .format = fmt, + .modifier = (((uint64_t)modLo) << 32) | (uint64_t)modLo, + }); + }); + + wl_display_roundtrip(g_pHyprpaper->m_sDisplay); + + g_pHyprpaper->m_pLinuxDmabuf = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)registry->resource(), name, &zwp_linux_dmabuf_v1_interface, 4)); } } @@ -70,6 +91,25 @@ void CHyprpaper::init() { wl_display_roundtrip(m_sDisplay); + g_pRenderer = std::make_unique(!m_bNoGpu); + + if (!m_bNoGpu) { + try { + if (m_vDmabufFormats.empty()) { + Debug::log(ERR, "No dmabuf support, using cpu rendering"); + m_bNoGpu = true; + } + } catch (std::exception& e) { + std::cerr << "Failed to create a gpu context: " << e.what() << ", falling back to cpu\n"; + m_bNoGpu = true; + } + } + + if (m_bNoGpu && (g_pEGL || g_pRenderer->gbmDevice)) { + g_pEGL.reset(); + g_pRenderer = std::make_unique(!m_bNoGpu); + } + while (m_vMonitors.size() < 1 || m_vMonitors[0]->name.empty()) { wl_display_dispatch(m_sDisplay); } @@ -99,7 +139,6 @@ void CHyprpaper::tick(bool force) { return; preloadAllWallpapersFromConfig(); - ensurePoolBuffersPresent(); recheckAllMonitors(); } @@ -129,25 +168,6 @@ void CHyprpaper::unloadWallpaper(const std::string& path) { return; } - // clean buffers - for (auto it = m_vBuffers.begin(); it != m_vBuffers.end();) { - - if (it->get()->target != path) { - it++; - continue; - } - - const auto PRELOADPATH = it->get()->name; - - Debug::log(LOG, "Unloading target %s, preload path %s", path.c_str(), PRELOADPATH.c_str()); - - std::filesystem::remove(PRELOADPATH); - - destroyBuffer(it->get()); - - it = m_vBuffers.erase(it); - } - m_mWallpaperTargets.erase(path); // will free the cairo surface } @@ -224,7 +244,7 @@ void CHyprpaper::recheckMonitor(SMonitor* pMonitor) { if (pMonitor->wantsReload) { pMonitor->wantsReload = false; - renderWallpaperForMonitor(pMonitor); + g_pRenderer->renderWallpaperForMonitor(pMonitor); } } @@ -272,46 +292,6 @@ SMonitor* CHyprpaper::getMonitorFromName(const std::string& monname) { return nullptr; } -void CHyprpaper::ensurePoolBuffersPresent() { - bool anyNewBuffers = false; - - for (auto& [file, wt] : m_mWallpaperTargets) { - for (auto& m : m_vMonitors) { - - if (m->size == Vector2D()) - continue; - - auto it = std::find_if(m_vBuffers.begin(), m_vBuffers.end(), [wt = &wt, &m](const std::unique_ptr& el) { - auto scale = std::round((m->pCurrentLayerSurface && m->pCurrentLayerSurface->pFractionalScaleInfo ? m->pCurrentLayerSurface->fScale : m->scale) * 120.0) / 120.0; - return el->target == wt->m_szPath && vectorDeltaLessThan(el->pixelSize, m->size * scale, 1); - }); - - if (it == m_vBuffers.end()) { - // create - const auto PBUFFER = m_vBuffers.emplace_back(std::make_unique()).get(); - auto scale = std::round((m->pCurrentLayerSurface && m->pCurrentLayerSurface->pFractionalScaleInfo ? m->pCurrentLayerSurface->fScale : m->scale) * 120.0) / 120.0; - createBuffer(PBUFFER, m->size.x * scale, m->size.y * scale, WL_SHM_FORMAT_ARGB8888); - - PBUFFER->target = wt.m_szPath; - - Debug::log(LOG, "Buffer created for target %s, Shared Memory usage: %.1fMB", wt.m_szPath.c_str(), PBUFFER->size / 1000000.f); - - anyNewBuffers = true; - } - } - } - - if (anyNewBuffers) { - uint64_t bytesUsed = 0; - - for (auto& bf : m_vBuffers) { - bytesUsed += bf->size; - } - - Debug::log(LOG, "Total SM usage for all buffers: %.1fMB", bytesUsed / 1000000.f); - } -} - void CHyprpaper::clearWallpaperFromMonitor(const std::string& monname) { const auto PMONITOR = getMonitorFromName(monname); @@ -411,227 +391,6 @@ void CHyprpaper::createLSForMonitor(SMonitor* pMonitor) { pMonitor->pCurrentLayerSurface = pMonitor->layerSurfaces.emplace_back(std::make_unique(pMonitor)).get(); } -bool CHyprpaper::setCloexec(const int& FD) { - long flags = fcntl(FD, F_GETFD); - if (flags == -1) { - return false; - } - - if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) { - return false; - } - - return true; -} - -int CHyprpaper::createPoolFile(size_t size, std::string& name) { - const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR"); - if (!XDGRUNTIMEDIR) { - Debug::log(CRIT, "XDG_RUNTIME_DIR not set!"); - exit(1); - } - - name = std::string(XDGRUNTIMEDIR) + "/.hyprpaper_XXXXXX"; - - const auto FD = mkstemp((char*)name.c_str()); - if (FD < 0) { - Debug::log(CRIT, "createPoolFile: fd < 0"); - exit(1); - } - - if (!setCloexec(FD)) { - close(FD); - Debug::log(CRIT, "createPoolFile: !setCloexec"); - exit(1); - } - - if (ftruncate(FD, size) < 0) { - close(FD); - Debug::log(CRIT, "createPoolFile: ftruncate < 0"); - exit(1); - } - - return FD; -} - -void CHyprpaper::createBuffer(SPoolBuffer* pBuffer, int32_t w, int32_t h, uint32_t format) { - const size_t STRIDE = w * 4; - const size_t SIZE = STRIDE * h; - - std::string name; - const auto FD = createPoolFile(SIZE, name); - - if (FD == -1) { - Debug::log(CRIT, "Unable to create pool file!"); - exit(1); - } - - const auto DATA = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0); - auto POOL = makeShared(g_pHyprpaper->m_pSHM->sendCreatePool(FD, SIZE)); - pBuffer->buffer = makeShared(POOL->sendCreateBuffer(0, w, h, STRIDE, format)); - POOL.reset(); - - close(FD); - - pBuffer->size = SIZE; - pBuffer->data = DATA; - pBuffer->surface = cairo_image_surface_create_for_data((unsigned char*)DATA, CAIRO_FORMAT_ARGB32, w, h, STRIDE); - pBuffer->cairo = cairo_create(pBuffer->surface); - pBuffer->pixelSize = Vector2D(w, h); - pBuffer->name = name; -} - -void CHyprpaper::destroyBuffer(SPoolBuffer* pBuffer) { - pBuffer->buffer.reset(); - cairo_destroy(pBuffer->cairo); - cairo_surface_destroy(pBuffer->surface); - munmap(pBuffer->data, pBuffer->size); - - pBuffer->buffer = nullptr; -} - -SPoolBuffer* CHyprpaper::getPoolBuffer(SMonitor* pMonitor, CWallpaperTarget* pWallpaperTarget) { - const auto IT = std::find_if(m_vBuffers.begin(), m_vBuffers.end(), [&](const std::unique_ptr& el) { - auto scale = - std::round((pMonitor->pCurrentLayerSurface && pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? pMonitor->pCurrentLayerSurface->fScale : pMonitor->scale) * - 120.0) / - 120.0; - return el->target == pWallpaperTarget->m_szPath && vectorDeltaLessThan(el->pixelSize, pMonitor->size * scale, 1); - }); - - if (IT == m_vBuffers.end()) - return nullptr; - return IT->get(); -} - -void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) { - static auto* const PRENDERSPLASH = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash")->getDataStaticPtr()); - static auto* const PSPLASHOFFSET = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash_offset")->getDataStaticPtr()); - - if (!m_mMonitorActiveWallpaperTargets[pMonitor]) - recheckMonitor(pMonitor); - - const auto PWALLPAPERTARGET = m_mMonitorActiveWallpaperTargets[pMonitor]; - const auto CONTAIN = m_mMonitorWallpaperRenderData[pMonitor->name].contain; - - if (!PWALLPAPERTARGET) { - Debug::log(CRIT, "wallpaper target null in render??"); - exit(1); - } - - auto* PBUFFER = getPoolBuffer(pMonitor, PWALLPAPERTARGET); - - if (!PBUFFER) { - Debug::log(LOG, "Pool buffer missing for available target??"); - ensurePoolBuffersPresent(); - - PBUFFER = getPoolBuffer(pMonitor, PWALLPAPERTARGET); - - if (!PBUFFER) { - Debug::log(LOG, "Pool buffer failed #2. Ignoring WP."); - return; - } - } - - const double SURFACESCALE = pMonitor->pCurrentLayerSurface && pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? pMonitor->pCurrentLayerSurface->fScale : pMonitor->scale; - const Vector2D DIMENSIONS = Vector2D{std::round(pMonitor->size.x * SURFACESCALE), std::round(pMonitor->size.y * SURFACESCALE)}; - - const auto PCAIRO = PBUFFER->cairo; - cairo_save(PCAIRO); - cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR); - cairo_paint(PCAIRO); - cairo_restore(PCAIRO); - - // always draw a black background behind the wallpaper - cairo_set_source_rgb(PCAIRO, 0, 0, 0); - cairo_rectangle(PCAIRO, 0, 0, DIMENSIONS.x, DIMENSIONS.y); - cairo_fill(PCAIRO); - cairo_surface_flush(PBUFFER->surface); - - // get scale - // we always do cover - double scale; - Vector2D origin; - - const bool LOWASPECTRATIO = pMonitor->size.x / pMonitor->size.y > PWALLPAPERTARGET->m_vSize.x / PWALLPAPERTARGET->m_vSize.y; - if ((CONTAIN && !LOWASPECTRATIO) || (!CONTAIN && LOWASPECTRATIO)) { - scale = DIMENSIONS.x / PWALLPAPERTARGET->m_vSize.x; - origin.y = -(PWALLPAPERTARGET->m_vSize.y * scale - DIMENSIONS.y) / 2.0 / scale; - } else { - scale = DIMENSIONS.y / PWALLPAPERTARGET->m_vSize.y; - origin.x = -(PWALLPAPERTARGET->m_vSize.x * scale - DIMENSIONS.x) / 2.0 / scale; - } - - Debug::log(LOG, "Image data for %s: %s at [%.2f, %.2f], scale: %.2f (original image size: [%i, %i])", pMonitor->name.c_str(), PWALLPAPERTARGET->m_szPath.c_str(), origin.x, - origin.y, scale, (int)PWALLPAPERTARGET->m_vSize.x, (int)PWALLPAPERTARGET->m_vSize.y); - - cairo_scale(PCAIRO, scale, scale); - cairo_set_source_surface(PCAIRO, PWALLPAPERTARGET->m_pCairoSurface, origin.x, origin.y); - - cairo_paint(PCAIRO); - - if (**PRENDERSPLASH && getenv("HYPRLAND_INSTANCE_SIGNATURE")) { - auto SPLASH = execAndGet("hyprctl splash"); - SPLASH.pop_back(); - - Debug::log(LOG, "Rendering splash: %s", SPLASH.c_str()); - - cairo_select_font_face(PCAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - - const auto FONTSIZE = (int)(DIMENSIONS.y / 76.0 / scale); - cairo_set_font_size(PCAIRO, FONTSIZE); - - static auto* const PSPLASHCOLOR = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash_color")->getDataStaticPtr()); - - Debug::log(LOG, "Splash color: %x", **PSPLASHCOLOR); - - cairo_set_source_rgba(PCAIRO, ((**PSPLASHCOLOR >> 16) & 0xFF) / 255.0, ((**PSPLASHCOLOR >> 8) & 0xFF) / 255.0, (**PSPLASHCOLOR & 0xFF) / 255.0, - ((**PSPLASHCOLOR >> 24) & 0xFF) / 255.0); - - cairo_text_extents_t textExtents; - cairo_text_extents(PCAIRO, SPLASH.c_str(), &textExtents); - - cairo_move_to(PCAIRO, ((DIMENSIONS.x - textExtents.width * scale) / 2.0) / scale, ((DIMENSIONS.y * (100 - **PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale); - - Debug::log(LOG, "Splash font size: %d, pos: %.2f, %.2f", FONTSIZE, (DIMENSIONS.x - textExtents.width) / 2.0 / scale, - ((DIMENSIONS.y * (100 - **PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale); - - cairo_show_text(PCAIRO, SPLASH.c_str()); - - cairo_surface_flush(PWALLPAPERTARGET->m_pCairoSurface); - } - - cairo_restore(PCAIRO); - - if (pMonitor->pCurrentLayerSurface) { - pMonitor->pCurrentLayerSurface->pSurface->sendAttach(PBUFFER->buffer.get(), 0, 0); - pMonitor->pCurrentLayerSurface->pSurface->sendSetBufferScale(pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? 1 : pMonitor->scale); - pMonitor->pCurrentLayerSurface->pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF); - - // our wps are always opaque - auto opaqueRegion = makeShared(g_pHyprpaper->m_pCompositor->sendCreateRegion()); - opaqueRegion->sendAdd(0, 0, PBUFFER->pixelSize.x, PBUFFER->pixelSize.y); - pMonitor->pCurrentLayerSurface->pSurface->sendSetOpaqueRegion(opaqueRegion.get()); - - if (pMonitor->pCurrentLayerSurface->pFractionalScaleInfo) { - Debug::log(LOG, "Submitting viewport dest size %ix%i for %x", static_cast(std::round(pMonitor->size.x)), static_cast(std::round(pMonitor->size.y)), - pMonitor->pCurrentLayerSurface); - pMonitor->pCurrentLayerSurface->pViewport->sendSetDestination(static_cast(std::round(pMonitor->size.x)), static_cast(std::round(pMonitor->size.y))); - } - pMonitor->pCurrentLayerSurface->pSurface->sendCommit(); - } - - // check if we dont need to remove a wallpaper - if (pMonitor->layerSurfaces.size() > 1) { - for (auto it = pMonitor->layerSurfaces.begin(); it != pMonitor->layerSurfaces.end(); it++) { - if (pMonitor->pCurrentLayerSurface != it->get()) { - pMonitor->layerSurfaces.erase(it); - break; - } - } - } -} - bool CHyprpaper::lockSingleInstance() { const std::string XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR"); diff --git a/src/Hyprpaper.hpp b/src/Hyprpaper.hpp index 4ab3390..a5854d6 100644 --- a/src/Hyprpaper.hpp +++ b/src/Hyprpaper.hpp @@ -4,22 +4,31 @@ #include "defines.hpp" #include "helpers/MiscFunctions.hpp" #include "helpers/Monitor.hpp" -#include "helpers/PoolBuffer.hpp" #include "ipc/Socket.hpp" #include "render/WallpaperTarget.hpp" #include - -#include "protocols/cursor-shape-v1.hpp" -#include "protocols/fractional-scale-v1.hpp" -#include "protocols/linux-dmabuf-v1.hpp" -#include "protocols/viewporter.hpp" -#include "protocols/wayland.hpp" -#include "protocols/wlr-layer-shell-unstable-v1.hpp" +#include struct SWallpaperRenderData { bool contain = false; }; +struct SDMABUFFormat { + uint32_t format = 0; // invalid + uint64_t modifier = 0; // linear +}; + +class CCWlCompositor; +class CCWlShm; +class CCZwlrLayerShellV1; +class CCWpFractionalScaleManagerV1; +class CCWpViewporter; +class CCWlSeat; +class CCWlPointer; +class CCWpCursorShapeDeviceV1; +class CCWpCursorShapeManagerV1; +class CCZwpLinuxDmabufV1; + class CHyprpaper { public: // important @@ -33,6 +42,7 @@ class CHyprpaper { SP m_pSeatPointer; SP m_pSeatCursorShapeDevice; SP m_pCursorShape; + SP m_pLinuxDmabuf; // init the utility CHyprpaper(); @@ -43,28 +53,22 @@ class CHyprpaper { std::unordered_map m_mMonitorActiveWallpapers; std::unordered_map m_mMonitorWallpaperRenderData; std::unordered_map m_mMonitorActiveWallpaperTargets; - std::vector> m_vBuffers; std::vector> m_vMonitors; + std::vector m_vDmabufFormats; std::string m_szExplicitConfigPath; bool m_bNoFractionalScale = false; + bool m_bNoGpu = false; void removeOldHyprpaperImages(); void preloadAllWallpapersFromConfig(); void recheckAllMonitors(); void ensureMonitorHasActiveWallpaper(SMonitor*); void createLSForMonitor(SMonitor*); - void renderWallpaperForMonitor(SMonitor*); - void createBuffer(SPoolBuffer*, int32_t, int32_t, uint32_t); - void destroyBuffer(SPoolBuffer*); - int createPoolFile(size_t, std::string&); - bool setCloexec(const int&); void clearWallpaperFromMonitor(const std::string&); SMonitor* getMonitorFromName(const std::string&); bool isPreloaded(const std::string&); void recheckMonitor(SMonitor*); - void ensurePoolBuffersPresent(); - SPoolBuffer* getPoolBuffer(SMonitor*, CWallpaperTarget*); void unloadWallpaper(const std::string&); void createSeat(SP); bool lockSingleInstance(); // fails on multi-instance diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index e9ef53b..e458816 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1,6 +1,8 @@ #include "Monitor.hpp" #include "../Hyprpaper.hpp" +#include "protocols/wayland.hpp" + void SMonitor::registerListeners() { output->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { size = Vector2D(width, height); }); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 937a02a..db2577b 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -2,8 +2,8 @@ #include "../defines.hpp" #include "../render/LayerSurface.hpp" -#include "PoolBuffer.hpp" -#include "protocols/wayland.hpp" + +class CCWlOutput; struct SMonitor { std::string name = ""; @@ -19,7 +19,6 @@ struct SMonitor { bool wildcard = true; uint32_t configureSerial = 0; - SPoolBuffer buffer; bool wantsReload = false; bool wantsACK = false; diff --git a/src/helpers/PoolBuffer.hpp b/src/helpers/PoolBuffer.hpp deleted file mode 100644 index cfe1a0f..0000000 --- a/src/helpers/PoolBuffer.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "../defines.hpp" -#include "protocols/wayland.hpp" - -class CWallpaperTarget; - -struct SPoolBuffer { - SP buffer = nullptr; - cairo_surface_t* surface = nullptr; - cairo_t* cairo = nullptr; - void* data = nullptr; - size_t size = 0; - std::string name = ""; - - std::string target = ""; - Vector2D pixelSize; -}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index d833364..2d39963 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ int main(int argc, char** argv, char** envp) { // parse some args std::string configPath; - bool noFractional = false; + bool noFractional = false, noGpu = false; for (int i = 1; i < argc; ++i) { if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config")) && argc >= i + 2) { configPath = std::string(argv[++i]); @@ -15,11 +15,15 @@ int main(int argc, char** argv, char** envp) { } else if (!strcmp(argv[i], "--no-fractional") || !strcmp(argv[i], "-n")) { noFractional = true; Debug::log(LOG, "Disabling fractional scaling support!"); + } else if (!strcmp(argv[i], "--no-gpu") || !strcmp(argv[i], "-g")) { + noGpu = true; + Debug::log(LOG, "Disabling gpu acceleration"); } else { std::cout << "Hyprpaper usage: hyprpaper [arg [...]].\n\nArguments:\n" << "--help -h | Show this help message\n" << "--config -c | Specify config file to use\n" - << "--no-fractional -n | Disable fractional scaling support\n"; + << "--no-fractional -n | Disable fractional scaling support\n" + << "--no-gpu -g | Disable gpu acceleration\n"; return 1; } } @@ -28,6 +32,7 @@ int main(int argc, char** argv, char** envp) { g_pHyprpaper = std::make_unique(); g_pHyprpaper->m_szExplicitConfigPath = configPath; g_pHyprpaper->m_bNoFractionalScale = noFractional; + g_pHyprpaper->m_bNoGpu = noGpu; g_pHyprpaper->init(); return 0; diff --git a/src/render/Buffer.cpp b/src/render/Buffer.cpp new file mode 100644 index 0000000..e2ff7f7 --- /dev/null +++ b/src/render/Buffer.cpp @@ -0,0 +1,202 @@ +#include "Buffer.hpp" +#include "../Hyprpaper.hpp" +#include +#include +#include +#include "Renderer.hpp" +#include "Egl.hpp" + +#include "protocols/wayland.hpp" +#include "protocols/linux-dmabuf-v1.hpp" + +static bool setCloexec(const int& FD) { + long flags = fcntl(FD, F_GETFD); + if (flags == -1) { + return false; + } + + if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) { + return false; + } + + return true; +} + +static int createPoolFile(size_t size, std::string& name) { + const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR"); + if (!XDGRUNTIMEDIR) { + Debug::log(CRIT, "XDG_RUNTIME_DIR not set!"); + exit(1); + } + + name = std::string(XDGRUNTIMEDIR) + "/.hyprpaper_XXXXXX"; + + const auto FD = mkstemp((char*)name.c_str()); + if (FD < 0) { + Debug::log(CRIT, "createPoolFile: fd < 0"); + exit(1); + } + + if (!setCloexec(FD)) { + close(FD); + Debug::log(CRIT, "createPoolFile: !setCloexec"); + exit(1); + } + + if (ftruncate(FD, size) < 0) { + close(FD); + Debug::log(CRIT, "createPoolFile: ftruncate < 0"); + exit(1); + } + + return FD; +} + +void CBuffer::createPool() { + const size_t STRIDE = pixelSize.x * 4; + const size_t SIZE = STRIDE * pixelSize.y; + + std::string name; + const auto FD = createPoolFile(SIZE, name); + + if (FD == -1) { + Debug::log(CRIT, "Unable to create pool file!"); + exit(1); + } + + const auto DATA = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0); + auto POOL = makeShared(g_pHyprpaper->m_pSHM->sendCreatePool(FD, SIZE)); + buffer = makeShared(POOL->sendCreateBuffer(0, pixelSize.x, pixelSize.y, STRIDE, WL_SHM_FORMAT_XRGB8888)); + POOL.reset(); + + close(FD); + + cpu.data = DATA; + cpu.size = SIZE; + cpu.surface = cairo_image_surface_create_for_data((unsigned char*)DATA, CAIRO_FORMAT_ARGB32, pixelSize.x, pixelSize.y, STRIDE); + cpu.cairo = cairo_create(cpu.surface); + cpu.name = name; +} + +void CBuffer::destroyPool() { + cairo_destroy(cpu.cairo); + cairo_surface_destroy(cpu.surface); + munmap(cpu.data, cpu.size); +} + +void CBuffer::createGpu() { + uint32_t format = 0; + + std::vector modifiers = {}; + + // try to find a 10b format+mod first + for (auto& [fmt, mod] : g_pHyprpaper->m_vDmabufFormats) { + if (fmt != DRM_FORMAT_XRGB2101010 && fmt != DRM_FORMAT_XBGR2101010) + continue; + + if (mod == DRM_FORMAT_MOD_LINEAR || mod == DRM_FORMAT_MOD_INVALID) + continue; + + if (format != 0 && fmt != format) + continue; + + format = fmt; + modifiers.emplace_back(mod); + } + + if (!format) { + Debug::log(WARN, "No 10-bit DMA format found, trying 8b"); + for (auto& [fmt, mod] : g_pHyprpaper->m_vDmabufFormats) { + if (fmt != DRM_FORMAT_XRGB8888 && fmt != DRM_FORMAT_XBGR8888) + continue; + + if (mod == DRM_FORMAT_MOD_LINEAR || mod == DRM_FORMAT_MOD_INVALID) + continue; + + if (format != 0 && fmt != format) + continue; + + format = fmt; + modifiers.emplace_back(mod); + } + } + + if (!format) { + Debug::log(ERR, "Failed to find a dma format for gpu buffer"); + return; + } + + gpu.bo = gbm_bo_create_with_modifiers2(g_pRenderer->gbmDevice, pixelSize.x, pixelSize.y, format, modifiers.data(), modifiers.size(), GBM_BO_USE_RENDERING); + + if (!gpu.bo) { + Debug::log(ERR, "Failed to get a bo for gpu buffer, retrying without mods"); + gpu.bo = gbm_bo_create(g_pRenderer->gbmDevice, pixelSize.x, pixelSize.y, format, GBM_BO_USE_RENDERING); + if (!gpu.bo) { + Debug::log(ERR, "Failed to get a bo for gpu buffers"); + return; + } + } + + gpu.attrs.planes = gbm_bo_get_plane_count(gpu.bo); + gpu.attrs.modifier = gbm_bo_get_modifier(gpu.bo); + gpu.attrs.size = pixelSize; + gpu.attrs.format = format; + + for (size_t i = 0; i < (size_t)gpu.attrs.planes; ++i) { + gpu.attrs.strides.at(i) = gbm_bo_get_stride_for_plane(gpu.bo, i); + gpu.attrs.offsets.at(i) = gbm_bo_get_offset(gpu.bo, i); + gpu.attrs.fds.at(i) = gbm_bo_get_fd_for_plane(gpu.bo, i); + + if (gpu.attrs.fds.at(i) < 0) { + Debug::log(ERR, "GBM: Failed to query fd for plane %i", i); + for (size_t j = 0; j < i; ++j) { + close(gpu.attrs.fds.at(j)); + } + gpu.attrs.planes = 0; + return; + } + } + + gpu.attrs.success = true; + + gpu.eglImage = g_pEGL->getEglImage(gpu.attrs); + + if (!gpu.eglImage) { + Debug::log(ERR, "Failed to get an eglImage for gpu buffer"); + return; + } + + // send to compositor + auto PARAMS = makeShared(g_pHyprpaper->m_pLinuxDmabuf->sendCreateParams()); + + for (size_t i = 0; i < (size_t)gpu.attrs.planes; ++i) { + PARAMS->sendAdd(gpu.attrs.fds.at(i), i, gpu.attrs.offsets.at(i), gpu.attrs.strides.at(i), gpu.attrs.modifier >> 32, gpu.attrs.modifier & 0xFFFFFFFF); + } + + buffer = makeShared(PARAMS->sendCreateImmed(pixelSize.x, pixelSize.y, format, (zwpLinuxBufferParamsV1Flags)0)); +} + +void CBuffer::destroyGpu() { + g_pEGL->destroyEglImage(gpu.eglImage); + for (int i = 0; i < gpu.attrs.planes; ++i) { + close(gpu.attrs.fds.at(i)); + } + gbm_bo_destroy(gpu.bo); + gpu.bo = nullptr; +} + +CBuffer::CBuffer(const Vector2D& size) : pixelSize(size) { + if (g_pRenderer->gbmDevice) + createGpu(); + else + createPool(); +} + +CBuffer::~CBuffer() { + buffer->sendDestroy(); + + if (gpu.bo) + destroyGpu(); + else + destroyPool(); +} \ No newline at end of file diff --git a/src/render/Buffer.hpp b/src/render/Buffer.hpp new file mode 100644 index 0000000..a67562e --- /dev/null +++ b/src/render/Buffer.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "../defines.hpp" +#include +#include + +class CCWlBuffer; + +struct SDMABUFAttrs { + bool success = false; + Hyprutils::Math::Vector2D size; + uint32_t format = 0; // fourcc + uint64_t modifier = 0; + + int planes = 1; + std::array offsets = {0}; + std::array strides = {0}; + std::array fds = {-1, -1, -1, -1}; +}; + +class CBuffer { + public: + CBuffer(const Vector2D& size); + ~CBuffer(); + + struct { + cairo_surface_t* surface = nullptr; + cairo_t* cairo = nullptr; + void* data = nullptr; + size_t size = 0; + std::string name = ""; + } cpu; + + struct { + void* eglImage = nullptr; + gbm_bo* bo = nullptr; + SDMABUFAttrs attrs; + } gpu; + + SP buffer = nullptr; + + std::string target = ""; + Vector2D pixelSize; + + private: + void destroyPool(); + void createPool(); + + void createGpu(); + void destroyGpu(); +}; \ No newline at end of file diff --git a/src/render/Egl.cpp b/src/render/Egl.cpp new file mode 100644 index 0000000..2454dc2 --- /dev/null +++ b/src/render/Egl.cpp @@ -0,0 +1,162 @@ +#include "Egl.hpp" +#include "../debug/Log.hpp" +#include +#include +#include + +PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; + +const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, +}; + +const EGLint context_attribs[] = { + EGL_CONTEXT_MAJOR_VERSION, + 3, + EGL_CONTEXT_MINOR_VERSION, + 2, + EGL_NONE, +}; + +CEGL::CEGL(gbm_device* device) { + const char* _EXTS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!_EXTS) { + if (eglGetError() == EGL_BAD_DISPLAY) + throw std::runtime_error("EGL_EXT_client_extensions not supported"); + else + throw std::runtime_error("Failed to query EGL client extensions"); + } + + std::string EXTS = _EXTS; + + if (!EXTS.contains("EGL_EXT_platform_base")) + throw std::runtime_error("EGL_EXT_platform_base not supported"); + + if (!EXTS.contains("EGL_EXT_platform_wayland")) + throw std::runtime_error("EGL_EXT_platform_wayland not supported"); + + eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (eglGetPlatformDisplayEXT == NULL) + throw std::runtime_error("Failed to get eglGetPlatformDisplayEXT"); + + eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); + if (eglCreatePlatformWindowSurfaceEXT == NULL) + throw std::runtime_error("Failed to get eglCreatePlatformWindowSurfaceEXT"); + + eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, device, NULL); + EGLint matched = 0; + if (eglDisplay == EGL_NO_DISPLAY) { + Debug::log(CRIT, "Failed to create EGL display"); + goto error; + } + + if (eglInitialize(eglDisplay, NULL, NULL) == EGL_FALSE) { + Debug::log(CRIT, "Failed to initialize EGL"); + goto error; + } + + if (!eglChooseConfig(eglDisplay, config_attribs, &eglConfig, 1, &matched)) { + Debug::log(CRIT, "eglChooseConfig failed"); + goto error; + } + if (matched == 0) { + Debug::log(CRIT, "Failed to match an EGL config"); + goto error; + } + + eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, context_attribs); + if (eglContext == EGL_NO_CONTEXT) { + Debug::log(CRIT, "Failed to create EGL context"); + goto error; + } + + eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); + if (eglDestroyImageKHR == NULL) + throw std::runtime_error("Failed to get eglDestroyImageKHR"); + + eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); + if (eglCreateImageKHR == NULL) + throw std::runtime_error("Failed to get eglCreateImageKHR"); + + glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); + if (glEGLImageTargetTexture2DOES == NULL) + throw std::runtime_error("Failed to get glEGLImageTargetTexture2DOES"); + + glEGLImageTargetRenderbufferStorageOES = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES"); + if (glEGLImageTargetRenderbufferStorageOES == NULL) + throw std::runtime_error("Failed to get glEGLImageTargetRenderbufferStorageOES"); + + return; + +error: + eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +CEGL::~CEGL() { + if (eglContext != EGL_NO_CONTEXT) + eglDestroyContext(eglDisplay, eglContext); + + if (eglDisplay) + eglTerminate(eglDisplay); + + eglReleaseThread(); +} + +void CEGL::makeCurrent(EGLSurface surf) { + eglMakeCurrent(eglDisplay, surf, surf, eglContext); +} + +void* CEGL::getEglImage(const SDMABUFAttrs& attrs) { + std::vector attribs; + + attribs.push_back(EGL_WIDTH); + attribs.push_back(attrs.size.x); + attribs.push_back(EGL_HEIGHT); + attribs.push_back(attrs.size.y); + attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT); + attribs.push_back(attrs.format); + + struct { + EGLint fd; + EGLint offset; + EGLint pitch; + EGLint modlo; + EGLint modhi; + } attrNames[4] = { + {EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}}; + + for (int i = 0; i < attrs.planes; i++) { + attribs.push_back(attrNames[i].fd); + attribs.push_back(attrs.fds[i]); + attribs.push_back(attrNames[i].offset); + attribs.push_back(attrs.offsets[i]); + attribs.push_back(attrNames[i].pitch); + attribs.push_back(attrs.strides[i]); + if (attrs.modifier != DRM_FORMAT_MOD_INVALID) { + attribs.push_back(attrNames[i].modlo); + attribs.push_back(attrs.modifier & 0xFFFFFFFF); + attribs.push_back(attrNames[i].modhi); + attribs.push_back(attrs.modifier >> 32); + } + } + + attribs.push_back(EGL_IMAGE_PRESERVED_KHR); + attribs.push_back(EGL_TRUE); + + attribs.push_back(EGL_NONE); + + EGLImageKHR image = eglCreateImageKHR(eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data()); + if (image == EGL_NO_IMAGE_KHR) { + Debug::log(ERR, "EGL: EGLCreateImageKHR failed with 0x%lx", eglGetError()); + return EGL_NO_IMAGE_KHR; + } + + return image; +} + +void CEGL::destroyEglImage(EGLImageKHR image) { + eglDestroyImageKHR(eglDisplay, image); +} diff --git a/src/render/Egl.hpp b/src/render/Egl.hpp new file mode 100644 index 0000000..f543ea6 --- /dev/null +++ b/src/render/Egl.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "Buffer.hpp" + +#include +typedef void* EGLImageKHR; +typedef void* GLeglImageOES; +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay dpy, EGLConfig config, void* native_window, const EGLint* attrib_list); +typedef EGLImageKHR(EGLAPIENTRYP PFNEGLCREATEIMAGEKHRPROC)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list); +typedef EGLBoolean(EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC)(EGLDisplay dpy, EGLImageKHR image); +typedef void(GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum target, GLeglImageOES image); +typedef void(GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)(GLenum target, GLeglImageOES image); + +struct gbm_device; + +class CEGL { + public: + CEGL(gbm_device*); + ~CEGL(); + + EGLDisplay eglDisplay = nullptr; + EGLConfig eglConfig = nullptr; + EGLContext eglContext = nullptr; + + PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = nullptr; + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; + PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr; + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; + + void makeCurrent(EGLSurface surf); + EGLImageKHR getEglImage(const SDMABUFAttrs& attrs); + void destroyEglImage(EGLImageKHR image); +}; + +inline std::unique_ptr g_pEGL; \ No newline at end of file diff --git a/src/render/LayerSurface.cpp b/src/render/LayerSurface.cpp index 9122e1a..f05e9cf 100644 --- a/src/render/LayerSurface.cpp +++ b/src/render/LayerSurface.cpp @@ -2,6 +2,11 @@ #include "../Hyprpaper.hpp" +#include "protocols/wlr-layer-shell-unstable-v1.hpp" +#include "protocols/wayland.hpp" +#include "protocols/fractional-scale-v1.hpp" +#include "protocols/viewporter.hpp" + CLayerSurface::CLayerSurface(SMonitor* pMonitor) { m_pMonitor = pMonitor; diff --git a/src/render/LayerSurface.hpp b/src/render/LayerSurface.hpp index 73127fb..283511d 100644 --- a/src/render/LayerSurface.hpp +++ b/src/render/LayerSurface.hpp @@ -1,10 +1,11 @@ #pragma once #include "../defines.hpp" -#include "protocols/fractional-scale-v1.hpp" -#include "protocols/viewporter.hpp" -#include "protocols/wayland.hpp" -#include "protocols/wlr-layer-shell-unstable-v1.hpp" + +class CCZwlrLayerSurfaceV1; +class CCWlSurface; +class CCWpFractionalScaleV1; +class CCWpViewport; struct SMonitor; diff --git a/src/render/Math.cpp b/src/render/Math.cpp new file mode 100644 index 0000000..04e4944 --- /dev/null +++ b/src/render/Math.cpp @@ -0,0 +1,207 @@ +#include "Math.hpp" +#include +#include +#include + +void matrixIdentity(float mat[9]) { + static const float identity[9] = { + 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + }; + memcpy(mat, identity, sizeof(identity)); +} + +void matrixMultiply(float mat[9], const float a[9], const float b[9]) { + float product[9]; + + product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6]; + product[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7]; + product[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8]; + + product[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6]; + product[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7]; + product[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8]; + + product[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6]; + product[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7]; + product[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8]; + + memcpy(mat, product, sizeof(product)); +} + +void matrixTranspose(float mat[9], const float a[9]) { + float transposition[9] = { + a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8], + }; + memcpy(mat, transposition, sizeof(transposition)); +} + +void matrixTranslate(float mat[9], float x, float y) { + float translate[9] = { + 1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f, + }; + matrixMultiply(mat, mat, translate); +} + +void matrixScale(float mat[9], float x, float y) { + float scale[9] = { + x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f, + }; + matrixMultiply(mat, mat, scale); +} + +void matrixRotate(float mat[9], float rad) { + float rotate[9] = { + (float)cos(rad), (float)-sin(rad), 0.0f, (float)sin(rad), (float)cos(rad), 0.0f, 0.0f, 0.0f, 1.0f, + }; + matrixMultiply(mat, mat, rotate); +} + +std::unordered_map> transforms = { + {HYPRUTILS_TRANSFORM_NORMAL, + { + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_90, + { + 0.0f, + 1.0f, + 0.0f, + -1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_180, + { + -1.0f, + 0.0f, + 0.0f, + 0.0f, + -1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_270, + { + 0.0f, + -1.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_FLIPPED, + { + -1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_FLIPPED_90, + { + 0.0f, + 1.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_FLIPPED_180, + { + 1.0f, + 0.0f, + 0.0f, + 0.0f, + -1.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, + {HYPRUTILS_TRANSFORM_FLIPPED_270, + { + 0.0f, + -1.0f, + 0.0f, + -1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + }}, +}; + +void matrixTransform(float mat[9], eTransform transform) { + matrixMultiply(mat, mat, transforms.at(transform).data()); +} + +void matrixProjection(float mat[9], int width, int height, eTransform transform) { + memset(mat, 0, sizeof(*mat) * 9); + + const float* t = transforms.at(transform).data(); + float x = 2.0f / width; + float y = 2.0f / height; + + // Rotation + reflection + mat[0] = x * t[0]; + mat[1] = x * t[1]; + mat[3] = y * -t[3]; + mat[4] = y * -t[4]; + + // Translation + mat[2] = -copysign(1.0f, mat[0] + mat[1]); + mat[5] = -copysign(1.0f, mat[3] + mat[4]); + + // Identity + mat[8] = 1.0f; +} + +void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]) { + double x = box.x; + double y = box.y; + double width = box.width; + double height = box.height; + + matrixIdentity(mat); + matrixTranslate(mat, x, y); + + if (rotation != 0) { + matrixTranslate(mat, width / 2, height / 2); + matrixRotate(mat, rotation); + matrixTranslate(mat, -width / 2, -height / 2); + } + + matrixScale(mat, width, height); + + if (transform != HYPRUTILS_TRANSFORM_NORMAL) { + matrixTranslate(mat, 0.5, 0.5); + matrixTransform(mat, transform); + matrixTranslate(mat, -0.5, -0.5); + } + + matrixMultiply(mat, projection, mat); +} diff --git a/src/render/Math.hpp b/src/render/Math.hpp new file mode 100644 index 0000000..999150a --- /dev/null +++ b/src/render/Math.hpp @@ -0,0 +1,18 @@ +#pragma once + +// FIXME: migrate this to utils + +// includes box and vector as well +#include + +using namespace Hyprutils::Math; + +void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]); +void matrixProjection(float mat[9], int width, int height, eTransform transform); +void matrixTransform(float mat[9], eTransform transform); +void matrixRotate(float mat[9], float rad); +void matrixScale(float mat[9], float x, float y); +void matrixTranslate(float mat[9], float x, float y); +void matrixTranspose(float mat[9], const float a[9]); +void matrixMultiply(float mat[9], const float a[9], const float b[9]); +void matrixIdentity(float mat[9]); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp new file mode 100644 index 0000000..b6458e6 --- /dev/null +++ b/src/render/Renderer.cpp @@ -0,0 +1,467 @@ +#include "Renderer.hpp" +#include "protocols/linux-dmabuf-v1.hpp" +#include "protocols/wlr-layer-shell-unstable-v1.hpp" +#include "protocols/wayland.hpp" +#include "protocols/viewporter.hpp" +#include "../Hyprpaper.hpp" +#include +#include "Buffer.hpp" +#include "LayerSurface.hpp" +#include "Egl.hpp" +#include +#include +#include +#include +#include +#include +#include "Math.hpp" +using namespace Hyprutils::Math; + +// ------------------- shader utils + +GLuint compileShader(const GLuint& type, std::string src) { + auto shader = glCreateShader(type); + + auto shaderSource = src.c_str(); + + glShaderSource(shader, 1, (const GLchar**)&shaderSource, nullptr); + glCompileShader(shader); + + GLint ok; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + + if (ok == GL_FALSE) + return 0; + + return shader; +} + +GLuint createProgram(const std::string& vert, const std::string& frag) { + auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert); + if (vertCompiled == 0) + return 0; + + auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag); + if (fragCompiled == 0) + return 0; + + auto prog = glCreateProgram(); + glAttachShader(prog, vertCompiled); + glAttachShader(prog, fragCompiled); + glLinkProgram(prog); + + glDetachShader(prog, vertCompiled); + glDetachShader(prog, fragCompiled); + glDeleteShader(vertCompiled); + glDeleteShader(fragCompiled); + + GLint ok; + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (ok == GL_FALSE) + return 0; + + return prog; +} + +inline const std::string VERT_SRC = R"#( +uniform mat3 proj; +attribute vec2 pos; +attribute vec2 texcoord; +varying vec2 v_texcoord; + +void main() { + gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); + v_texcoord = texcoord; +})#"; + +inline const std::string FRAG_SRC = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +void main() { + gl_FragColor = texture2D(tex, v_texcoord); +})#"; + +inline const std::string FRAG_SRC_EXT = R"#( +#extension GL_OES_EGL_image_external : require +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform samplerExternalOES texture0; + +void main() { + gl_FragColor = texture2D(texture0, v_texcoord); +})#"; + +// ------------------- + +CRenderer::CRenderer(bool gpu) { + if (gpu) { + dmabufFeedback = makeShared(g_pHyprpaper->m_pLinuxDmabuf->sendGetDefaultFeedback()); + + dmabufFeedback->setMainDevice([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* deviceArr) { + dev_t device; + if (deviceArr->size != sizeof(device)) + abort(); + memcpy(&device, deviceArr->data, sizeof(device)); + + drmDevice* drmDev; + if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) { + Debug::log(ERR, "zwp_linux_dmabuf_v1: drmGetDeviceFromDevId failed"); + return; + } + + const char* name = nullptr; + if (drmDev->available_nodes & (1 << DRM_NODE_RENDER)) + name = drmDev->nodes[DRM_NODE_RENDER]; + else { + // Likely a split display/render setup. Pick the primary node and hope + // Mesa will open the right render node under-the-hood. + if (!(drmDev->available_nodes & (1 << DRM_NODE_PRIMARY))) + abort(); + name = drmDev->nodes[DRM_NODE_PRIMARY]; + Debug::log(WARN, "zwp_linux_dmabuf_v1: DRM device has no render node, using primary"); + } + + if (!name) { + Debug::log(ERR, "zwp_linux_dmabuf_v1: no node name"); + return; + } + + nodeName = name; + + drmFreeDevice(&drmDev); + + Debug::log(LOG, "zwp_linux_dmabuf_v1: got node %s", nodeName.c_str()); + }); + + wl_display_roundtrip(g_pHyprpaper->m_sDisplay); + + if (!nodeName.empty()) { + nodeFD = open(nodeName.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (nodeFD < 0) { + Debug::log(ERR, "zwp_linux_dmabuf_v1: failed to open node"); + return; + } + + Debug::log(LOG, "zwp_linux_dmabuf_v1: opened node %s with fd %i", nodeName.c_str(), nodeFD); + + gbmDevice = gbm_create_device(nodeFD); + } + + g_pEGL = std::make_unique(gbmDevice); + g_pEGL->makeCurrent(EGL_NO_SURFACE); + + gl.shader.program = createProgram(VERT_SRC, FRAG_SRC); + if (gl.shader.program == 0) { + Debug::log(ERR, "renderer: failed to link shader"); + return; + } + + gl.shader.proj = glGetUniformLocation(gl.shader.program, "proj"); + gl.shader.posAttrib = glGetAttribLocation(gl.shader.program, "pos"); + gl.shader.texAttrib = glGetAttribLocation(gl.shader.program, "texcoord"); + gl.shader.tex = glGetUniformLocation(gl.shader.program, "tex"); + } +} + +void CRenderer::renderWallpaperForMonitor(SMonitor* pMonitor) { + + const auto PBUFFER = getOrCreateBuffer(pMonitor); + + if (!g_pHyprpaper->m_mMonitorActiveWallpaperTargets[pMonitor]) + g_pHyprpaper->recheckMonitor(pMonitor); + + if (gbmDevice) + renderGpu(pMonitor, PBUFFER); + else + renderCpu(pMonitor, PBUFFER); + + if (pMonitor->pCurrentLayerSurface) { + pMonitor->pCurrentLayerSurface->pSurface->sendAttach(PBUFFER->buffer.get(), 0, 0); + pMonitor->pCurrentLayerSurface->pSurface->sendSetBufferScale(pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? 1 : pMonitor->scale); + pMonitor->pCurrentLayerSurface->pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF); + + // our wps are always opaque + auto opaqueRegion = makeShared(g_pHyprpaper->m_pCompositor->sendCreateRegion()); + opaqueRegion->sendAdd(0, 0, PBUFFER->pixelSize.x, PBUFFER->pixelSize.y); + pMonitor->pCurrentLayerSurface->pSurface->sendSetOpaqueRegion(opaqueRegion.get()); + + if (pMonitor->pCurrentLayerSurface->pFractionalScaleInfo) { + Debug::log(LOG, "Submitting viewport dest size %ix%i for %x", static_cast(std::round(pMonitor->size.x)), static_cast(std::round(pMonitor->size.y)), + pMonitor->pCurrentLayerSurface); + pMonitor->pCurrentLayerSurface->pViewport->sendSetDestination(static_cast(std::round(pMonitor->size.x)), static_cast(std::round(pMonitor->size.y))); + } + pMonitor->pCurrentLayerSurface->pSurface->sendCommit(); + } + + // check if we dont need to remove a wallpaper + if (pMonitor->layerSurfaces.size() > 1) { + for (auto it = pMonitor->layerSurfaces.begin(); it != pMonitor->layerSurfaces.end(); it++) { + if (pMonitor->pCurrentLayerSurface != it->get()) { + pMonitor->layerSurfaces.erase(it); + break; + } + } + } +} + +SP CRenderer::getOrCreateBuffer(SMonitor* pMonitor) { + Vector2D size = pMonitor->size; + + if (pMonitor->pCurrentLayerSurface) + size = size * pMonitor->pCurrentLayerSurface->fScale; + + if (monitorBuffers.contains(pMonitor) && monitorBuffers.at(pMonitor)->pixelSize == size) + return monitorBuffers.at(pMonitor); + + auto buf = makeShared(size); + + monitorBuffers[pMonitor] = buf; + + return buf; +} + +void CRenderer::renderCpu(SMonitor* pMonitor, SP buf) { + renderWallpaper(pMonitor, buf->cpu.surface, buf->cpu.cairo); +} + +SGLTex CRenderer::glTex(void* cairoData, const Vector2D& size) { + SGLTex tex; + + glGenTextures(1, &tex.texid); + + glBindTexture(GL_TEXTURE_2D, tex.texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, (size.x * 4) / 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, cairoData); + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + return tex; +} + +inline const float fullVerts[] = { + 1, 0, // top right + 0, 0, // top left + 1, 1, // bottom right + 0, 1, // bottom left +}; + +void CRenderer::renderGpu(SMonitor* pMonitor, SP buf) { + // TODO: get the image in 10b + // TODO: actually render with the gpu + + auto pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, buf->pixelSize.x, buf->pixelSize.y); + auto pCairo = cairo_create(pCairoSurface); + + renderWallpaper(pMonitor, pCairoSurface, pCairo, true); + + cairo_destroy(pCairo); + + g_pEGL->makeCurrent(EGL_NO_SURFACE); + + SGLTex fromTex = glTex(cairo_image_surface_get_data(pCairoSurface), buf->pixelSize); + + GLuint rboID = 0, fboID = 0; + + glGenRenderbuffers(1, &rboID); + glBindRenderbuffer(GL_RENDERBUFFER, rboID); + g_pEGL->glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)buf->gpu.eglImage); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &fboID); + glBindFramebuffer(GL_FRAMEBUFFER, fboID); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Debug::log(ERR, "EGL: failed to create a rbo"); + return; + } + + glClearColor(0.77F, 0.F, 0.74F, 1.F); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + // done, let's render the texture to the rbo + // first, render the bg + double scale = 1.0; + Vector2D origin; + + const auto PWALLPAPERTARGET = g_pHyprpaper->m_mMonitorActiveWallpaperTargets[pMonitor]; + const auto CONTAIN = g_pHyprpaper->m_mMonitorWallpaperRenderData[pMonitor->name].contain; + const double SURFACESCALE = pMonitor->pCurrentLayerSurface && pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? pMonitor->pCurrentLayerSurface->fScale : pMonitor->scale; + const Vector2D DIMENSIONS = Vector2D{std::round(pMonitor->size.x * SURFACESCALE), std::round(pMonitor->size.y * SURFACESCALE)}; + + const bool LOWASPECTRATIO = pMonitor->size.x / pMonitor->size.y > PWALLPAPERTARGET->m_vSize.x / PWALLPAPERTARGET->m_vSize.y; + if ((CONTAIN && !LOWASPECTRATIO) || (!CONTAIN && LOWASPECTRATIO)) { + scale = DIMENSIONS.x / PWALLPAPERTARGET->m_vSize.x; + origin.y = -(PWALLPAPERTARGET->m_vSize.y * scale - DIMENSIONS.y) / 2.0 / scale; + } else { + scale = DIMENSIONS.y / PWALLPAPERTARGET->m_vSize.y; + origin.x = -(PWALLPAPERTARGET->m_vSize.x * scale - DIMENSIONS.x) / 2.0 / scale; + } + + renderTexture(PWALLPAPERTARGET->gpu.textureID, CBox{origin, PWALLPAPERTARGET->m_vSize * scale}, buf->pixelSize); + + // then, any decoration we got from cairo + renderTexture(fromTex.texid, {{}, buf->pixelSize}, buf->pixelSize); + + // rendered, cleanup + + glFlush(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glDeleteFramebuffers(1, &fboID); + glDeleteRenderbuffers(1, &rboID); + glDeleteTextures(1, &fromTex.texid); + cairo_surface_destroy(pCairoSurface); +} + +void CRenderer::renderTexture(GLuint texid, const CBox& box, const Vector2D& viewport) { + CBox renderBox = {{}, viewport}; + + float mtx[9]; + float base[9]; + float monitorProj[9]; + matrixIdentity(base); + + auto& SHADER = gl.shader; + + // KMS uses flipped y, we have to do FLIPPED_180 + matrixTranslate(base, viewport.x / 2.0, viewport.y / 2.0); + matrixTransform(base, HYPRUTILS_TRANSFORM_FLIPPED_180); + matrixTranslate(base, -viewport.x / 2.0, -viewport.y / 2.0); + + projectBox(mtx, renderBox, HYPRUTILS_TRANSFORM_FLIPPED_180, 0, base); + + matrixProjection(monitorProj, viewport.x, viewport.y, HYPRUTILS_TRANSFORM_FLIPPED_180); + + float glMtx[9]; + matrixMultiply(glMtx, monitorProj, mtx); + + glViewport(0, 0, viewport.x, viewport.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texid); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glUseProgram(SHADER.program); + + glDisable(GL_SCISSOR_TEST); + + matrixTranspose(glMtx, glMtx); + glUniformMatrix3fv(SHADER.proj, 1, GL_FALSE, glMtx); + + glUniform1i(SHADER.tex, 0); + + glVertexAttribPointer(SHADER.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(SHADER.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(SHADER.posAttrib); + glEnableVertexAttribArray(SHADER.texAttrib); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(SHADER.posAttrib); + glDisableVertexAttribArray(SHADER.texAttrib); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void CRenderer::renderWallpaper(SMonitor* pMonitor, cairo_surface_t* pCairoSurface, cairo_t* pCairo, bool splashOnly) { + static auto* const PRENDERSPLASH = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash")->getDataStaticPtr()); + static auto* const PSPLASHOFFSET = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash_offset")->getDataStaticPtr()); + + const auto PWALLPAPERTARGET = g_pHyprpaper->m_mMonitorActiveWallpaperTargets[pMonitor]; + const auto CONTAIN = g_pHyprpaper->m_mMonitorWallpaperRenderData[pMonitor->name].contain; + + if (!PWALLPAPERTARGET) { + Debug::log(CRIT, "wallpaper target null in render??"); + exit(1); + } + + const double SURFACESCALE = pMonitor->pCurrentLayerSurface && pMonitor->pCurrentLayerSurface->pFractionalScaleInfo ? pMonitor->pCurrentLayerSurface->fScale : pMonitor->scale; + const Vector2D DIMENSIONS = Vector2D{std::round(pMonitor->size.x * SURFACESCALE), std::round(pMonitor->size.y * SURFACESCALE)}; + + cairo_save(pCairo); + cairo_set_source_rgba(pCairo, 0, 0, 0, 0); + cairo_set_operator(pCairo, CAIRO_OPERATOR_SOURCE); + cairo_paint(pCairo); + cairo_restore(pCairo); + + // always draw a black background behind the wallpaper + if (!splashOnly) { + cairo_set_source_rgb(pCairo, 0, 0, 0); + cairo_rectangle(pCairo, 0, 0, DIMENSIONS.x, DIMENSIONS.y); + cairo_fill(pCairo); + cairo_surface_flush(pCairoSurface); + } + + // get scale + double scale; + Vector2D origin; + + const bool LOWASPECTRATIO = pMonitor->size.x / pMonitor->size.y > PWALLPAPERTARGET->m_vSize.x / PWALLPAPERTARGET->m_vSize.y; + if ((CONTAIN && !LOWASPECTRATIO) || (!CONTAIN && LOWASPECTRATIO)) { + scale = DIMENSIONS.x / PWALLPAPERTARGET->m_vSize.x; + origin.y = -(PWALLPAPERTARGET->m_vSize.y * scale - DIMENSIONS.y) / 2.0 / scale; + } else { + scale = DIMENSIONS.y / PWALLPAPERTARGET->m_vSize.y; + origin.x = -(PWALLPAPERTARGET->m_vSize.x * scale - DIMENSIONS.x) / 2.0 / scale; + } + + cairo_scale(pCairo, scale, scale); + + if (!splashOnly) { + Debug::log(LOG, "Image data for %s: %s at [%.2f, %.2f], scale: %.2f (original image size: [%i, %i])", pMonitor->name.c_str(), PWALLPAPERTARGET->m_szPath.c_str(), origin.x, + origin.y, scale, (int)PWALLPAPERTARGET->m_vSize.x, (int)PWALLPAPERTARGET->m_vSize.y); + + cairo_set_source_surface(pCairo, PWALLPAPERTARGET->cpu.cairoSurface, origin.x, origin.y); + + cairo_paint(pCairo); + } + + if (**PRENDERSPLASH && getenv("HYPRLAND_INSTANCE_SIGNATURE")) { + auto SPLASH = execAndGet("hyprctl splash"); + SPLASH.pop_back(); + + Debug::log(LOG, "Rendering splash: %s", SPLASH.c_str()); + + cairo_select_font_face(pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + const auto FONTSIZE = (int)(DIMENSIONS.y / 76.0 / scale); + cairo_set_font_size(pCairo, FONTSIZE); + + static auto* const PSPLASHCOLOR = reinterpret_cast(g_pConfigManager->config->getConfigValuePtr("splash_color")->getDataStaticPtr()); + + Debug::log(LOG, "Splash color: %x", **PSPLASHCOLOR); + + cairo_set_source_rgba(pCairo, ((**PSPLASHCOLOR >> 16) & 0xFF) / 255.0, ((**PSPLASHCOLOR >> 8) & 0xFF) / 255.0, (**PSPLASHCOLOR & 0xFF) / 255.0, + ((**PSPLASHCOLOR >> 24) & 0xFF) / 255.0); + + cairo_text_extents_t textExtents; + cairo_text_extents(pCairo, SPLASH.c_str(), &textExtents); + + cairo_move_to(pCairo, ((DIMENSIONS.x - textExtents.width * scale) / 2.0) / scale, ((DIMENSIONS.y * (100 - **PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale); + + Debug::log(LOG, "Splash font size: %d, pos: %.2f, %.2f", FONTSIZE, (DIMENSIONS.x - textExtents.width) / 2.0 / scale, + ((DIMENSIONS.y * (100 - **PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale); + + cairo_show_text(pCairo, SPLASH.c_str()); + } + + cairo_restore(pCairo); + cairo_surface_flush(pCairoSurface); +} \ No newline at end of file diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp new file mode 100644 index 0000000..1606fe5 --- /dev/null +++ b/src/render/Renderer.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "../defines.hpp" +#include +#include +#include +using namespace Hyprutils::Math; + +struct SMonitor; +class CBuffer; +class CCZwpLinuxDmabufFeedbackV1; + +struct SGLTex { + void* image = nullptr; + GLuint texid = 0; + GLuint target = GL_TEXTURE_2D; +}; + +class CRenderer { + public: + CRenderer(bool gpu); + + gbm_device* gbmDevice = nullptr; + std::string nodeName = ""; + int nodeFD = -1; + + void renderWallpaperForMonitor(SMonitor* pMonitor); + SGLTex glTex(void* cairoData, const Vector2D& size); + + // no need for double-buffering as we don't update the images + std::unordered_map> monitorBuffers; + + private: + SP dmabufFeedback; + + void renderCpu(SMonitor* pMonitor, SP buf); + void renderGpu(SMonitor* pMonitor, SP buf); + + void renderWallpaper(SMonitor* pMonitor, cairo_surface_t* pCairoSurface, cairo_t* pCairo, bool splashOnly = false); + void renderTexture(GLuint texid, const CBox& box, const Vector2D& viewport); + + SP getOrCreateBuffer(SMonitor* pMonitor); + + struct { + struct SShader { + GLuint program = 0; + GLint proj = -1, tex = -1, posAttrib = -1, texAttrib = -1; + } shader; + } gl; +}; + +inline std::unique_ptr g_pRenderer; diff --git a/src/render/WallpaperTarget.cpp b/src/render/WallpaperTarget.cpp index 6c2ab3b..cdc0764 100644 --- a/src/render/WallpaperTarget.cpp +++ b/src/render/WallpaperTarget.cpp @@ -2,9 +2,14 @@ #include #include +#include "Egl.hpp" +#include "Renderer.hpp" CWallpaperTarget::~CWallpaperTarget() { - cairo_surface_destroy(m_pCairoSurface); + if (cpu.cairoSurface) + cairo_surface_destroy(cpu.cairoSurface); + if (gpu.textureID) + glDeleteTextures(1, &gpu.textureID); } void CWallpaperTarget::create(const std::string& path) { @@ -57,5 +62,16 @@ void CWallpaperTarget::create(const std::string& path) { Debug::log(LOG, "Preloaded target %s in %.2fms -> Pixel size: [%i, %i]", path.c_str(), MS, (int)m_vSize.x, (int)m_vSize.y); - m_pCairoSurface = CAIROSURFACE; + if (!g_pEGL) { + cpu.cairoSurface = CAIROSURFACE; + return; + } + + Debug::log(LOG, "GPU mode, uploading the preloaded image into VRAM and deleting from RAM"); + + auto tex = g_pRenderer->glTex(cairo_image_surface_get_data(CAIROSURFACE), m_vSize); + + gpu.textureID = tex.texid; + + cairo_surface_destroy(CAIROSURFACE); } diff --git a/src/render/WallpaperTarget.hpp b/src/render/WallpaperTarget.hpp index 9a28d92..ba4082e 100644 --- a/src/render/WallpaperTarget.hpp +++ b/src/render/WallpaperTarget.hpp @@ -9,13 +9,19 @@ class CWallpaperTarget { public: ~CWallpaperTarget(); - void create(const std::string& path); + void create(const std::string& path); - std::string m_szPath; + std::string m_szPath; - Vector2D m_vSize; + Vector2D m_vSize; - bool m_bHasAlpha = true; + bool m_bHasAlpha = true; - cairo_surface_t* m_pCairoSurface; + struct { + cairo_surface_t* cairoSurface = nullptr; + } cpu; + + struct { + uint32_t textureID = 0; + } gpu; };