renderer: add shadowable props

This commit is contained in:
Vaxry 2024-03-05 20:24:11 +00:00
parent 2fa0da47ab
commit a4dc596cb8
13 changed files with 173 additions and 21 deletions

View File

@ -35,6 +35,13 @@ CConfigManager::CConfigManager() : m_config(getMainConfigPath().c_str(), Hyprlan
} }
void CConfigManager::init() { void CConfigManager::init() {
#define SHADOWABLE(name) \
m_config.addSpecialConfigValue(name, "shadow_size", Hyprlang::INT{3}); \
m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{2}); \
m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \
m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2});
m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0}); m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0}); m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
m_config.addConfigValue("general:grace", Hyprlang::INT{0}); m_config.addConfigValue("general:grace", Hyprlang::INT{0});
@ -70,6 +77,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"}); m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"});
m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0}); m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1}); m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1});
SHADOWABLE("input-field");
m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""});
@ -80,6 +88,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"}); m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"});
m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"}); m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"});
m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"}); m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"});
SHADOWABLE("label");
m_config.registerHandler(&::handleSource, "source", {false}); m_config.registerHandler(&::handleSource, "source", {false});
@ -89,6 +98,8 @@ void CConfigManager::init() {
if (result.error) if (result.error)
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());
#undef SHADOWABLE
} }
std::mutex configMtx; std::mutex configMtx;
@ -101,6 +112,12 @@ void* const* CConfigManager::getValuePtr(const std::string& name) {
std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() { std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
std::vector<CConfigManager::SWidgetConfig> result; std::vector<CConfigManager::SWidgetConfig> result;
#define SHADOWABLE(name) \
{"shadow_size", m_config.getSpecialConfigValue(name, "shadow_size", k.c_str())}, {"shadow_passes", m_config.getSpecialConfigValue(name, "shadow_passes", k.c_str())}, \
{"shadow_color", m_config.getSpecialConfigValue(name, "shadow_color", k.c_str())}, { \
"shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \
}
// //
auto keys = m_config.listKeysForSpecialCategory("background"); auto keys = m_config.listKeysForSpecialCategory("background");
for (auto& k : keys) { for (auto& k : keys) {
@ -146,6 +163,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())}, {"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())},
{"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())}, {"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())},
{"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())}, {"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())},
SHADOWABLE("input-field"),
} }
}); });
// clang-format on // clang-format on
@ -165,6 +183,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"text", m_config.getSpecialConfigValue("label", "text", k.c_str())}, {"text", m_config.getSpecialConfigValue("label", "text", k.c_str())},
{"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())}, {"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())},
{"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())}, {"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())},
SHADOWABLE("label"),
} }
}); });
// clang-format on // clang-format on

View File

