feat: basic inverted rendering

This commit is contained in:
Virt 2024-06-30 11:10:42 +02:00
commit c8ae8fedb8
7 changed files with 323 additions and 16 deletions

View file

@ -28,6 +28,7 @@ Is also largely identical to hyprlands impl, but uses our custom rendering to ro
*/ */
void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos) { void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMonitor, timespec* now, CRegion& damage, std::optional<Vector2D> overridePos) {
static auto* const* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->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();
if (!pointers->hasCursor()) if (!pointers->hasCursor())
return; return;
@ -63,7 +64,10 @@ void CDynamicCursors::renderSoftware(CPointerManager* pointers, SP<CMonitor> pMo
box.rot = this->angle; box.rot = this->angle;
// now pass the hotspot to rotate around // now pass the hotspot to rotate around
renderCursorTextureInternalWithDamage(texture, &box, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST); 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);
} }
/* /*
@ -96,6 +100,7 @@ It is largely copied from hyprland, but adjusted to allow the cursor to be rotat
wlr_buffer* CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) { 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* 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* PNEAREST = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_SHAKE_NEAREST)->getDataStaticPtr();
static auto* const* PINVERT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, CONFIG_INVERT)->getDataStaticPtr();
auto output = state->monitor->output; auto output = state->monitor->output;
@ -164,7 +169,10 @@ wlr_buffer* CDynamicCursors::renderHardware(CPointerManager* pointers, SP<CPoint
xbox.rot = this->angle; xbox.rot = this->angle;
// use our custom draw function // use our custom draw function
renderCursorTextureInternalWithDamage(texture, &xbox, &damage, 1.F, pointers->currentCursorImage.hotspot * state->monitor->scale * zoom, zoom > 1 && **PNEAREST); 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);
g_pHyprOpenGL->end(); g_pHyprOpenGL->end();
glFlush(); glFlush();

View file

@ -2,21 +2,28 @@
#include <hyprland/src/plugins/PluginAPI.hpp> #include <hyprland/src/plugins/PluginAPI.hpp>
#define CONFIG_ENABLED "plugin:dynamic-cursors:enabled" #define CONFIG_ENABLED "plugin:dynamic-cursors:enabled"
#define CONFIG_MODE "plugin:dynamic-cursors:mode" #define CONFIG_MODE "plugin:dynamic-cursors:mode"
#define CONFIG_SHAKE "plugin:dynamic-cursors:shake" #define CONFIG_THRESHOLD "plugin:dynamic-cursors:threshold"
#define CONFIG_THRESHOLD "plugin:dynamic-cursors:threshold" #define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug"
#define CONFIG_SHAKE_NEAREST "plugin:dynamic-cursors:shake:nearest"
#define CONFIG_SHAKE_THRESHOLD "plugin:dynamic-cursors:shake:threshold"
#define CONFIG_SHAKE_FACTOR "plugin:dynamic-cursors:shake:factor"
#define CONFIG_SHAKE_EFFECTS "plugin:dynamic-cursors:shake:effects"
#define CONFIG_SHAKE_IPC "plugin:dynamic-cursors:shake:ipc"
#define CONFIG_LENGTH "plugin:dynamic-cursors:rotate:length"
#define CONFIG_ROTATE_OFFSET "plugin:dynamic-cursors:rotate:offset"
#define CONFIG_MASS "plugin:dynamic-cursors:tilt:limit"
#define CONFIG_FUNCTION "plugin:dynamic-cursors:tilt:function"
#define CONFIG_HW_DEBUG "plugin:dynamic-cursors:hw_debug" #define CONFIG_LENGTH "plugin:dynamic-cursors:rotate:length"
#define CONFIG_ROTATE_OFFSET "plugin:dynamic-cursors:rotate:offset"
#define CONFIG_MASS "plugin:dynamic-cursors:tilt:limit"
#define CONFIG_FUNCTION "plugin:dynamic-cursors:tilt:function"
#define CONFIG_SHAKE "plugin:dynamic-cursors:shake"
#define CONFIG_SHAKE_NEAREST "plugin:dynamic-cursors:shake:nearest"
#define CONFIG_SHAKE_THRESHOLD "plugin:dynamic-cursors:shake:threshold"
#define CONFIG_SHAKE_FACTOR "plugin:dynamic-cursors:shake:factor"
#define CONFIG_SHAKE_EFFECTS "plugin:dynamic-cursors:shake:effects"
#define CONFIG_SHAKE_IPC "plugin:dynamic-cursors:shake:ipc"
#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"
inline HANDLE PHANDLE = nullptr; inline HANDLE PHANDLE = nullptr;

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

@ -8,6 +8,7 @@
#include "globals.hpp" #include "globals.hpp"
#include "cursor.hpp" #include "cursor.hpp"
#include "invert/shader.hpp"
#include "src/managers/PointerManager.hpp" #include "src/managers/PointerManager.hpp"
typedef void (*origRenderSofwareCursorsFor)(void*, SP<CMonitor>, timespec*, CRegion&, std::optional<Vector2D>); typedef void (*origRenderSofwareCursorsFor)(void*, SP<CMonitor>, timespec*, CRegion&, std::optional<Vector2D>);
@ -78,6 +79,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
} }
g_pDynamicCursors = std::make_unique<CDynamicCursors>(); g_pDynamicCursors = std::make_unique<CDynamicCursors>();
g_pShaders = std::make_unique<CShaders>();
static const auto RENDER_SOFTWARE_CURSORS_FOR_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderSoftwareCursorsFor"); static const auto RENDER_SOFTWARE_CURSORS_FOR_METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderSoftwareCursorsFor");
g_pRenderSoftwareCursorsForHook = HyprlandAPI::createFunctionHook(PHANDLE, RENDER_SOFTWARE_CURSORS_FOR_METHODS[0].address, (void*) &hkRenderSoftwareCursorsFor); g_pRenderSoftwareCursorsForHook = HyprlandAPI::createFunctionHook(PHANDLE, RENDER_SOFTWARE_CURSORS_FOR_METHODS[0].address, (void*) &hkRenderSoftwareCursorsFor);
@ -116,6 +118,11 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_SHAKE_THRESHOLD, Hyprlang::FLOAT{4}); HyprlandAPI::addConfigValue(PHANDLE, CONFIG_SHAKE_THRESHOLD, Hyprlang::FLOAT{4});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_SHAKE_FACTOR, Hyprlang::FLOAT{1.5}); HyprlandAPI::addConfigValue(PHANDLE, CONFIG_SHAKE_FACTOR, Hyprlang::FLOAT{1.5});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_INVERT, Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_INVERT_SHADER, Hyprlang::STRING{"normal"});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_INVERT_CHROMA, Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_INVERT_CHROMA_COLOR, Hyprlang::INT{0xFF000000}); // opaque black
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_FUNCTION, Hyprlang::STRING{"negative_quadratic"}); HyprlandAPI::addConfigValue(PHANDLE, CONFIG_FUNCTION, Hyprlang::STRING{"negative_quadratic"});
HyprlandAPI::addConfigValue(PHANDLE, CONFIG_MASS, Hyprlang::INT{5000}); HyprlandAPI::addConfigValue(PHANDLE, CONFIG_MASS, Hyprlang::INT{5000});

View file

@ -11,6 +11,7 @@
#include "renderer.hpp" #include "renderer.hpp"
#include "hyprland/math.hpp" #include "hyprland/math.hpp"
#include "invert/shader.hpp"
/* /*
This is the projectBox method from hyprland, but with support for rotation around a point, the hotspot. This is the projectBox method from hyprland, but with support for rotation around a point, the hotspot.
@ -136,3 +137,92 @@ void renderCursorTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, CRegion
glBindTexture(tex->m_iTarget, 0); glBindTexture(tex->m_iTarget, 0);
} }
void renderCursorTextureInternalWithDamageInverted(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, Vector2D hotspot, bool nearest) {
TRACY_GPU_ZONE("RenderDynamicCursor");
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(wlr_output_transform_invert(!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);
float glMatrix[9];
wlr_matrix_multiply(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!");
}
// bind background and cursor texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB.m_cTex->m_iTarget, g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB.m_cTex->m_iTexID);
glActiveTexture(GL_TEXTURE1);
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
wlr_matrix_transpose(glMatrix, glMatrix);
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix);
#endif
glUniform1i(shader->backgroundTex, 0);
glUniform1i(shader->cursorTex, 1);
glUniform1f(shader->alpha, alpha);
CBox transformedBox = newBox;
transformedBox.transform(wlTransformToHyprutils(wlr_output_transform_invert(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,4 +2,5 @@
#include <hyprland/src/helpers/math/Math.hpp> #include <hyprland/src/helpers/math/Math.hpp>
void renderCursorTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, Vector2D hotspot, bool nearest); void renderCursorTextureInternalWithDamage(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, Vector2D hotspot, bool nearest);
void renderCursorTextureInternalWithDamageInverted(SP<CTexture> tex, CBox* pBox, CRegion* damage, float alpha, Vector2D hotspot, bool nearest);
void projectCursorBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9], Vector2D hotspot); void projectCursorBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9], Vector2D hotspot);