feat: basic inverted rendering

This commit is contained in:
Virt 2024-06-30 11:10:42 +02:00
commit 69e2583e57
7 changed files with 333 additions and 1 deletions

View file

@ -10,6 +10,11 @@
#define CONFIG_THRESHOLD "threshold"
#define CONFIG_HW_DEBUG "hw_debug"
#define CONFIG_INVERT "plugin:dynamic-cursors:invert"
#define CONFIG_INVERT_SHADER "plugin:dynamic-cursors:invert:shader"
#define CONFIG_INVERT_CHROMA "plugin:dynamic-cursors:invert:chroma"
#define CONFIG_INVERT_CHROMA_COLOR "plugin:dynamic-cursors:invert:chroma:color"
#define CONFIG_SHAKE "shake:enabled"
#define CONFIG_SHAKE_NEAREST "shake:nearest"
#define CONFIG_SHAKE_THRESHOLD "shake:threshold"

View file

@ -54,7 +54,12 @@ Reimplements rendering of the software cursor.
Is also largely identical to hyprlands impl, but uses our custom rendering to rotate the cursor.
*/
void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos) {
<<<<<<< HEAD
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
=======
static auto* const* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->getDataStaticPtr();
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
>>>>>>> c8ae8fe (feat: basic inverted rendering)
if (!pointers->hasCursor())
return;
@ -94,10 +99,17 @@ void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMo
box.rot = resultShown.rotation;
// now pass the hotspot to rotate around
<<<<<<< HEAD
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.F, nullptr, 0, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST, resultShown.stretch.angle, resultShown.stretch.magnitude);
if (pointers->currentCursorImage.surface)
pointers->currentCursorImage.surface->resource()->frame(now);
=======
if (**PINVERT)
renderCursorTextureInternalWithDamageInverted(texture, &box, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
else
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
>>>>>>> c8ae8fe (feat: basic inverted rendering)
}
/*
@ -131,9 +143,16 @@ void CDynamicCursors::damageSoftware(CPointerManager* pointers) {
This function reimplements the hardware cursor buffer drawing.
It is largely copied from hyprland, but adjusted to allow the cursor to be rotated.
*/
<<<<<<< HEAD
SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
static auto* const* PHW_DEBUG = (Hyprlang::INT* const*) getConfig(CONFIG_HW_DEBUG);
static auto* const* PNEAREST = (Hyprlang::INT* const*) getConfig(CONFIG_SHAKE_NEAREST);
=======
wlr_buffer* CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
static auto* const* PHW_DEBUG= (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_HW_DEBUG)->getDataStaticPtr();
static auto* const* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->getDataStaticPtr();
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
>>>>>>> c8ae8fe (feat: basic inverted rendering)
auto output = state->monitor->output;
@ -243,7 +262,14 @@ SP<Aquamarine::IBuffer> CDynamicCursors::renderHardware(CPointerManager* pointer
xbox.rot = resultShown.rotation;
// use our custom draw function
<<<<<<< HEAD
renderCursorTextureInternalWithDamage(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.waitTimeline, pointers->currentCursorImage.waitPoint, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST, resultShown.stretch.angle, resultShown.stretch.magnitude);
=======
if (**PINVERT)
renderCursorTextureInternalWithDamageInverted(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
else
renderCursorTextureInternalWithDamage(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST);
>>>>>>> c8ae8fe (feat: basic inverted rendering)
g_pHyprOpenGL->end();
glFlush();

42
src/invert/shader.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "src/debug/Log.hpp"
#define private public
#include <hyprland/src/render/OpenGL.hpp>
#undef private
#include <hyprland/src/Compositor.hpp>
#include <hyprland/src/render/Shaders.hpp>
#include "shader.hpp"
void CInversionShader::compile(std::string vertex, std::string fragment) {
program = g_pHyprOpenGL->createProgram(vertex, fragment);
texAttrib = glGetAttribLocation(program, "texcoord");
posAttrib = glGetAttribLocation(program, "pos");
proj = glGetUniformLocation(program, "proj");
backgroundTex = glGetUniformLocation(program, "backgroundTex");
cursorTex = glGetUniformLocation(program, "cursorTex");
alpha = glGetUniformLocation(program, "alpha");
applyTint = glGetUniformLocation(program, "applyTint");
tint = glGetUniformLocation(program, "tint");
}
CInversionShader::~CInversionShader() {
glDeleteProgram(program);
program = 0;
}
CShaders::CShaders() {
RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)),
"Couldn't set current EGL!");
rgba.compile(VERTEX, RGBA);
ext.compile(VERTEX, EXT);
rgbx.compile(VERTEX, RGBX);
RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)),
"Couldn't set current EGL!");
}