@ -17,11 +17,11 @@ static uint32_t glFormatToType(uint32_t gl) {
return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE; return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
} }
bool CFramebuffer::alloc(int w, int h) { bool CFramebuffer::alloc(int w, int h, bool highres) {
bool firstAlloc = false; bool firstAlloc = false;
uint32_t glFormat = drmFormatToGL(DRM_FORMAT_XRGB2101010); // TODO: revise only 10b when I find a way to figure out without sc whether display is 10b uint32_t glFormat = highres ? GL_RGBA16F : drmFormatToGL(DRM_FORMAT_XRGB2101010); // TODO: revise only 10b when I find a way to figure out without sc whether display is 10b
uint32_t glType = glFormatToType(glFormat); uint32_t glType = highres ? GL_FLOAT : glFormatToType(glFormat);
if (m_iFb == (uint32_t)-1) { if (m_iFb == (uint32_t)-1) {
firstAlloc = true; firstAlloc = true;

View File

@ -8,7 +8,7 @@ class CFramebuffer {
public: public:
~CFramebuffer(); ~CFramebuffer();
bool alloc(int w, int h); bool alloc(int w, int h, bool highres = false);
void addStencil(); void addStencil();
void bind() const; void bind() const;
void release(); void release();

View File

@ -138,14 +138,17 @@ CRenderer::CRenderer() {
blurPrepareShader.contrast = glGetUniformLocation(prog, "contrast"); blurPrepareShader.contrast = glGetUniformLocation(prog, "contrast");
blurPrepareShader.brightness = glGetUniformLocation(prog, "brightness"); blurPrepareShader.brightness = glGetUniformLocation(prog, "brightness");
prog = createProgram(TEXVERTSRC, FRAGBLURFINISH); prog = createProgram(TEXVERTSRC, FRAGBLURFINISH);
blurFinishShader.program = prog; blurFinishShader.program = prog;
blurFinishShader.tex = glGetUniformLocation(prog, "tex"); blurFinishShader.tex = glGetUniformLocation(prog, "tex");
blurFinishShader.proj = glGetUniformLocation(prog, "proj"); blurFinishShader.proj = glGetUniformLocation(prog, "proj");
blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos"); blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos");
blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord"); blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord");
blurFinishShader.brightness = glGetUniformLocation(prog, "brightness"); blurFinishShader.brightness = glGetUniformLocation(prog, "brightness");
blurFinishShader.noise = glGetUniformLocation(prog, "noise"); blurFinishShader.noise = glGetUniformLocation(prog, "noise");
blurFinishShader.colorize = glGetUniformLocation(prog, "colorize");
blurFinishShader.colorizeTint = glGetUniformLocation(prog, "colorizeTint");
blurFinishShader.boostA = glGetUniformLocation(prog, "boostA");
wlr_matrix_identity(projMatrix.data()); wlr_matrix_identity(projMatrix.data());
@ -164,6 +167,10 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
g_pEGL->makeCurrent(surf.eglSurface); g_pEGL->makeCurrent(surf.eglSurface);
glViewport(0, 0, surf.size.x, surf.size.y); glViewport(0, 0, surf.size.x, surf.size.y);
GLint fb = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fb);
pushFb(fb);
glClearColor(0.0, 0.0, 0.0, 0.0); glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -330,8 +337,8 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
wlr_matrix_multiply(glMatrix, projection.data(), matrix); wlr_matrix_multiply(glMatrix, projection.data(), matrix);
CFramebuffer mirrors[2]; CFramebuffer mirrors[2];
mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y); mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);
mirrors[1].alloc(outfb.m_vSize.x, outfb.m_vSize.y); mirrors[1].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);
CFramebuffer* currentRenderToFB = &mirrors[0]; CFramebuffer* currentRenderToFB = &mirrors[0];
@ -442,6 +449,10 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
glUniformMatrix3fv(blurFinishShader.proj, 1, GL_TRUE, glMatrix); glUniformMatrix3fv(blurFinishShader.proj, 1, GL_TRUE, glMatrix);
glUniform1f(blurFinishShader.noise, params.noise); glUniform1f(blurFinishShader.noise, params.noise);
glUniform1f(blurFinishShader.brightness, params.brightness); glUniform1f(blurFinishShader.brightness, params.brightness);
glUniform1i(blurFinishShader.colorize, params.colorize.has_value());
if (params.colorize.has_value())
glUniform3f(blurFinishShader.colorizeTint, params.colorize->r, params.colorize->g, params.colorize->b);
glUniform1f(blurFinishShader.boostA, params.boostA);
glUniform1i(blurFinishShader.tex, 0); glUniform1i(blurFinishShader.tex, 0);
@ -467,4 +478,14 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
renderTexture(box, currentRenderToFB->m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL); renderTexture(box, currentRenderToFB->m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL);
glEnable(GL_BLEND); glEnable(GL_BLEND);
} }
void CRenderer::pushFb(GLint fb) {
boundFBs.push_back(fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
}
void CRenderer::popFb() {
boundFBs.pop_back();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back());
}

