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() {
#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{"<i>Input Password</i>"});
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::SWidgetConfig> CConfigManager::getWidgetConfigs() {
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");
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())},
{"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::SWidgetConfig> 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

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;
}
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;

View File

@ -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();

View File

@ -146,6 +146,9 @@ CRenderer::CRenderer() {
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);
@ -468,3 +479,13 @@ void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
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 <chrono>
#include <optional>
#include "../core/LockSurface.hpp"
#include "Shader.hpp"
@ -24,6 +25,8 @@ class CRenderer {
struct SBlurParams {
int size = 0, passes = 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);
@ -35,6 +38,9 @@ class CRenderer {
std::unique_ptr<CAsyncResourceGatherer> 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<float, 9> projMatrix;
std::array<float, 9> projection;
std::vector<GLint> boundFBs;
};
inline std::unique_ptr<CRenderer> g_pRenderer;

View File

@ -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();

View File

@ -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;
}
)#";

View File

@ -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<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"));
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
CColor labelColor = std::any_cast<Hyprlang::INT>(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;

View File

@ -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<CTimer> labelTimer;
CShadowable shadow;
};

View File

@ -3,7 +3,7 @@
#include "../../core/hyprlock.hpp"
#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"));
inner = std::any_cast<Hyprlang::INT>(props.at("inner_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 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;

View File

@ -3,6 +3,7 @@
#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp"
#include "Shadowable.hpp"
#include <chrono>
#include <vector>
#include <any>
@ -22,6 +23,8 @@ class CPasswordInputField : public IWidget {
void updateFailTex();
void updateHiddenInputState();
bool firstRender = true;
Vector2D size;
Vector2D pos;
Vector2D viewport;
@ -64,4 +67,6 @@ class CPasswordInputField : public IWidget {
} hiddenInputState;
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;
};