From 7b020ffa84e64d0734709b2489e1da5463689e61 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Thu, 1 Dec 2022 13:36:07 +0000 Subject: [PATCH] Added screen shaders --- example/screenShader.frag | 16 ++++ src/config/ConfigManager.cpp | 21 ++++++ src/render/OpenGL.cpp | 141 ++++++++++++++++++++++++++--------- src/render/OpenGL.hpp | 11 ++- src/render/Shader.cpp | 6 ++ src/render/Shader.hpp | 2 + 6 files changed, 158 insertions(+), 39 deletions(-) create mode 100644 example/screenShader.frag diff --git a/example/screenShader.frag b/example/screenShader.frag new file mode 100644 index 00000000..5eeac17a --- /dev/null +++ b/example/screenShader.frag @@ -0,0 +1,16 @@ +// +// Example blue light filter shader. +// + +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + pixColor[2] *= 0.8; + + gl_FragColor = pixColor; +} diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 8834aff1..c9890d17 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -92,6 +92,7 @@ void CConfigManager::setDefaultVars() { configValues["decoration:col.shadow_inactive"].intValue = INT_MAX; configValues["decoration:dim_inactive"].intValue = 0; configValues["decoration:dim_strength"].floatValue = 0.5f; + configValues["decoration:screen_shader"].strValue = STRVAL_EMPTY; configValues["dwindle:pseudotile"].intValue = 0; configValues["dwindle:col.group_border"].intValue = 0x66777700; @@ -403,6 +404,21 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s } } } + + if (COMMAND == "decoration:screen_shader") { + const auto PATH = absolutePath(VALUE, configCurrentPath); + + configPaths.push_back(PATH); + + struct stat fileStat; + int err = stat(PATH.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at %s, error %i: %s", PATH.c_str(), err, strerror(err)); + return; + } + + configModifyTimes[PATH] = fileStat.st_mtime; + } } void CConfigManager::handleRawExec(const std::string& command, const std::string& args) { @@ -1230,6 +1246,9 @@ void CConfigManager::loadConfigLoadVars() { // Calculate the internal vars configValues["general:main_mod_internal"].intValue = g_pKeybindManager->stringToModMask(configValues["general:main_mod"].strValue); + if (!isFirstLaunch) + g_pHyprOpenGL->m_bReloadScreenShader = true; + // parseError will be displayed next frame if (parseError != "") g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(255, 50, 50, 255)); @@ -1652,6 +1671,8 @@ SAnimationPropertyConfig* CConfigManager::getAnimationPropertyConfig(const std:: void CConfigManager::addParseError(const std::string& err) { if (parseError == "") parseError = err; + + g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(255, 50, 50, 255)); } CMonitor* CConfigManager::getBoundMonitorForWS(std::string wsname) { diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index bee6449b..b3914fde 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -30,12 +30,22 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), "Couldn't unset current EGL!"); } -GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag) { +GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic) { auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert); - RASSERT(vertCompiled, "Compiling shader failed. VERTEX NULL! Shader source:\n\n%s", vert.c_str()); + if (dynamic) { + if (vertCompiled == 0) + return 0; + } else { + RASSERT(vertCompiled, "Compiling shader failed. VERTEX NULL! Shader source:\n\n%s", vert.c_str()); + } auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag); - RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT NULL! Shader source:\n\n%s", frag.c_str()); + if (dynamic) { + if (fragCompiled == 0) + return 0; + } else { + RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT NULL! Shader source:\n\n%s", frag.c_str()); + } auto prog = glCreateProgram(); glAttachShader(prog, vertCompiled); @@ -49,12 +59,17 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string GLint ok; glGetProgramiv(prog, GL_LINK_STATUS, &ok); - RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!"); + if (dynamic) { + if (ok == GL_FALSE) + return 0; + } else { + RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!"); + } return prog; } -GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src) { +GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic) { auto shader = glCreateShader(type); auto shaderSource = src.c_str(); @@ -64,7 +79,12 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src) { GLint ok; glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); - RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!"); + if (dynamic) { + if (ok == GL_FALSE) + return 0; + } else { + RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!"); + } return shader; } @@ -112,6 +132,11 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, pixman_region32_t* pDamage, bool m_RenderData.pDamage = pDamage; m_bFakeFrame = fake; + + if (m_bReloadScreenShader) { + m_bReloadScreenShader = false; + applyScreenShader(g_pConfigManager->getString("decoration:screen_shader")); + } } void CHyprOpenGLImpl::end() { @@ -128,9 +153,11 @@ void CHyprOpenGLImpl::end() { clear(CColor(11, 11, 11, 255)); m_bEndFrame = true; + m_bApplyFinalShader = true; renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 255.f, 0); + m_bApplyFinalShader = false; m_bEndFrame = false; } @@ -250,6 +277,35 @@ void CHyprOpenGLImpl::initShaders() { Debug::log(LOG, "Shaders initialized successfully."); } +void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { + + m_sFinalScreenShader.destroy(); + + if (path == "" || path == STRVAL_EMPTY) + return; + + std::ifstream infile(absolutePath(path, std::string(getenv("HOME")) + "/.config/hypr")); + + if (!infile.good()) { + g_pConfigManager->addParseError("Screen shader parser: Screen shader path not found"); + return; + } + + std::string fragmentShader((std::istreambuf_iterator(infile)), (std::istreambuf_iterator())); + + m_sFinalScreenShader.program = createProgram(TEXVERTSRC, fragmentShader, true); + + if (!m_sFinalScreenShader.program) { + g_pConfigManager->addParseError("Screen shader parser: Screen shader parse failed"); + return; + } + + m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj"); + m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex"); + m_sFinalScreenShader.texAttrib = glGetAttribLocation(m_sFinalScreenShader.program, "texcoord"); + m_sFinalScreenShader.posAttrib = glGetAttribLocation(m_sFinalScreenShader.program, "pos"); +} + void CHyprOpenGLImpl::clear(const CColor& color) { RASSERT(m_RenderData.pMonitor, "Tried to render without begin()!"); @@ -420,18 +476,25 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - switch (tex.m_iType) { - case TEXTURE_RGBA: - shader = &m_RenderData.pCurrentMonData->m_shRGBA; - break; - case TEXTURE_RGBX: - shader = &m_RenderData.pCurrentMonData->m_shRGBX; - break; - case TEXTURE_EXTERNAL: - shader = &m_RenderData.pCurrentMonData->m_shEXT; - break; - default: - RASSERT(false, "tex.m_iTarget unsupported!"); + bool usingFinalShader = false; + + if (m_bApplyFinalShader && m_sFinalScreenShader.program) { + shader = &m_sFinalScreenShader; + usingFinalShader = true; + } else { + switch (tex.m_iType) { + case TEXTURE_RGBA: + shader = &m_RenderData.pCurrentMonData->m_shRGBA; + break; + case TEXTURE_RGBX: + shader = &m_RenderData.pCurrentMonData->m_shRGBX; + break; + case TEXTURE_EXTERNAL: + shader = &m_RenderData.pCurrentMonData->m_shEXT; + break; + default: + RASSERT(false, "tex.m_iTarget unsupported!"); + } } glActiveTexture(GL_TEXTURE0); @@ -448,8 +511,10 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix); #endif glUniform1i(shader->tex, 0); - glUniform1f(shader->alpha, alpha / 255.f); - glUniform1i(shader->discardOpaque, (int)discardOpaque); + if (!usingFinalShader) { + glUniform1f(shader->alpha, alpha / 255.f); + glUniform1i(shader->discardOpaque, (int)discardOpaque); + } wlr_box transformedBox; wlr_box_transform(&transformedBox, pBox, wlr_output_transform_invert(m_RenderData.pMonitor->transform), @@ -459,18 +524,20 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); static auto *const PMULTISAMPLEEDGES = &g_pConfigManager->getConfigValuePtr("decoration:multisample_edges")->intValue; - // Rounded corners - glUniform2f(shader->topLeft, TOPLEFT.x, TOPLEFT.y); - glUniform2f(shader->fullSize, FULLSIZE.x ,FULLSIZE.y); - glUniform1f(shader->radius, round); - glUniform1i(shader->primitiveMultisample, (int)(*PMULTISAMPLEEDGES == 1 && round != 0 && !noAA)); + if (!usingFinalShader) { + // Rounded corners + glUniform2f(shader->topLeft, TOPLEFT.x, TOPLEFT.y); + glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y); + glUniform1f(shader->radius, round); + glUniform1i(shader->primitiveMultisample, (int)(*PMULTISAMPLEEDGES == 1 && round != 0 && !noAA)); - if (allowDim && m_pCurrentWindow && *PDIMINACTIVE && m_pCurrentWindow != g_pCompositor->m_pLastWindow) { - glUniform1i(shader->applyTint, 1); - const auto DIM = m_pCurrentWindow->m_fDimPercent.fl(); - glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM); - } else { - glUniform1i(shader->applyTint, 0); + if (allowDim && m_pCurrentWindow && *PDIMINACTIVE && m_pCurrentWindow != g_pCompositor->m_pLastWindow) { + glUniform1i(shader->applyTint, 1); + const auto DIM = m_pCurrentWindow->m_fDimPercent.fl(); + glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM); + } else { + glUniform1i(shader->applyTint, 0); + } } const float verts[] = { @@ -504,13 +571,13 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b } } - pixman_region32_fini(&damageClip); + pixman_region32_fini(&damageClip); } else { - PIXMAN_DAMAGE_FOREACH(damage) { - const auto RECT = RECTSARR[i]; - scissor(&RECT); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } + PIXMAN_DAMAGE_FOREACH(damage) { + const auto RECT = RECTSARR[i]; + scissor(&RECT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } } glDisableVertexAttribArray(shader->posAttrib); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index e54b76aa..fabd3666 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -111,11 +111,15 @@ public: void onWindowResizeStart(CWindow*); void onWindowResizeEnd(CWindow*); + void applyScreenShader(const std::string& path); + SCurrentRenderData m_RenderData; GLint m_iCurrentOutputFb = 0; GLint m_iWLROutputFb = 0; + bool m_bReloadScreenShader = true; // at launch it can be set + CWindow* m_pCurrentWindow = nullptr; // hack to get the current rendered window pixman_region32_t m_rOriginalDamageRegion; // used for storing the pre-expanded region @@ -134,9 +138,12 @@ private: bool m_bFakeFrame = false; bool m_bEndFrame = false; + bool m_bApplyFinalShader = false; - GLuint createProgram(const std::string&, const std::string&); - GLuint compileShader(const GLuint&, std::string); + CShader m_sFinalScreenShader; + + GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); + GLuint compileShader(const GLuint&, std::string, bool dynamic = false); void createBGTextureForMonitor(CMonitor*); void initShaders(); diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index f965e681..03b13055 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -14,5 +14,11 @@ GLint CShader::getUniformLocation(const std::string& unif) { CShader::~CShader() { // destroy shader + destroy(); + + program = 0; +} + +void CShader::destroy() { glDeleteProgram(program); } \ No newline at end of file diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index c9c63725..f107bb6d 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -39,6 +39,8 @@ public: GLint getUniformLocation(const std::string&); + void destroy(); + private: std::unordered_map m_muUniforms; }; \ No newline at end of file