mirror of
https://github.com/hyprwm/hyprpaper.git
synced 2024-12-22 20:29:48 +01:00
basic gpu stuff
This commit is contained in:
parent
87791c0a99
commit
c788a59dac
20 changed files with 1312 additions and 332 deletions
|
@ -50,6 +50,9 @@ pkg_check_modules(
|
|||
deps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET
|
||||
gbm
|
||||
egl
|
||||
libdrm
|
||||
wayland-client
|
||||
wayland-protocols
|
||||
cairo
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
#include "Hyprpaper.hpp"
|
||||
#include "render/Renderer.hpp"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#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<CCWpCursorShapeManagerV1>((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<CCZwpLinuxDmabufV1>((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<CCZwpLinuxDmabufV1>((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<CRenderer>(!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<CRenderer>(!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<SPoolBuffer>& 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<SPoolBuffer>()).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<CLayerSurface>(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<CCWlShmPool>(g_pHyprpaper->m_pSHM->sendCreatePool(FD, SIZE));
|
||||
pBuffer->buffer = makeShared<CCWlBuffer>(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<SPoolBuffer>& 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<Hyprlang::INT* const*>(g_pConfigManager->config->getConfigValuePtr("splash")->getDataStaticPtr());
|
||||
static auto* const PSPLASHOFFSET = reinterpret_cast<Hyprlang::FLOAT* const*>(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<Hyprlang::INT* const*>(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<CCWlRegion>(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<int>(std::round(pMonitor->size.x)), static_cast<int>(std::round(pMonitor->size.y)),
|
||||
pMonitor->pCurrentLayerSurface);
|
||||
pMonitor->pCurrentLayerSurface->pViewport->sendSetDestination(static_cast<int>(std::round(pMonitor->size.x)), static_cast<int>(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");
|
||||
|
||||
|
|
|
@ -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 <mutex>
|
||||
|
||||
#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 <unordered_map>
|
||||
|
||||
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<CCWlPointer> m_pSeatPointer;
|
||||
SP<CCWpCursorShapeDeviceV1> m_pSeatCursorShapeDevice;
|
||||
SP<CCWpCursorShapeManagerV1> m_pCursorShape;
|
||||
SP<CCZwpLinuxDmabufV1> m_pLinuxDmabuf;
|
||||
|
||||
// init the utility
|
||||
CHyprpaper();
|
||||
|
@ -43,28 +53,22 @@ class CHyprpaper {
|
|||
std::unordered_map<std::string, std::string> m_mMonitorActiveWallpapers;
|
||||
std::unordered_map<std::string, SWallpaperRenderData> m_mMonitorWallpaperRenderData;
|
||||
std::unordered_map<SMonitor*, CWallpaperTarget*> m_mMonitorActiveWallpaperTargets;
|
||||
std::vector<std::unique_ptr<SPoolBuffer>> m_vBuffers;
|
||||
std::vector<std::unique_ptr<SMonitor>> m_vMonitors;
|
||||
std::vector<SDMABUFFormat> 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<CCWlSeat>);
|
||||
bool lockSingleInstance(); // fails on multi-instance
|
||||
|
|
|
@ -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); });
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "protocols/wayland.hpp"
|
||||
|
||||
class CWallpaperTarget;
|
||||
|
||||
struct SPoolBuffer {
|
||||
SP<CCWlBuffer> 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;
|
||||
};
|
|
@ -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<CHyprpaper>();
|
||||
g_pHyprpaper->m_szExplicitConfigPath = configPath;
|
||||
g_pHyprpaper->m_bNoFractionalScale = noFractional;
|
||||
g_pHyprpaper->m_bNoGpu = noGpu;
|
||||
g_pHyprpaper->init();
|
||||
|
||||
return 0;
|
||||
|
|
202
src/render/Buffer.cpp
Normal file
202
src/render/Buffer.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
#include "Buffer.hpp"
|
||||
#include "../Hyprpaper.hpp"
|
||||
#include <gbm.h>
|
||||
#include <xf86drm.h>
|
||||
#include <drm_fourcc.h>
|
||||
#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<CCWlShmPool>(g_pHyprpaper->m_pSHM->sendCreatePool(FD, SIZE));
|
||||
buffer = makeShared<CCWlBuffer>(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<uint64_t> 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<CCZwpLinuxBufferParamsV1>(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<CCWlBuffer>(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();
|
||||
}
|
51
src/render/Buffer.hpp
Normal file
51
src/render/Buffer.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include <EGL/egl.h>
|
||||
#include <gbm.h>
|
||||
|
||||
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<uint32_t, 4> offsets = {0};
|
||||
std::array<uint32_t, 4> strides = {0};
|
||||
std::array<int, 4> 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<CCWlBuffer> buffer = nullptr;
|
||||
|
||||
std::string target = "";
|
||||
Vector2D pixelSize;
|
||||
|
||||
private:
|
||||
void destroyPool();
|
||||
void createPool();
|
||||
|
||||
void createGpu();
|
||||
void destroyGpu();
|
||||
};
|
162
src/render/Egl.cpp
Normal file
162
src/render/Egl.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
#include "Egl.hpp"
|
||||
#include "../debug/Log.hpp"
|
||||
#include <EGL/eglext.h>
|
||||
#include <xf86drm.h>
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
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<uint32_t> 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);
|
||||
}
|
38
src/render/Egl.hpp
Normal file
38
src/render/Egl.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Buffer.hpp"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
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<CEGL> g_pEGL;
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
207
src/render/Math.cpp
Normal file
207
src/render/Math.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#include "Math.hpp"
|
||||
#include <unordered_map>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
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<eTransform, std::array<float, 9>> 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);
|
||||
}
|
18
src/render/Math.hpp
Normal file
18
src/render/Math.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
// FIXME: migrate this to utils
|
||||
|
||||
// includes box and vector as well
|
||||
#include <hyprutils/math/Region.hpp>
|
||||
|
||||
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]);
|
467
src/render/Renderer.cpp
Normal file
467
src/render/Renderer.cpp
Normal file
|
@ -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 <xf86drm.h>
|
||||
#include "Buffer.hpp"
|
||||
#include "LayerSurface.hpp"
|
||||
#include "Egl.hpp"
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <hyprutils/math/Box.hpp>
|
||||
#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<CCZwpLinuxDmabufFeedbackV1>(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<CEGL>(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<CCWlRegion>(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<int>(std::round(pMonitor->size.x)), static_cast<int>(std::round(pMonitor->size.y)),
|
||||
pMonitor->pCurrentLayerSurface);
|
||||
pMonitor->pCurrentLayerSurface->pViewport->sendSetDestination(static_cast<int>(std::round(pMonitor->size.x)), static_cast<int>(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<CBuffer> 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<CBuffer>(size);
|
||||
|
||||
monitorBuffers[pMonitor] = buf;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void CRenderer::renderCpu(SMonitor* pMonitor, SP<CBuffer> 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<CBuffer> 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<Hyprlang::INT* const*>(g_pConfigManager->config->getConfigValuePtr("splash")->getDataStaticPtr());
|
||||
static auto* const PSPLASHOFFSET = reinterpret_cast<Hyprlang::FLOAT* const*>(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<Hyprlang::INT* const*>(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);
|
||||
}
|
52
src/render/Renderer.hpp
Normal file
52
src/render/Renderer.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include <gbm.h>
|
||||
#include <unordered_map>
|
||||
#include <hyprutils/math/Box.hpp>
|
||||
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<SMonitor*, SP<CBuffer>> monitorBuffers;
|
||||
|
||||
private:
|
||||
SP<CCZwpLinuxDmabufFeedbackV1> dmabufFeedback;
|
||||
|
||||
void renderCpu(SMonitor* pMonitor, SP<CBuffer> buf);
|
||||
void renderGpu(SMonitor* pMonitor, SP<CBuffer> 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<CBuffer> getOrCreateBuffer(SMonitor* pMonitor);
|
||||
|
||||
struct {
|
||||
struct SShader {
|
||||
GLuint program = 0;
|
||||
GLint proj = -1, tex = -1, posAttrib = -1, texAttrib = -1;
|
||||
} shader;
|
||||
} gl;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CRenderer> g_pRenderer;
|
|
@ -2,9 +2,14 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <magic.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -17,5 +17,11 @@ class CWallpaperTarget {
|
|||
|
||||
bool m_bHasAlpha = true;
|
||||
|
||||
cairo_surface_t* m_pCairoSurface;
|
||||
struct {
|
||||
cairo_surface_t* cairoSurface = nullptr;
|
||||
} cpu;
|
||||
|
||||
struct {
|
||||
uint32_t textureID = 0;
|
||||
} gpu;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue