diff --git a/src/Compositor.cpp b/src/Compositor.cpp index b5dba342e..c1c210203 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2923,6 +2923,9 @@ void CCompositor::leaveUnsafeState() { } void CCompositor::setPreferredScaleForSurface(SP pSurface, double scale) { + if (!pSurface) + return; + PROTO::fractional->sendScale(pSurface, scale); pSurface->sendPreferredScale(std::ceil(scale)); @@ -2937,6 +2940,9 @@ void CCompositor::setPreferredScaleForSurface(SP pSurface, d } void CCompositor::setPreferredTransformForSurface(SP pSurface, wl_output_transform transform) { + if (!pSurface) + return; + pSurface->sendPreferredTransform(transform); const auto PSURFACE = Desktop::View::CWLSurface::fromResource(pSurface); diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 0200ebd4a..af4eaf041 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -264,7 +264,7 @@ SP CWLSurfaceResource::fromResource(wl_resource* res) { } bool CWLSurfaceResource::good() { - return m_resource->resource(); + return m_resource && m_resource->resource(); } wl_client* CWLSurfaceResource::client() { @@ -272,6 +272,9 @@ wl_client* CWLSurfaceResource::client() { } void CWLSurfaceResource::enter(PHLMONITOR monitor) { + if (!good()) + return; + if (std::ranges::find(m_enteredOutputs, monitor) != m_enteredOutputs.end()) return; @@ -304,6 +307,9 @@ void CWLSurfaceResource::enter(PHLMONITOR monitor) { } void CWLSurfaceResource::leave(PHLMONITOR monitor) { + if (!good()) + return; + if UNLIKELY (std::ranges::find(m_enteredOutputs, monitor) == m_enteredOutputs.end()) return; @@ -325,12 +331,16 @@ void CWLSurfaceResource::leave(PHLMONITOR monitor) { } void CWLSurfaceResource::sendPreferredTransform(wl_output_transform t) { + if (!good()) + return; if (m_resource->version() < 6) return; m_resource->sendPreferredBufferTransform(t); } void CWLSurfaceResource::sendPreferredScale(int32_t scale) { + if (!good()) + return; if (m_resource->version() < 6) return; m_resource->sendPreferredBufferScale(scale); @@ -471,6 +481,8 @@ std::pair, Vector2D> CWLSurfaceResource::at(const Vector2 } uint32_t CWLSurfaceResource::id() { + if (!good()) + return 0; return wl_resource_get_id(m_resource->resource()); } @@ -505,6 +517,8 @@ void CWLSurfaceResource::releaseBuffers(bool onlyCurrent) { } void CWLSurfaceResource::error(int code, const std::string& str) { + if (!good()) + return; m_resource->error(code, str); } diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 6bb04cfae..a46a938ea 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -664,9 +664,28 @@ EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attr return image; } +bool CHyprOpenGLImpl::attemptContextReset() { + Log::logger->log(Log::ERR, "GPU reset: attempting EGL context recovery..."); + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglContext) != EGL_TRUE) { + Log::logger->log(Log::ERR, "GPU reset: eglMakeCurrent failed, context may be lost"); + return false; + } + m_shadersInitialized = false; + Log::logger->log(Log::WARN, "GPU reset: EGL context re-established, shaders will reinitialize on next frame."); + return true; +} + void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP rb, SP fb) { g_pHyprRenderer->m_renderData.pMonitor = pMonitor; + if (m_gpuResetCooldown > 0) { + m_gpuResetCooldown--; + Log::logger->log(Log::WARN, "GPU reset recovery cooldown, skipping frame ({} remaining)", m_gpuResetCooldown); + g_pHyprRenderer->m_renderData.pMonitor.reset(); + return; + } + const GLenum RESETSTATUS = glGetGraphicsResetStatus(); if (RESETSTATUS != GL_NO_ERROR) { std::string errStr = ""; @@ -676,7 +695,14 @@ void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP case GL_UNKNOWN_CONTEXT_RESET: errStr = "GL_UNKNOWN_CONTEXT_RESET"; break; default: errStr = "UNKNOWN??"; break; } - RASSERT(false, "Aborting, glGetGraphicsResetStatus returned {}. Cannot continue until proper GPU reset handling is implemented.", errStr); + Log::logger->log(Log::ERR, "GPU reset detected in beginSimple: {}. Attempting EGL context recovery.", errStr); + if (!attemptContextReset()) { + Log::logger->log(Log::ERR, "GPU reset recovery failed. Skipping frames for cooldown."); + m_gpuResetCooldown = 60; + } else { + Log::logger->log(Log::WARN, "GPU reset recovery succeeded. Skipping current frame to reinitialize."); + } + g_pHyprRenderer->m_renderData.pMonitor.reset(); return; } @@ -715,6 +741,13 @@ void CHyprOpenGLImpl::makeEGLCurrent() { void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP fb, std::optional finalDamage) { g_pHyprRenderer->m_renderData.pMonitor = pMonitor; + if (m_gpuResetCooldown > 0) { + m_gpuResetCooldown--; + Log::logger->log(Log::WARN, "GPU reset recovery cooldown, skipping frame ({} remaining)", m_gpuResetCooldown); + g_pHyprRenderer->m_renderData.pMonitor.reset(); + return; + } + const GLenum RESETSTATUS = glGetGraphicsResetStatus(); if (RESETSTATUS != GL_NO_ERROR) { std::string errStr = ""; @@ -724,7 +757,14 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPlog(Log::ERR, "GPU reset detected in begin: {}. Attempting EGL context recovery.", errStr); + if (!attemptContextReset()) { + Log::logger->log(Log::ERR, "GPU reset recovery failed. Skipping frames for cooldown."); + m_gpuResetCooldown = 60; + } else { + Log::logger->log(Log::WARN, "GPU reset recovery succeeded. Skipping current frame to reinitialize."); + } + g_pHyprRenderer->m_renderData.pMonitor.reset(); return; } @@ -858,8 +898,10 @@ void CHyprOpenGLImpl::end() { // check for gl errors const GLenum ERR = glGetError(); - if UNLIKELY (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */ - RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented."); + if UNLIKELY (ERR == GL_CONTEXT_LOST) { + Log::logger->log(Log::ERR, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Recovery will trigger on next begin()."); + m_gpuResetCooldown = 60; + } } } @@ -1048,7 +1090,12 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol CRegion damage{g_pHyprRenderer->m_renderData.damage}; damage.intersect(box); - auto blurredBG = data.xray ? g_pHyprRenderer->m_renderData.pMonitor->resources()->m_blurFB->getTexture() : g_pHyprRenderer->blurMainFramebuffer(data.blurA, &damage); + auto blurFB = g_pHyprRenderer->m_renderData.pMonitor->resources()->m_blurFB; + auto blurredBG = data.xray ? (blurFB ? blurFB->getTexture() : nullptr) : g_pHyprRenderer->blurMainFramebuffer(data.blurA, &damage); + if (!blurredBG) { + Log::logger->log(Log::ERR, "renderTextureWithBlur: blur texture unavailable (likely GPU reset). Skipping blur."); + return; + } CBox MONITORBOX = {0, 0, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.x, g_pHyprRenderer->m_renderData.pMonitor->m_transformedSize.y}; g_pHyprRenderer->pushMonitorTransformEnabled(true); @@ -1439,8 +1486,13 @@ WP CHyprOpenGLImpl::renderToFBInternal(SP tex, const STexture void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { RASSERT(g_pHyprRenderer->m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT(tex, "Attempted to draw nullptr texture!"); - RASSERT(tex->ok(), "Attempted to draw invalid texture!"); + + if UNLIKELY (!tex || !tex->ok()) { + // After a GPU reset, textures become invalid. Skip the draw + // instead of aborting — recovery will happen on the next begin(). + Log::logger->log(Log::ERR, "renderTextureInternal: invalid texture (likely GPU reset). Skipping draw."); + return; + } TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 82b8f0548..405cdcdcf 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -313,6 +313,8 @@ namespace Render::GL { bool m_applyFinalShader = false; bool m_blend = false; bool m_offloadedFramebuffer = false; + int m_gpuResetCooldown = 0; + bool attemptContextReset(); bool m_cmSupported = true; SP m_finalScreenShader; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 635bef556..cb0482f7e 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1451,6 +1451,8 @@ SP IHyprRenderer::loadAsset(const std::string& filename) { } SP IHyprRenderer::getBlurTexture(PHLMONITORREF pMonitor) { + if (!pMonitor || !pMonitor->resources()->m_blurFB) + return nullptr; return pMonitor->resources()->m_blurFB->getTexture(); } @@ -1789,9 +1791,10 @@ Mat3x3 IHyprRenderer::projectBoxToTarget(const CBox& box, std::optional IHyprRenderer::blurMainFramebuffer(float a, CRegion* originalDamage) { - if (!m_renderData.currentFB->getTexture()) { + if (!m_renderData.currentFB || !m_renderData.currentFB->getTexture()) { Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); - return m_renderData.pMonitor->resources()->m_blurFB->getTexture(); // return something to sample from at least + auto blurFB = m_renderData.pMonitor->resources()->m_blurFB; + return blurFB ? blurFB->getTexture() : nullptr; } auto guard = bindTempFB(m_renderData.currentFB); // blurFramebuffer messes with FB bindings @@ -1803,6 +1806,10 @@ void IHyprRenderer::preBlurForCurrentMonitor(CRegion* fakeDamage) { const auto blurredTex = blurMainFramebuffer(1, fakeDamage); // render onto blurFB + if (!m_renderData.pMonitor->resources()->m_blurFB) { + Log::logger->log(Log::ERR, "preBlurForCurrentMonitor: blurFB is null, skipping blur."); + return; + } auto guard = bindTempFB(m_renderData.pMonitor->resources()->m_blurFB); const auto SAVE_TRANSFORM = blurredTex->m_transform; blurredTex->m_transform = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform));