From a4dc596cb8b4e2f65247bd5f884ab9dbae100f1d Mon Sep 17 00:00:00 2001 From: Vaxry Date: Tue, 5 Mar 2024 20:24:11 +0000 Subject: [PATCH] renderer: add shadowable props --- src/config/ConfigManager.cpp | 19 +++++++++ src/renderer/Framebuffer.cpp | 6 +-- src/renderer/Framebuffer.hpp | 2 +- src/renderer/Renderer.cpp | 43 +++++++++++++++------ src/renderer/Renderer.hpp | 12 +++++- src/renderer/Shader.hpp | 5 +++ src/renderer/Shaders.hpp | 11 ++++++ src/renderer/widgets/Label.cpp | 7 +++- src/renderer/widgets/Label.hpp | 3 ++ src/renderer/widgets/PasswordInputField.cpp | 11 +++++- src/renderer/widgets/PasswordInputField.hpp | 7 +++- src/renderer/widgets/Shadowable.cpp | 36 +++++++++++++++++ src/renderer/widgets/Shadowable.hpp | 32 +++++++++++++++ 13 files changed, 173 insertions(+), 21 deletions(-) create mode 100644 src/renderer/widgets/Shadowable.cpp create mode 100644 src/renderer/widgets/Shadowable.hpp diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 59c473c..3f69749 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -35,6 +35,13 @@ CConfigManager::CConfigManager() : m_config(getMainConfigPath().c_str(), Hyprlan } 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:hide_cursor", 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{"Input Password"}); m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0}); m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1}); + SHADOWABLE("input-field"); m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); 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", "halign", Hyprlang::STRING{"none"}); m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"}); + SHADOWABLE("label"); m_config.registerHandler(&::handleSource, "source", {false}); @@ -89,6 +98,8 @@ void CConfigManager::init() { if (result.error) Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); + +#undef SHADOWABLE } std::mutex configMtx; @@ -101,6 +112,12 @@ void* const* CConfigManager::getValuePtr(const std::string& name) { std::vector CConfigManager::getWidgetConfigs() { std::vector 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"); for (auto& k : keys) { @@ -146,6 +163,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", 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())}, + SHADOWABLE("input-field"), } }); // clang-format on @@ -165,6 +183,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"text", m_config.getSpecialConfigValue("label", "text", k.c_str())}, {"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())}, {"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())}, + SHADOWABLE("label"), } }); // clang-format on diff --git a/src/renderer/Framebuffer.cpp b/src/renderer/Framebuffer.cpp index 0b3cb62..75de303 100644 --- a/src/renderer/Framebuffer.cpp +++ b/src/renderer/Framebuffer.cpp @@ -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; } -bool CFramebuffer::alloc(int w, int h) { +bool CFramebuffer::alloc(int w, int h, bool highres) { 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 glType = glFormatToType(glFormat); + 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 = highres ? GL_FLOAT : glFormatToType(glFormat); if (m_iFb == (uint32_t)-1) { firstAlloc = true; diff --git a/src/renderer/Framebuffer.hpp b/src/renderer/Framebuffer.hpp index 76858fc..07fd772 100644 --- a/src/renderer/Framebuffer.hpp +++ b/src/renderer/Framebuffer.hpp @@ -8,7 +8,7 @@ class CFramebuffer { public: ~CFramebuffer(); - bool alloc(int w, int h); + bool alloc(int w, int h, bool highres = false); void addStencil(); void bind() const; void release(); diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index 56765c4..af8c042 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -138,14 +138,17 @@ CRenderer::CRenderer() { blurPrepareShader.contrast = glGetUniformLocation(prog, "contrast"); blurPrepareShader.brightness = glGetUniformLocation(prog, "brightness"); - prog = createProgram(TEXVERTSRC, FRAGBLURFINISH); - blurFinishShader.program = prog; - blurFinishShader.tex = glGetUniformLocation(prog, "tex"); - blurFinishShader.proj = glGetUniformLocation(prog, "proj"); - blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos"); - blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord"); - blurFinishShader.brightness = glGetUniformLocation(prog, "brightness"); - blurFinishShader.noise = glGetUniformLocation(prog, "noise"); + prog = createProgram(TEXVERTSRC, FRAGBLURFINISH); + blurFinishShader.program = prog; + blurFinishShader.tex = glGetUniformLocation(prog, "tex"); + blurFinishShader.proj = glGetUniformLocation(prog, "proj"); + blurFinishShader.posAttrib = glGetAttribLocation(prog, "pos"); + blurFinishShader.texAttrib = glGetAttribLocation(prog, "texcoord"); + blurFinishShader.brightness = glGetUniformLocation(prog, "brightness"); + 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()); @@ -164,6 +167,10 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf g_pEGL->makeCurrent(surf.eglSurface); 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); glClear(GL_COLOR_BUFFER_BIT); @@ -330,8 +337,8 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) { wlr_matrix_multiply(glMatrix, projection.data(), matrix); CFramebuffer mirrors[2]; - mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y); - mirrors[1].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, true); CFramebuffer* currentRenderToFB = &mirrors[0]; @@ -442,6 +449,10 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) { glUniformMatrix3fv(blurFinishShader.proj, 1, GL_TRUE, glMatrix); glUniform1f(blurFinishShader.noise, params.noise); 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); @@ -467,4 +478,14 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) { renderTexture(box, currentRenderToFB->m_cTex, 1.0, 0, WL_OUTPUT_TRANSFORM_NORMAL); glEnable(GL_BLEND); -} \ No newline at end of file +} + +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()); +} diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index 30fb931..6599740 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "../core/LockSurface.hpp" #include "Shader.hpp" @@ -22,8 +23,10 @@ class CRenderer { }; struct SBlurParams { - int size = 0, passes = 0; - float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0; + int size = 0, passes = 0; + float noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0; + std::optional colorize; + float boostA = 1.0; }; SRenderFeedback renderLock(const CSessionLockSurface& surface); @@ -35,6 +38,9 @@ class CRenderer { std::unique_ptr asyncResourceGatherer; std::chrono::system_clock::time_point gatheredAt; + void pushFb(GLint fb); + void popFb(); + private: widgetMap_t widgets; @@ -49,6 +55,8 @@ class CRenderer { std::array projMatrix; std::array projection; + + std::vector boundFBs; }; inline std::unique_ptr g_pRenderer; \ No newline at end of file diff --git a/src/renderer/Shader.hpp b/src/renderer/Shader.hpp index e123632..bb1d5e8 100644 --- a/src/renderer/Shader.hpp +++ b/src/renderer/Shader.hpp @@ -59,6 +59,11 @@ class CShader { GLint brightness = -1; GLint noise = -1; + // colorize + GLint colorize = -1; + GLint colorizeTint = -1; + GLint boostA = -1; + GLint getUniformLocation(const std::string&); void destroy(); diff --git a/src/renderer/Shaders.hpp b/src/renderer/Shaders.hpp index de41f3a..2710051 100644 --- a/src/renderer/Shaders.hpp +++ b/src/renderer/Shaders.hpp @@ -331,6 +331,10 @@ uniform sampler2D tex; uniform float noise; uniform float brightness; +uniform int colorize; +uniform vec3 colorizeTint; +uniform float boostA; + float hash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); } @@ -348,6 +352,13 @@ void main() { pixColor.rgb *= brightness; } + pixColor.a *= boostA; + + if (colorize == 1) { + gl_FragColor = vec4(colorizeTint.r, colorizeTint.g, colorizeTint.b, pixColor.a); + return; + } + gl_FragColor = pixColor; } )#"; \ No newline at end of file diff --git a/src/renderer/widgets/Label.cpp b/src/renderer/widgets/Label.cpp index 3e6d54c..5c2a05a 100644 --- a/src/renderer/widgets/Label.cpp +++ b/src/renderer/widgets/Label.cpp @@ -52,7 +52,8 @@ void CLabel::plantTimer() { labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this); } -CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, CSessionLockSurface* surface_) : surface(surface_) { +CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map& props, CSessionLockSurface* surface_) : + surface(surface_), shadow(this, props, viewport_) { labelPreFormat = std::any_cast(props.at("text")); std::string fontFamily = std::any_cast(props.at("font_family")); CColor labelColor = std::any_cast(props.at("color")); @@ -92,6 +93,7 @@ bool CLabel::draw(const SRenderData& data) { // calc pos pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign); + shadow.markShadowDirty(); } if (!pendingResourceID.empty()) { @@ -104,11 +106,12 @@ bool CLabel::draw(const SRenderData& data) { resourceID = pendingResourceID; pendingResourceID = ""; 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}; - + shadow.draw(data); g_pRenderer->renderTexture(box, asset->texture, data.opacity); return false; diff --git a/src/renderer/widgets/Label.hpp b/src/renderer/widgets/Label.hpp index 890390e..b3dd6f0 100644 --- a/src/renderer/widgets/Label.hpp +++ b/src/renderer/widgets/Label.hpp @@ -1,6 +1,7 @@ #pragma once #include "IWidget.hpp" +#include "Shadowable.hpp" #include "../../helpers/Vector2D.hpp" #include "../../core/Timer.hpp" #include "../AsyncResourceGatherer.hpp" @@ -38,4 +39,6 @@ class CLabel : public IWidget { CAsyncResourceGatherer::SPreloadRequest request; std::shared_ptr labelTimer; + + CShadowable shadow; }; \ No newline at end of file diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index 41d08c4..fd09a7e 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -3,7 +3,7 @@ #include "../../core/hyprlock.hpp" #include -CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props) { +CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map& props) : shadow(this, props, viewport_) { size = std::any_cast(props.at("size")); inner = std::any_cast(props.at("inner_color")); outer = std::any_cast(props.at("outer_color")); @@ -105,6 +105,11 @@ bool CPasswordInputField::draw(const SRenderData& data) { CBox inputFieldBox = {pos, size}; CBox outerBox = {pos - Vector2D{outThick, outThick}, size + Vector2D{outThick * 2, outThick * 2}}; + if (firstRender) { + firstRender = false; + shadow.markShadowDirty(); + } + bool forceReload = false; updateFade(); @@ -112,6 +117,10 @@ bool CPasswordInputField::draw(const SRenderData& data) { updateFailTex(); updateHiddenInputState(); + SRenderData shadowData = data; + shadowData.opacity *= fade.a; + shadow.draw(shadowData); + float passAlpha = g_pHyprlock->passwordCheckWaiting() ? 0.5 : 1.0; CColor outerCol = outer; diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 49c51d3..433e114 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -3,6 +3,7 @@ #include "IWidget.hpp" #include "../../helpers/Vector2D.hpp" #include "../../helpers/Color.hpp" +#include "Shadowable.hpp" #include #include #include @@ -22,6 +23,8 @@ class CPasswordInputField : public IWidget { void updateFailTex(); void updateHiddenInputState(); + bool firstRender = true; + Vector2D size; Vector2D pos; Vector2D viewport; @@ -63,5 +66,7 @@ class CPasswordInputField : public IWidget { bool enabled = false; } hiddenInputState; - bool fadeOnEmpty; + bool fadeOnEmpty; + + CShadowable shadow; }; diff --git a/src/renderer/widgets/Shadowable.cpp b/src/renderer/widgets/Shadowable.cpp new file mode 100644 index 0000000..c2fbcd0 --- /dev/null +++ b/src/renderer/widgets/Shadowable.cpp @@ -0,0 +1,36 @@ +#include "Shadowable.hpp" +#include "../Renderer.hpp" + +CShadowable::CShadowable(IWidget* widget_, const std::unordered_map& props, const Vector2D& viewport_) : widget(widget_), viewport(viewport_) { + size = std::any_cast(props.at("shadow_size")); + passes = std::any_cast(props.at("shadow_passes")); + color = std::any_cast(props.at("shadow_color")); + boostA = std::any_cast(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; +} \ No newline at end of file diff --git a/src/renderer/widgets/Shadowable.hpp b/src/renderer/widgets/Shadowable.hpp new file mode 100644 index 0000000..5dafb68 --- /dev/null +++ b/src/renderer/widgets/Shadowable.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "../Framebuffer.hpp" +#include "../Texture.hpp" +#include "../../helpers/Color.hpp" +#include "IWidget.hpp" + +#include +#include +#include + +class CShadowable { + public: + CShadowable(IWidget* widget_, const std::unordered_map& 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; +}; \ No newline at end of file