View File

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#include <optional>
#include "../core/LockSurface.hpp" #include "../core/LockSurface.hpp"
#include "Shader.hpp" #include "Shader.hpp"
@ -22,8 +23,10 @@ class CRenderer {
}; };
struct SBlurParams { struct SBlurParams {
int size = 0, passes = 0; int size = 0, passes = 0;
float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0; float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0;
std::optional<CColor> colorize;
float boostA = 1.0;
}; };
SRenderFeedback renderLock(const CSessionLockSurface& surface); SRenderFeedback renderLock(const CSessionLockSurface& surface);
@ -35,6 +38,9 @@ class CRenderer {
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer; std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
std::chrono::system_clock::time_point gatheredAt; std::chrono::system_clock::time_point gatheredAt;
void pushFb(GLint fb);
void popFb();
private: private:
widgetMap_t widgets; widgetMap_t widgets;
@ -49,6 +55,8 @@ class CRenderer {
std::array<float, 9> projMatrix; std::array<float, 9> projMatrix;
std::array<float, 9> projection; std::array<float, 9> projection;
std::vector<GLint> boundFBs;
}; };
inline std::unique_ptr<CRenderer> g_pRenderer; inline std::unique_ptr<CRenderer> g_pRenderer;

View File

@ -59,6 +59,11 @@ class CShader {
GLint brightness = -1; GLint brightness = -1;
GLint noise = -1; GLint noise = -1;
// colorize
GLint colorize = -1;
GLint colorizeTint = -1;
GLint boostA = -1;
GLint getUniformLocation(const std::string&); GLint getUniformLocation(const std::string&);
void destroy(); void destroy();

View File

@ -331,6 +331,10 @@ uniform sampler2D tex;
uniform float noise; uniform float noise;
uniform float brightness; uniform float brightness;
uniform int colorize;
uniform vec3 colorizeTint;
uniform float boostA;
float hash(vec2 p) { float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
} }
@ -348,6 +352,13 @@ void main() {
pixColor.rgb *= brightness; pixColor.rgb *= brightness;
} }
pixColor.a *= boostA;
if (colorize == 1) {
gl_FragColor = vec4(colorizeTint.r, colorizeTint.g, colorizeTint.b, pixColor.a);
return;
}
gl_FragColor = pixColor; gl_FragColor = pixColor;
} }
)#"; )#";

View File

@ -52,7 +52,8 @@ void CLabel::plantTimer() {
labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this); labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this);
} }
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_) : surface(surface_) { CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, CSessionLockSurface* surface_) :
surface(surface_), shadow(this, props, viewport_) {
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text")); labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family")); std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color")); CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
@ -92,6 +93,7 @@ bool CLabel::draw(const SRenderData& data) {
// calc pos // calc pos
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign); pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
shadow.markShadowDirty();
} }
if (!pendingResourceID.empty()) { if (!pendingResourceID.empty()) {
@ -104,11 +106,12 @@ bool CLabel::draw(const SRenderData& data) {
resourceID = pendingResourceID; resourceID = pendingResourceID;
pendingResourceID = ""; pendingResourceID = "";
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign); pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
shadow.markShadowDirty();
} }
} }
CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y}; CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
shadow.draw(data);
g_pRenderer->renderTexture(box, asset->texture, data.opacity); g_pRenderer->renderTexture(box, asset->texture, data.opacity);
return false; return false;

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "IWidget.hpp" #include "IWidget.hpp"
#include "Shadowable.hpp"
#include "../../helpers/Vector2D.hpp" #include "../../helpers/Vector2D.hpp"
#include "../../core/Timer.hpp" #include "../../core/Timer.hpp"
#include "../AsyncResourceGatherer.hpp" #include "../AsyncResourceGatherer.hpp"
@ -38,4 +39,6 @@ class CLabel : public IWidget {
CAsyncResourceGatherer::SPreloadRequest request; CAsyncResourceGatherer::SPreloadRequest request;
std::shared_ptr<CTimer> labelTimer; std::shared_ptr<CTimer> labelTimer;
CShadowable shadow;
}; };

View File

