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
|
deps
|
||||||
REQUIRED
|
REQUIRED
|
||||||
IMPORTED_TARGET
|
IMPORTED_TARGET
|
||||||
|
gbm
|
||||||
|
egl
|
||||||
|
libdrm
|
||||||
wayland-client
|
wayland-client
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
cairo
|
cairo
|
||||||
|
|
|
@ -6,6 +6,7 @@ Hyprpaper is a blazing fast wallpaper utility for Hyprland with the ability to d
|
||||||
- Per-output wallpapers
|
- Per-output wallpapers
|
||||||
- fill or contain modes
|
- fill or contain modes
|
||||||
- fractional scaling support
|
- fractional scaling support
|
||||||
|
- gpu acceleration
|
||||||
- IPC for blazing fast wallpaper switches
|
- IPC for blazing fast wallpaper switches
|
||||||
- preloading targets into memory
|
- preloading targets into memory
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
#include "Hyprpaper.hpp"
|
#include "Hyprpaper.hpp"
|
||||||
|
#include "render/Renderer.hpp"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/types.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;
|
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) {
|
} else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
|
||||||
g_pHyprpaper->m_pCursorShape =
|
g_pHyprpaper->m_pCursorShape =
|
||||||
makeShared<CCWpCursorShapeManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)registry->resource(), name, &wp_cursor_shape_manager_v1_interface, 1));
|
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);
|
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()) {
|
while (m_vMonitors.size() < 1 || m_vMonitors[0]->name.empty()) {
|
||||||
wl_display_dispatch(m_sDisplay);
|
wl_display_dispatch(m_sDisplay);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +139,6 @@ void CHyprpaper::tick(bool force) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
preloadAllWallpapersFromConfig();
|
preloadAllWallpapersFromConfig();
|
||||||
ensurePoolBuffersPresent();
|
|
||||||
|
|
||||||
recheckAllMonitors();
|
recheckAllMonitors();
|
||||||
}
|
}
|
||||||
|
@ -129,25 +168,6 @@ void CHyprpaper::unloadWallpaper(const std::string& path) {
|
||||||
return;
|
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
|
m_mWallpaperTargets.erase(path); // will free the cairo surface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +244,7 @@ void CHyprpaper::recheckMonitor(SMonitor* pMonitor) {
|
||||||
|
|
||||||
if (pMonitor->wantsReload) {
|
if (pMonitor->wantsReload) {
|
||||||
pMonitor->wantsReload = false;
|
pMonitor->wantsReload = false;
|
||||||
renderWallpaperForMonitor(pMonitor);
|
g_pRenderer->renderWallpaperForMonitor(pMonitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,46 +292,6 @@ SMonitor* CHyprpaper::getMonitorFromName(const std::string& monname) {
|
||||||
return nullptr;
|
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) {
|
void CHyprpaper::clearWallpaperFromMonitor(const std::string& monname) {
|
||||||
|
|
||||||
const auto PMONITOR = getMonitorFromName(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();
|
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() {
|
bool CHyprpaper::lockSingleInstance() {
|
||||||
const std::string XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR");
|
const std::string XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR");
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,31 @@
|
||||||
#include "defines.hpp"
|
#include "defines.hpp"
|
||||||
#include "helpers/MiscFunctions.hpp"
|
#include "helpers/MiscFunctions.hpp"
|
||||||
#include "helpers/Monitor.hpp"
|
#include "helpers/Monitor.hpp"
|
||||||
#include "helpers/PoolBuffer.hpp"
|
|
||||||
#include "ipc/Socket.hpp"
|
#include "ipc/Socket.hpp"
|
||||||
#include "render/WallpaperTarget.hpp"
|
#include "render/WallpaperTarget.hpp"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
#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"
|
|
||||||
|
|
||||||
struct SWallpaperRenderData {
|
struct SWallpaperRenderData {
|
||||||
bool contain = false;
|
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 {
|
class CHyprpaper {
|
||||||
public:
|
public:
|
||||||
// important
|
// important
|
||||||
|
@ -33,6 +42,7 @@ class CHyprpaper {
|
||||||
SP<CCWlPointer> m_pSeatPointer;
|
SP<CCWlPointer> m_pSeatPointer;
|
||||||
SP<CCWpCursorShapeDeviceV1> m_pSeatCursorShapeDevice;
|
SP<CCWpCursorShapeDeviceV1> m_pSeatCursorShapeDevice;
|
||||||
SP<CCWpCursorShapeManagerV1> m_pCursorShape;
|
SP<CCWpCursorShapeManagerV1> m_pCursorShape;
|
||||||
|
SP<CCZwpLinuxDmabufV1> m_pLinuxDmabuf;
|
||||||
|
|
||||||
// init the utility
|
// init the utility
|
||||||
CHyprpaper();
|
CHyprpaper();
|
||||||
|
@ -43,28 +53,22 @@ class CHyprpaper {
|
||||||
std::unordered_map<std::string, std::string> m_mMonitorActiveWallpapers;
|
std::unordered_map<std::string, std::string> m_mMonitorActiveWallpapers;
|
||||||
std::unordered_map<std::string, SWallpaperRenderData> m_mMonitorWallpaperRenderData;
|
std::unordered_map<std::string, SWallpaperRenderData> m_mMonitorWallpaperRenderData;
|
||||||
std::unordered_map<SMonitor*, CWallpaperTarget*> m_mMonitorActiveWallpaperTargets;
|
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<std::unique_ptr<SMonitor>> m_vMonitors;
|
||||||
|
std::vector<SDMABUFFormat> m_vDmabufFormats;
|
||||||
|
|
||||||
std::string m_szExplicitConfigPath;
|
std::string m_szExplicitConfigPath;
|
||||||
bool m_bNoFractionalScale = false;
|
bool m_bNoFractionalScale = false;
|
||||||
|
bool m_bNoGpu = false;
|
||||||
|
|
||||||
void removeOldHyprpaperImages();
|
void removeOldHyprpaperImages();
|
||||||
void preloadAllWallpapersFromConfig();
|
void preloadAllWallpapersFromConfig();
|
||||||
void recheckAllMonitors();
|
void recheckAllMonitors();
|
||||||
void ensureMonitorHasActiveWallpaper(SMonitor*);
|
void ensureMonitorHasActiveWallpaper(SMonitor*);
|
||||||
void createLSForMonitor(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&);
|
void clearWallpaperFromMonitor(const std::string&);
|
||||||
SMonitor* getMonitorFromName(const std::string&);
|
SMonitor* getMonitorFromName(const std::string&);
|
||||||
bool isPreloaded(const std::string&);
|
bool isPreloaded(const std::string&);
|
||||||
void recheckMonitor(SMonitor*);
|
void recheckMonitor(SMonitor*);
|
||||||
void ensurePoolBuffersPresent();
|
|
||||||
SPoolBuffer* getPoolBuffer(SMonitor*, CWallpaperTarget*);
|
|
||||||
void unloadWallpaper(const std::string&);
|
void unloadWallpaper(const std::string&);
|
||||||
void createSeat(SP<CCWlSeat>);
|
void createSeat(SP<CCWlSeat>);
|
||||||
bool lockSingleInstance(); // fails on multi-instance
|
bool lockSingleInstance(); // fails on multi-instance
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Monitor.hpp"
|
#include "Monitor.hpp"
|
||||||
#include "../Hyprpaper.hpp"
|
#include "../Hyprpaper.hpp"
|
||||||
|
|
||||||
|
#include "protocols/wayland.hpp"
|
||||||
|
|
||||||
void SMonitor::registerListeners() {
|
void SMonitor::registerListeners() {
|
||||||
output->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { size = Vector2D(width, height); });
|
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 "../defines.hpp"
|
||||||
#include "../render/LayerSurface.hpp"
|
#include "../render/LayerSurface.hpp"
|
||||||
#include "PoolBuffer.hpp"
|
|
||||||
#include "protocols/wayland.hpp"
|
class CCWlOutput;
|
||||||
|
|
||||||
struct SMonitor {
|
struct SMonitor {
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
|
@ -19,7 +19,6 @@ struct SMonitor {
|
||||||
bool wildcard = true;
|
bool wildcard = true;
|
||||||
|
|
||||||
uint32_t configureSerial = 0;
|
uint32_t configureSerial = 0;
|
||||||
SPoolBuffer buffer;
|
|
||||||
|
|
||||||
bool wantsReload = false;
|
bool wantsReload = false;
|
||||||
bool wantsACK = 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
|
// parse some args
|
||||||
std::string configPath;
|
std::string configPath;
|
||||||
bool noFractional = false;
|
bool noFractional = false, noGpu = false;
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config")) && argc >= i + 2) {
|
if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config")) && argc >= i + 2) {
|
||||||
configPath = std::string(argv[++i]);
|
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")) {
|
} else if (!strcmp(argv[i], "--no-fractional") || !strcmp(argv[i], "-n")) {
|
||||||
noFractional = true;
|
noFractional = true;
|
||||||
Debug::log(LOG, "Disabling fractional scaling support!");
|
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 {
|
} else {
|
||||||
std::cout << "Hyprpaper usage: hyprpaper [arg [...]].\n\nArguments:\n"
|
std::cout << "Hyprpaper usage: hyprpaper [arg [...]].\n\nArguments:\n"
|
||||||
<< "--help -h | Show this help message\n"
|
<< "--help -h | Show this help message\n"
|
||||||
<< "--config -c | Specify config file to use\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;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +32,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
g_pHyprpaper = std::make_unique<CHyprpaper>();
|
g_pHyprpaper = std::make_unique<CHyprpaper>();
|
||||||
g_pHyprpaper->m_szExplicitConfigPath = configPath;
|
g_pHyprpaper->m_szExplicitConfigPath = configPath;
|
||||||
g_pHyprpaper->m_bNoFractionalScale = noFractional;
|
g_pHyprpaper->m_bNoFractionalScale = noFractional;
|
||||||
|
g_pHyprpaper->m_bNoGpu = noGpu;
|
||||||
g_pHyprpaper->init();
|
g_pHyprpaper->init();
|
||||||
|
|
||||||
return 0;
|
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 "../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) {
|
CLayerSurface::CLayerSurface(SMonitor* pMonitor) {
|
||||||
m_pMonitor = pMonitor;
|
m_pMonitor = pMonitor;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../defines.hpp"
|
#include "../defines.hpp"
|
||||||
#include "protocols/fractional-scale-v1.hpp"
|
|
||||||
#include "protocols/viewporter.hpp"
|
class CCZwlrLayerSurfaceV1;
|
||||||
#include "protocols/wayland.hpp"
|
class CCWlSurface;
|
||||||
#include "protocols/wlr-layer-shell-unstable-v1.hpp"
|
class CCWpFractionalScaleV1;
|
||||||
|
class CCWpViewport;
|
||||||
|
|
||||||
struct SMonitor;
|
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 <chrono>
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
#include "Egl.hpp"
|
||||||
|
#include "Renderer.hpp"
|
||||||
|
|
||||||
CWallpaperTarget::~CWallpaperTarget() {
|
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) {
|
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);
|
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;
|
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