diff --git a/README.md b/README.md index 4715597d..633b8339 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Nevertheless, REPORT any you find! Make an issue! - Config reloaded instantly upon saving - Easily expandable and readable codebase - Rounded corners + - Window blur - Fade in/out - Support for docks/whatever - Window rules @@ -38,7 +39,6 @@ Nevertheless, REPORT any you find! Make an issue! - Damage tracking - Animations (better) - Fix GDK popups on multimon - - Blur - Fix electron rendering issues - Optimization - Fix weird scroll on XWayland diff --git a/example/hyprland.conf b/example/hyprland.conf index 2e1425a5..db239c18 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -28,6 +28,8 @@ general { decoration { rounding=10 + blur=1 + blur_size=8 # minimum 2 } animations { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 39e9409e..5d866b0b 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -23,6 +23,8 @@ CConfigManager::CConfigManager() { configValues["general:col.inactive_border"].intValue = 0xff444444; configValues["decoration:rounding"].intValue = 1; + configValues["decoration:blur"].intValue = 1; + configValues["decoration:blur_size"].intValue = 8; configValues["dwindle:pseudotile"].intValue = 0; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 5f5818fb..26e18970 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -50,6 +50,22 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { m_shEXT.posAttrib = glGetAttribLocation(prog, "pos"); m_shEXT.texAttrib = glGetAttribLocation(prog, "texcoord"); + prog = createProgram(TEXVERTSRC, FRAGBLUR1); + m_shBLUR1.program = prog; + m_shBLUR1.tex = glGetUniformLocation(prog, "tex"); + m_shBLUR1.alpha = glGetUniformLocation(prog, "alpha"); + m_shBLUR1.proj = glGetUniformLocation(prog, "proj"); + m_shBLUR1.posAttrib = glGetAttribLocation(prog, "pos"); + m_shBLUR1.texAttrib = glGetAttribLocation(prog, "texcoord"); + + prog = createProgram(TEXVERTSRC, FRAGBLUR2); + m_shBLUR2.program = prog; + m_shBLUR2.tex = glGetUniformLocation(prog, "tex"); + m_shBLUR2.alpha = glGetUniformLocation(prog, "alpha"); + m_shBLUR2.proj = glGetUniformLocation(prog, "proj"); + m_shBLUR2.posAttrib = glGetAttribLocation(prog, "pos"); + m_shBLUR2.texAttrib = glGetAttribLocation(prog, "texcoord"); + Debug::log(LOG, "Shaders initialized successfully."); // End shaders @@ -259,6 +275,97 @@ void CHyprOpenGLImpl::renderTexture(const CTexture& tex, float matrix[9], float glBindTexture(tex.m_iTarget, 0); } +// This is probably not the quickest method possible, +// feel free to contribute if you have a better method. +// cheers. + +// 2-pass pseudo-gaussian blur +void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, float matrix[9], float a, int round) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!"); + + // if blur disabled, just render the texture + if (g_pConfigManager->getInt("decoration:blur") == 0) { + renderTexture(tex, matrix, a, round); + return; + } + + // create a stencil for our thang + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + // we clear the stencil and then draw our texture with GL_ALWAYS to make a stencil mask. + glClear(GL_STENCIL_BUFFER_BIT); + + glStencilMask(0xFF); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); + + renderTexture(tex, matrix, 0, round); // 0 alpha because we dont want to draw to the FB + + // then we disable writing to the mask and ONLY accept writing within the stencil + glStencilMask(0x00); + glStencilFunc(GL_EQUAL, 1, 0xFF); + + glEnable(GL_BLEND); + + // now we make the blur by blurring the main framebuffer (it will only affect the stencil) + + // matrix + const auto TRANSFORM = wlr_output_transform_invert(WL_OUTPUT_TRANSFORM_NORMAL); + float matrixFull[9]; + wlr_box fullMonBox = {0, 0, m_RenderData.pMonitor->vecSize.x, m_RenderData.pMonitor->vecSize.y}; + wlr_matrix_project_box(matrixFull, &fullMonBox, TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix); + + float glMatrix[9]; + wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrixFull); + wlr_matrix_multiply(glMatrix, matrixFlip180, glMatrix); + + wlr_matrix_transpose(glMatrix, glMatrix); + + const auto RADIUS = g_pConfigManager->getInt("decoration:blur_size") + 2; + const auto PFRAMEBUFFER = &m_mMonitorFramebuffers[m_RenderData.pMonitor]; + + auto drawWithShader = [=](CShader* pShader) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(PFRAMEBUFFER->m_cTex.m_iTarget, PFRAMEBUFFER->m_cTex.m_iTexID); + + glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glUseProgram(pShader->program); + + glUniform1f(glGetUniformLocation(pShader->program, "radius"), RADIUS); + glUniform2f(glGetUniformLocation(pShader->program, "resolution"), m_RenderData.pMonitor->vecSize.x, m_RenderData.pMonitor->vecSize.y); + glUniformMatrix3fv(pShader->proj, 1, GL_FALSE, glMatrix); + glUniform1i(pShader->tex, 0); + glUniform1f(pShader->alpha, a / 255.f); + + glVertexAttribPointer(pShader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(pShader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(pShader->posAttrib); + glEnableVertexAttribArray(pShader->texAttrib); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(pShader->posAttrib); + glDisableVertexAttribArray(pShader->texAttrib); + }; + + drawWithShader(&m_shBLUR1); // horizontal pass + drawWithShader(&m_shBLUR2); // vertical pass + + glBindTexture(tex.m_iTarget, 0); + + // when the blur is done, let's render the window itself + renderTexture(tex, matrix, a, round); + + // and disable the stencil + glStencilMask(0xFF); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glDisable(GL_STENCIL_TEST); +} + void pushVert2D(float x, float y, float* arr, int& counter, wlr_box* box) { // 0-1 space god damnit arr[counter * 2 + 0] = x / box->width; diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 7792a1fc..bc2c6d88 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -40,6 +40,7 @@ public: void renderRect(wlr_box*, const CColor&); void renderTexture(wlr_texture*, float matrix[9], float a, int round = 0); void renderTexture(const CTexture&, float matrix[9], float a, int round = 0); + void renderTextureWithBlur(const CTexture&, float matrix[9], float a, int round = 0); void renderBorder(wlr_box*, const CColor&, int thick = 1, int round = 0); void makeWindowSnapshot(CWindow*); @@ -68,6 +69,8 @@ private: CShader m_shRGBA; CShader m_shRGBX; CShader m_shEXT; + CShader m_shBLUR1; + CShader m_shBLUR2; // GLuint createProgram(const std::string&, const std::string&); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 197b29b1..b47a74b9 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -31,7 +31,10 @@ void renderSurface(struct wlr_surface* surface, int x, int y, void* data) { float matrix[9]; wlr_matrix_project_box(matrix, &windowBox, TRANSFORM, 0, RDATA->output->transform_matrix); - g_pHyprOpenGL->renderTexture(TEXTURE, matrix, RDATA->fadeAlpha, RDATA->dontRound ? 0 : g_pConfigManager->getInt("decoration:rounding")); // TODO: fadeinout + if (RDATA->surface && surface == RDATA->surface) + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, matrix, RDATA->fadeAlpha, RDATA->dontRound ? 0 : g_pConfigManager->getInt("decoration:rounding")); + else + g_pHyprOpenGL->renderTexture(TEXTURE, matrix, RDATA->fadeAlpha, RDATA->dontRound ? 0 : g_pConfigManager->getInt("decoration:rounding")); wlr_surface_send_frame_done(surface, RDATA->when); diff --git a/src/render/Shaders.hpp b/src/render/Shaders.hpp index b8239c57..09d4d504 100644 --- a/src/render/Shaders.hpp +++ b/src/render/Shaders.hpp @@ -138,6 +138,107 @@ void main() { gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; })#"; +// thanks to Loadus +// https://www.shadertoy.com/view/Mtl3Rj +// for this pseudo-gaussian blur! +inline const std::string FRAGBLUR1 = R"#( +precision mediump float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float radius; +uniform vec2 resolution; +uniform float alpha; + +float SCurve (float x) { + x = x * 2.0 - 1.0; + return -x * abs(x) * 0.5 + x + 0.5; +} + +vec4 BlurH (vec2 size, vec2 uv) { + if (radius >= 1.0) { + vec4 A = vec4(0.0); + vec4 C = vec4(0.0); + + float width = 1.0 / size.x; + + float divisor = 0.0; + float weight = 0.0; + + float radiusMultiplier = 1.0 / radius; + + for (float x = -radius; x <= radius; x++) { + A = texture2D(tex, uv + vec2(x * width, 0.0)); + + weight = SCurve(1.0 - (abs(x) * radiusMultiplier)); + + C += A * weight; + + divisor += weight; + } + + return vec4(C.r / divisor, C.g / divisor, C.b / divisor, 1.0); + } + + return texture2D(tex, uv); +} + + +void main() { + gl_FragColor = BlurH(resolution, v_texcoord) * alpha; +} + +)#"; + +inline const std::string FRAGBLUR2 = R"#( +precision mediump float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float radius; +uniform vec2 resolution; +uniform float alpha; + +float SCurve (float x) { + x = x * 2.0 - 1.0; + return -x * abs(x) * 0.5 + x + 0.5; +} + +vec4 BlurV (vec2 size, vec2 uv) { + if (radius >= 1.0) { + vec4 A = vec4(0.0); + vec4 C = vec4(0.0); + + float height = 1.0 / size.y; + + float divisor = 0.0; + float weight = 0.0; + + float radiusMultiplier = 1.0 / radius; + + for (float y = -radius; y <= radius; y++) { + A = texture2D(tex, uv + vec2(0.0, y * height)); + + weight = SCurve(1.0 - (abs(y) * radiusMultiplier)); + + C += A * weight; + + divisor += weight; + } + + return vec4(C.r / divisor, C.g / divisor, C.b / divisor, 1.0); + } + + return texture2D(tex, uv); +} + + +void main() { + gl_FragColor = BlurV(resolution, v_texcoord) * alpha; +} + +)#"; + inline const std::string TEXFRAGSRCEXT = R"#( #extension GL_OES_EGL_image_external : require @@ -189,4 +290,4 @@ void main() { } gl_FragColor = texture2D(texture0, v_texcoord) * alpha; -})#"; \ No newline at end of file +})#";