152
src/invert/shader.hpp Normal file
View file

@ -0,0 +1,152 @@
#include <string>
#include <hyprland/src/render/Shader.hpp>
// we need our own shader class as we have two textures
class CInversionShader {
public:
GLuint program = 0;
GLint proj = -1;
GLint color = -1;
GLint alphaMatte = -1;
GLint cursorTex = -1;
GLint backgroundTex = -1;
GLint alpha = -1;
GLint posAttrib = -1;
GLint texAttrib = -1;
GLint matteTexAttrib = -1;
GLint discardOpaque = -1;
GLint discardAlpha = -1;
GLfloat discardAlphaValue = -1;
GLint applyTint = -1;
GLint tint = -1;
void compile(std::string vertex, std::string fragment);
~CInversionShader();
};
class CShaders {
public:
CInversionShader rgba;
CInversionShader rgbx;
CInversionShader ext;
CShaders();
};
inline std::unique_ptr<CShaders> g_pShaders;
inline const std::string VERTEX = R"#(
uniform mat3 proj;
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;
varying vec2 v_screencord;
void main() {
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
v_screencord = gl_Position.xy / 2.0 + vec2(0.5, 0.5); // transform to texture coords
v_texcoord = texcoord;
}
)#";
inline const std::string RGBA = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
varying vec2 v_screencord; // is in 0-1
uniform sampler2D cursorTex;
uniform sampler2D backgroundTex;
uniform float alpha;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 cursor = texture2D(cursorTex, v_texcoord);
// load and invert background
vec4 background = texture2D(backgroundTex, v_screencord);
background.rgb = vec3(1.0, 1.0, 1.0) - background.rgb;
background *= cursor[3]; // premultiplied alpha
// invert hue
//background.rgb = -background.rgb + dot(vec3(0.26312, 0.5283, 0.10488), background.rgb) * 2.0;
vec4 pixColor = cursor;
//if (cursor[3] != 1.0) pixColor = cursor;
//pixColor = vec4(v_screencord + vec2(1.0, 1.0) / 2.0, 0.0, 1.0);
vec4 chroma = vec4(0.0, 0.0, 0.0, 1.0);
float diff = (abs(chroma.x - cursor.x) + abs(chroma.y - cursor.y) + abs(chroma.z - cursor.z) + abs(chroma.w - cursor.w)) / 4.0;
pixColor = background * (1.0 - diff) + cursor * diff;
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
gl_FragColor = pixColor * alpha;
}
)#";
inline const std::string RGBX = R"#(
precision highp float;
varying vec2 v_texcoord;
uniform sampler2D cursorTex;
uniform sampler2D backgroundTex;
uniform float alpha;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = vec4(texture2D(cursorTex, v_texcoord).rgb, 1.0);
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
pixColor.r = 1.0;
gl_FragColor = pixColor * alpha;
}
)#";
inline const std::string EXT = R"#(
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 v_texcoord;
uniform samplerExternalOES texture0;
uniform samplerExternalOES texture1;
uniform float alpha;
uniform int applyTint;
uniform vec3 tint;
void main() {
vec4 pixColor = texture2D(texture0, v_texcoord);
if (applyTint == 1) {
pixColor[0] = pixColor[0] * tint[0];
pixColor[1] = pixColor[1] * tint[1];
pixColor[2] = pixColor[2] * tint[2];
}
pixColor.r = 1.0;
gl_FragColor = pixColor * alpha;
}
)#";

View file

@ -10,6 +10,7 @@
#include "cursor.hpp"
#include "config/config.hpp"
#include "src/debug/Log.hpp"
#include "invert/shader.hpp"
#include "src/managers/PointerManager.hpp"
#include "src/version.h"
@ -112,6 +113,11 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
addConfig(CONFIG_SHAKE_THRESHOLD, 4.0f);
addConfig(CONFIG_SHAKE_FACTOR, 1.5f);
addConfig(CONFIG_INVERT, 0);
addConfig(CONFIG_INVERT_SHADER, "normal");
addConfig(CONFIG_INVERT_CHROMA, 0);
addConfig(CONFIG_INVERT_CHROMA_COLOR, 0xFF000000); // opaque black
addShapeConfig(CONFIG_TILT_FUNCTION, "negative_quadratic");
addShapeConfig(CONFIG_TILT_LIMIT, 5000);
@ -128,6 +134,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
// init things
g_pDynamicCursors = std::make_unique<CDynamicCursors>();
g_pShaders = std::make_unique<CShaders>();
// try hooking
try {
@ -145,7 +152,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
}
return {"dynamic-cursors", "a plugin to make your hyprland cursor more realistic, also adds shake to find", "Virt", "0.1"};
}
}
APICALL EXPORT void PLUGIN_EXIT() { }

