From d8b865366af9d5ed30d2ee0a437b9a3ed43c10bd Mon Sep 17 00:00:00 2001 From: Vaxry Date: Fri, 1 Nov 2024 15:52:03 +0000 Subject: [PATCH] renderer: Add a missing texture asset and a user check When an asset is missing, instead of a black screen, render an obnoxious, yet standard, missing texture. Additionally, warn the user assets failed to load. Shoutout to Arch for having their assets broken for months. Fix your shit. I am tired of it, and it's negatively impacting users. --- src/Compositor.cpp | 6 ++ src/render/Framebuffer.cpp | 15 +++- src/render/OpenGL.cpp | 155 +++++++++++++++++++++++++------------ src/render/OpenGL.hpp | 14 ++-- src/render/Renderer.cpp | 20 ++--- 5 files changed, 139 insertions(+), 71 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 65891633..62612398 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2738,6 +2738,12 @@ void CCompositor::performUserChecks() { CColor{}, 15000, ICON_WARNING); } } + + if (g_pHyprOpenGL->failedAssetsNo > 0) { + g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!", + g_pHyprOpenGL->failedAssetsNo, g_pHyprOpenGL->failedAssetsNo > 1 ? "s" : ""), + CColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR); + } } void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) { diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index c48ff6f3..7e086778 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -2,7 +2,7 @@ #include "OpenGL.hpp" CFramebuffer::CFramebuffer() { - m_cTex = makeShared(); + ; } bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { @@ -12,6 +12,9 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { uint32_t glFormat = FormatUtils::drmFormatToGL(drmFormat); uint32_t glType = FormatUtils::glFormatToType(glFormat); + if (!m_cTex) + m_cTex = makeShared(); + if (!m_iFbAllocated) { firstAlloc = true; glGenFramebuffers(1, &m_iFb); @@ -54,7 +57,8 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { } glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb); + if (g_pHyprOpenGL) + glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb); m_vSize = Vector2D(w, h); @@ -85,14 +89,17 @@ void CFramebuffer::bind() { #else glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); #endif - glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y); + if (g_pHyprOpenGL) + glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y); + else + glViewport(0, 0, m_vSize.x, m_vSize.y); } void CFramebuffer::release() { if (m_iFbAllocated) glDeleteFramebuffers(1, &m_iFb); - m_cTex->destroyTexture(); + m_cTex.reset(); m_iFbAllocated = false; m_vSize = Vector2D(); } diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 9e524fa4..20963707 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -13,6 +13,13 @@ #include #include +const std::vector ASSET_PATHS = { +#ifdef DATAROOTDIR + DATAROOTDIR, +#endif + "/usr/share", +}; + inline void loadGLProc(void* pProc, const char* name) { void* proc = (void*)eglGetProcAddress(name); if (proc == NULL) { @@ -2595,11 +2602,32 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const cairo_surface_flush(CAIROSURFACE); } -SP CHyprOpenGLImpl::loadAsset(const std::string& file) { - const auto CAIROSURFACE = cairo_image_surface_create_from_png(file.c_str()); +SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { - if (!CAIROSURFACE) - return nullptr; + std::string fullPath; + for (auto& e : ASSET_PATHS) { + std::string p = std::string{e} + "/hypr/" + filename; + std::error_code ec; + if (std::filesystem::exists(p, ec)) { + fullPath = p; + break; + } else + Debug::log(LOG, "loadAsset: looking at {} unsuccessful: ec {}", filename, ec.message()); + } + + if (fullPath.empty()) { + failedAssetsNo++; + Debug::log(ERR, "loadAsset: looking for {} failed (no provider found)", filename); + return m_pMissingAssetTexture; + } + + const auto CAIROSURFACE = cairo_image_surface_create_from_png(fullPath.c_str()); + + if (!CAIROSURFACE) { + failedAssetsNo++; + Debug::log(ERR, "loadAsset: failed to load {} (corrupt / inaccessible / not png)", fullPath); + return m_pMissingAssetTexture; + } const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROSURFACE); auto tex = makeShared(); @@ -2710,51 +2738,68 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CColor col, in return tex; } -void CHyprOpenGLImpl::initAssets() { - std::string assetsPath = ""; -#ifndef DATAROOTDIR - assetsPath = "/usr/share/hypr/"; -#else - assetsPath = std::format("{}{}", DATAROOTDIR, "/hypr/"); -#endif +void CHyprOpenGLImpl::initMissingAssetTexture() { + SP tex = makeShared(); + tex->allocate(); - m_pLockDeadTexture = loadAsset(assetsPath + "lockdead.png"); - m_pLockDead2Texture = loadAsset(assetsPath + "lockdead2.png"); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512); + const auto CAIRO = cairo_create(CAIROSURFACE); + + cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_NONE); + cairo_save(CAIRO); + cairo_set_source_rgba(CAIRO, 0, 0, 0, 1); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_SOURCE); + cairo_paint(CAIRO); + cairo_set_source_rgba(CAIRO, 1, 0, 1, 1); + cairo_rectangle(CAIRO, 256, 0, 256, 256); + cairo_fill(CAIRO); + cairo_rectangle(CAIRO, 0, 256, 256, 256); + cairo_fill(CAIRO); + cairo_restore(CAIRO); + + cairo_surface_flush(CAIROSURFACE); + + tex->m_vSize = {512, 512}; + + // copy the data to an OpenGL texture we have + const GLint glFormat = GL_RGBA; + const GLint glType = GL_UNSIGNED_BYTE; + + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); + + cairo_surface_destroy(CAIROSURFACE); + cairo_destroy(CAIRO); + + m_pMissingAssetTexture = tex; +} + +void CHyprOpenGLImpl::initAssets() { + initMissingAssetTexture(); + + static auto PFORCEWALLPAPER = CConfigValue("misc:force_default_wallpaper"); + + const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); + + m_pLockDeadTexture = loadAsset("lockdead.png"); + m_pLockDead2Texture = loadAsset("lockdead2.png"); m_pLockTtyTextTexture = renderText(std::format("Running on tty {}", g_pCompositor->m_pAqBackend->hasSession() && g_pCompositor->m_pAqBackend->session->vt > 0 ? std::to_string(g_pCompositor->m_pAqBackend->session->vt) : "unknown"), CColor{0.9F, 0.9F, 0.9F, 0.7F}, 20, true); -} -void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { - RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); - - Debug::log(LOG, "Creating a texture for BGTex"); - - static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); - static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); - static auto PFORCEWALLPAPER = CConfigValue("misc:force_default_wallpaper"); - - const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); - - if (*PRENDERTEX) - return; - - // release the last tex if exists - const auto PFB = &m_mMonitorBGFBs[pMonitor]; - PFB->release(); - - PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); - - if (!m_pBackgroundTexture) { - std::string texPath = ""; -#ifndef DATAROOTDIR - texPath = "/usr/share/hypr/wall"; -#else - texPath = std::format("{}{}", DATAROOTDIR, "/hypr/wall"); -#endif + // create the default background texture + { + std::string texPath = std::format("{}", "wall"); // get the adequate tex if (FORCEWALLPAPER == -1) { @@ -2767,15 +2812,29 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { texPath += ".png"; - // check if wallpapers exist - std::error_code err; - if (!std::filesystem::exists(texPath, err)) { - Debug::log(ERR, "createBGTextureForMonitor: failed, file \"{}\" doesn't exist or access denied, ec: {}", texPath, err.message()); - return; // the texture will be empty, oh well. We'll clear with a solid color anyways. - } - m_pBackgroundTexture = loadAsset(texPath); } +} + +void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { + RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); + + Debug::log(LOG, "Creating a texture for BGTex"); + + static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); + static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); + + if (*PRENDERTEX) + return; + + // release the last tex if exists + const auto PFB = &m_mMonitorBGFBs[pMonitor]; + PFB->release(); + + PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); + + if (!m_pBackgroundTexture) // ?!?!?! + return; // create a new one with cairo SP tex = makeShared(); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 115dcac7..c594a7cc 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -215,11 +215,12 @@ class CHyprOpenGLImpl { GLint m_iCurrentOutputFb = 0; - int m_iGBMFD = -1; - gbm_device* m_pGbmDevice = nullptr; - EGLContext m_pEglContext = nullptr; - EGLDisplay m_pEglDisplay = nullptr; - EGLDeviceEXT m_pEglDevice = nullptr; + int m_iGBMFD = -1; + gbm_device* m_pGbmDevice = nullptr; + EGLContext m_pEglContext = nullptr; + EGLDisplay m_pEglDisplay = nullptr; + EGLDeviceEXT m_pEglDevice = nullptr; + uint failedAssetsNo = 0; bool m_bReloadScreenShader = true; // at launch it can be set @@ -277,7 +278,7 @@ class CHyprOpenGLImpl { CShader m_sFinalScreenShader; CTimer m_tGlobalTimer; - SP m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; + SP m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; void logShaderError(const GLuint&, bool program = false); GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); @@ -290,6 +291,7 @@ class CHyprOpenGLImpl { SP loadAsset(const std::string& file); SP renderText(const std::string& text, CColor col, int pt, bool italic = false); void initAssets(); + void initMissingAssetTexture(); // std::optional> getModsForFormat(EGLint format); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 1dedcc7d..a133899b 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1063,22 +1063,16 @@ void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) { if (ANY_PRESENT) { // render image2, without instructions. Lock still "alive", unless texture dead - if (g_pHyprOpenGL->m_pLockDead2Texture) - g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDead2Texture, &monbox, ALPHA); - else - g_pHyprOpenGL->renderRect(&monbox, CColor(1.0, 0.2, 0.2, ALPHA)); + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDead2Texture, &monbox, ALPHA); } else { // render image, with instructions. Lock is gone. - if (g_pHyprOpenGL->m_pLockDeadTexture) { - g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDeadTexture, &monbox, ALPHA); + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDeadTexture, &monbox, ALPHA); - // also render text for the tty number - if (g_pHyprOpenGL->m_pLockTtyTextTexture) { - CBox texbox = {{}, g_pHyprOpenGL->m_pLockTtyTextTexture->m_vSize}; - g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockTtyTextTexture, &texbox, 1.F); - } - } else - g_pHyprOpenGL->renderRect(&monbox, CColor(1.0, 0.2, 0.2, ALPHA)); + // also render text for the tty number + if (g_pHyprOpenGL->m_pLockTtyTextTexture) { + CBox texbox = {{}, g_pHyprOpenGL->m_pLockTtyTextTexture->m_vSize}; + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockTtyTextTexture, &texbox, 1.F); + } } if (ALPHA < 1.f) /* animate */