@ -3,7 +3,7 @@
#include "../../core/hyprlock.hpp" #include "../../core/hyprlock.hpp"
#include <algorithm> #include <algorithm>
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) { CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {
size = std::any_cast<Hyprlang::VEC2>(props.at("size")); size = std::any_cast<Hyprlang::VEC2>(props.at("size"));
inner = std::any_cast<Hyprlang::INT>(props.at("inner_color")); inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
outer = std::any_cast<Hyprlang::INT>(props.at("outer_color")); outer = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
@ -105,6 +105,11 @@ bool CPasswordInputField::draw(const SRenderData& data) {
CBox inputFieldBox = {pos, size}; CBox inputFieldBox = {pos, size};
CBox outerBox = {pos - Vector2D{outThick, outThick}, size + Vector2D{outThick * 2, outThick * 2}}; CBox outerBox = {pos - Vector2D{outThick, outThick}, size + Vector2D{outThick * 2, outThick * 2}};
if (firstRender) {
firstRender = false;
shadow.markShadowDirty();
}
bool forceReload = false; bool forceReload = false;
updateFade(); updateFade();
@ -112,6 +117,10 @@ bool CPasswordInputField::draw(const SRenderData& data) {
updateFailTex(); updateFailTex();
updateHiddenInputState(); updateHiddenInputState();
SRenderData shadowData = data;
shadowData.opacity *= fade.a;
shadow.draw(shadowData);
float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0; float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0;
CColor outerCol = outer; CColor outerCol = outer;

View File

@ -3,6 +3,7 @@
#include "IWidget.hpp" #include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp" #include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp" #include "../../helpers/Color.hpp"
#include "Shadowable.hpp"
#include <chrono> #include <chrono>
#include <vector> #include <vector>
#include <any> #include <any>
@ -22,6 +23,8 @@ class CPasswordInputField : public IWidget {
void updateFailTex(); void updateFailTex();
void updateHiddenInputState(); void updateHiddenInputState();
bool firstRender = true;
Vector2D size; Vector2D size;
Vector2D pos; Vector2D pos;
Vector2D viewport; Vector2D viewport;
@ -63,5 +66,7 @@ class CPasswordInputField : public IWidget {
bool enabled = false; bool enabled = false;
} hiddenInputState; } hiddenInputState;
bool fadeOnEmpty; bool fadeOnEmpty;
CShadowable shadow;
}; };

View File

@ -0,0 +1,36 @@
#include "Shadowable.hpp"
#include "../Renderer.hpp"
CShadowable::CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) : widget(widget_), viewport(viewport_) {
size = std::any_cast<Hyprlang::INT>(props.at("shadow_size"));
passes = std::any_cast<Hyprlang::INT>(props.at("shadow_passes"));
color = std::any_cast<Hyprlang::INT>(props.at("shadow_color"));
boostA = std::any_cast<Hyprlang::FLOAT>(props.at("shadow_boost"));
}
void CShadowable::markShadowDirty() {
if (!shadowFB.isAllocated())
shadowFB.alloc(viewport.x, viewport.y, true);
g_pRenderer->pushFb(shadowFB.m_iFb);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
ignoreDraw = true;
widget->draw(IWidget::SRenderData{.opacity = 1.0});
ignoreDraw = false;
g_pRenderer->blurFB(shadowFB, CRenderer::SBlurParams{.size = size, .passes = passes, .colorize = color, .boostA = boostA});
g_pRenderer->popFb();
}
bool CShadowable::draw(const IWidget::SRenderData& data) {
if (!shadowFB.isAllocated() || ignoreDraw)
return true;
CBox box = {0, 0, viewport.x, viewport.y};
g_pRenderer->renderTexture(box, shadowFB.m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL);
return true;
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "../Framebuffer.hpp"
#include "../Texture.hpp"
#include "../../helpers/Color.hpp"
#include "IWidget.hpp"
#include <string>
#include <unordered_map>
#include <any>
class CShadowable {
public:
CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */);
// instantly re-renders the shadow using the widget's draw() method
void markShadowDirty();
virtual bool draw(const IWidget::SRenderData& data);
private:
IWidget* widget = nullptr;
int size = 10;
int passes = 4;
float boostA = 1.0;
CColor color{0, 0, 0, 1.0};
Vector2D viewport;
// to avoid recursive shadows
bool ignoreDraw = false;
CFramebuffer shadowFB;
};