View file

@ -11,6 +11,7 @@
#include <hyprland/src/config/ConfigValue.hpp>
#include "renderer.hpp"
#include "invert/shader.hpp"
/*
This is the projectBox method from hyprland, but with support for rotation around a point, the hotspot.
@ -156,3 +157,101 @@ void renderCursorTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, CRegion
glBindTexture(tex->m_iTarget, 0);
}
void renderCursorTextureInternalWithDamageInverted(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint, Vector2D hotspot, bool nearest, float stretchAngle, Vector2D stretch, int mode, bool chroma, CColor chromaColor, Vector2D screenOffset) {
TRACY_GPU_ZONE("RenderDynamicCursor");
if (waitTimeline != nullptr) {
if (!g_pHyprOpenGL->waitForTimelinePoint(waitTimeline, waitPoint)) {
Debug::log(ERR, "renderTextureInternalWithDamage: failed to wait for explicit sync point {}", waitPoint);
return;
}
}
alpha = std::clamp(alpha, 0.f, 1.f);
if (damage->empty())
return;
CBox newBox = *pBox;
g_pHyprOpenGL->m_RenderData.renderModif.applyToBox(newBox);
// get transform
const auto TRANSFORM = wlTransformToHyprutils(invertTransform(!g_pHyprOpenGL->m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : g_pHyprOpenGL->m_RenderData.pMonitor->transform));
float matrix[9];
projectCursorBox(matrix, newBox, TRANSFORM, newBox.rot, g_pHyprOpenGL->m_RenderData.monitorProjection.data(), hotspot, stretchAngle, stretch);
float glMatrix[9];
matrixMultiply(glMatrix, g_pHyprOpenGL->m_RenderData.projection, matrix);
CInversionShader* shader = nullptr;
switch (tex->m_iType) {
case TEXTURE_RGBA: shader = &g_pShaders->rgba; break;
case TEXTURE_RGBX: shader = &g_pShaders->rgbx; break;
case TEXTURE_EXTERNAL: shader = &g_pShaders->ext; break;
default: RASSERT(false, "tex->m_iTarget unsupported!");
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(tex->m_iTarget, tex->m_iTexID);
if (g_pHyprOpenGL->m_RenderData.useNearestNeighbor || nearest) {
glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glUseProgram(shader->program);
#ifndef GLES2
glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix);
#else
matrixTranspose(glMatrix, glMatrix);
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix);
#endif
glUniform2f(shader->screenOffset, screenOffset.x, screenOffset.y);
glUniform1i(shader->backgroundTex, 0);
glUniform1i(shader->cursorTex, 1);
glUniform1f(shader->alpha, alpha);
glUniform1i(shader->mode, mode);
glUniform1i(shader->chroma, chroma);
glUniform4f(shader->chromaColor, chromaColor.r, chromaColor.g, chromaColor.b, chromaColor.a);
CBox transformedBox = newBox;
transformedBox.transform(wlTransformToHyprutils(invertTransform(g_pHyprOpenGL->m_RenderData.pMonitor->transform)), g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y);
glUniform1i(shader->applyTint, 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);
if (g_pHyprOpenGL->m_RenderData.clipBox.width != 0 && g_pHyprOpenGL->m_RenderData.clipBox.height != 0) {
CRegion damageClip{g_pHyprOpenGL->m_RenderData.clipBox.x, g_pHyprOpenGL->m_RenderData.clipBox.y, g_pHyprOpenGL->m_RenderData.clipBox.width, g_pHyprOpenGL->m_RenderData.clipBox.height};
damageClip.intersect(*damage);
if (!damageClip.empty()) {
for (auto& RECT : damageClip.getRects()) {
g_pHyprOpenGL->scissor(&RECT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
} else {
for (auto& RECT : damage->getRects()) {
g_pHyprOpenGL->scissor(&RECT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
glDisableVertexAttribArray(shader->posAttrib);
glDisableVertexAttribArray(shader->texAttrib);
glBindTexture(tex->m_iTarget, 0);
}

View file

@ -2,3 +2,4 @@
#include <hyprland/src/helpers/math/Math.hpp>
void renderCursorTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint, Vector2D hotspot, bool nearest, float stretchAngle, Vector2D stretch);
void renderCursorTextureInternalWithDamageInverted(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, SP<CSyncTimeline> waitTimeline, uint64_t waitPoint, Vector2D hotspot, bool nearest, float stretchAngle, Vector2D stretch, int mode, bool chroma, CColor chromaColor, Vector2D screenOffset);