#include "GLRenderer.hpp" #include "decorations/CHyprInnerGlowDecoration.hpp" #include #include "../config/ConfigValue.hpp" #include "../managers/CursorManager.hpp" #include "../managers/PointerManager.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/core/DataDevice.hpp" #include "../protocols/core/Compositor.hpp" #include "../debug/HyprDebugOverlay.hpp" #include "../helpers/Monitor.hpp" #include "pass/TexPassElement.hpp" #include "pass/SurfacePassElement.hpp" #include "../debug/log/Logger.hpp" #include "../protocols/types/ContentType.hpp" #include "OpenGL.hpp" #include "Renderer.hpp" #include "./gl/GLElementRenderer.hpp" #include "./gl/GLFramebuffer.hpp" #include "./gl/GLTexture.hpp" #include #include #include #include using namespace Hyprutils::Utils; using namespace Hyprutils::OS; using enum NContentType::eContentType; using namespace NColorManagement; using namespace Render; using namespace Render::GL; extern "C" { #include } CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer(), m_elementRenderer(makeUnique()) {} IHyprRenderer::eType CHyprGLRenderer::type() { return RT_GL; } void CHyprGLRenderer::initRender() { g_pHyprOpenGL->makeEGLCurrent(); g_pHyprRenderer->m_renderData.pMonitor = renderData().pMonitor; } bool CHyprGLRenderer::initRenderBuffer(SP buffer, uint32_t fmt) { try { m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, fmt); } catch (std::exception& e) { Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", NFormatUtils::drmFormatName(fmt)); return false; } return m_currentRenderbuffer; } bool CHyprGLRenderer::beginFullFakeRenderInternal(PHLMONITOR pMonitor, CRegion& damage, SP fb, bool simple) { initRender(); RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!"); bindFB(fb); if (simple) g_pHyprOpenGL->beginSimple(pMonitor, damage, nullptr, fb); else g_pHyprOpenGL->begin(pMonitor, damage, fb); return true; } bool CHyprGLRenderer::beginRenderInternal(PHLMONITOR pMonitor, CRegion& damage, bool simple) { m_currentRenderbuffer->bind(); if (simple) g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); else g_pHyprOpenGL->begin(pMonitor, damage); return true; } void CHyprGLRenderer::endRender(const std::function& renderingDoneCallback) { const auto PMONITOR = g_pHyprRenderer->m_renderData.pMonitor; static auto PNVIDIAANTIFLICKER = CConfigValue("opengl:nvidia_anti_flicker"); g_pHyprRenderer->m_renderData.damage = m_renderPass.render(g_pHyprRenderer->m_renderData.damage); auto cleanup = CScopeGuard([this]() { if (m_currentRenderbuffer) m_currentRenderbuffer->unbind(); m_currentRenderbuffer = nullptr; m_currentBuffer = nullptr; }); if (m_renderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) g_pHyprOpenGL->end(); else { g_pHyprRenderer->m_renderData.pMonitor.reset(); g_pHyprRenderer->m_renderData.mouseZoomFactor = 1.f; g_pHyprRenderer->m_renderData.mouseZoomUseMouse = true; } if (m_renderMode == RENDER_MODE_FULL_FAKE) return; if (m_renderMode == RENDER_MODE_NORMAL) PMONITOR->m_output->state->setBuffer(m_currentBuffer); if (!explicitSyncSupported()) { Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); // nvidia doesn't have implicit sync, so we have to explicitly wait here, llvmpipe and other software renderer seems to bug out aswell. if ((isNvidia() && *PNVIDIAANTIFLICKER) || isSoftware()) glFinish(); else glFlush(); // mark an implicit sync point m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works if (renderingDoneCallback) renderingDoneCallback(); return; } auto eglSync = createSyncFDManager(); if LIKELY (eglSync && eglSync->isValid()) { for (auto const& buf : m_usedAsyncBuffers) { for (const auto& releaser : buf->m_syncReleasers) { releaser->addSyncFileFd(eglSync->fd()); } } // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync std::erase_if(m_usedAsyncBuffers, [](const auto& buf) { return !buf->m_syncReleasers.empty(); }); // release buffer refs without release points when EGLSync sync_file/fence is signalled g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(m_usedAsyncBuffers)]() mutable { prevbfs.clear(); if (renderingDoneCallback) renderingDoneCallback(); }); m_usedAsyncBuffers.clear(); if (m_renderMode == RENDER_MODE_NORMAL) { PMONITOR->m_inFence = eglSync->takeFd(); PMONITOR->m_output->state->setExplicitInFence(PMONITOR->m_inFence.get()); } } else { Log::logger->log(Log::ERR, "renderer: Explicit sync failed, releasing resources"); m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works if (renderingDoneCallback) renderingDoneCallback(); } } void CHyprGLRenderer::renderOffToMain(SP off) { g_pHyprOpenGL->renderOffToMain(off); } SP CHyprGLRenderer::getOrCreateRenderbufferInternal(SP buffer, uint32_t fmt) { g_pHyprOpenGL->makeEGLCurrent(); return makeShared(buffer, fmt); } UP CHyprGLRenderer::createSyncFDManager() { return CEGLSync::create(); } SP CHyprGLRenderer::createStencilTexture(const int width, const int height) { g_pHyprOpenGL->makeEGLCurrent(); auto tex = makeShared(); tex->allocate({width, height}); return tex; } SP CHyprGLRenderer::createTexture(bool opaque) { g_pHyprOpenGL->makeEGLCurrent(); return makeShared(opaque); } SP CHyprGLRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { g_pHyprOpenGL->makeEGLCurrent(); return makeShared(drmFormat, pixels, stride, size, keepDataCopy, opaque); } SP CHyprGLRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { g_pHyprOpenGL->makeEGLCurrent(); const auto image = g_pHyprOpenGL->createEGLImage(attrs); if (!image) return nullptr; return makeShared(attrs, image, opaque); } SP CHyprGLRenderer::createTexture(const int width, const int height, unsigned char* const data) { g_pHyprOpenGL->makeEGLCurrent(); SP tex = makeShared(); tex->allocate({width, height}, DRM_FORMAT_ARGB8888); // FIXME assume DRM_FORMAT_ARGB8888 tex->m_size = {width, height}; // copy the data to an OpenGL texture we have const GLint glFormat = GL_RGBA; const GLint glType = GL_UNSIGNED_BYTE; tex->bind(); tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); tex->unbind(); return tex; } SP CHyprGLRenderer::createTexture(cairo_surface_t* cairo) { g_pHyprOpenGL->makeEGLCurrent(); const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); auto tex = makeShared(); tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; const auto DATA = cairo_image_surface_get_data(cairo); tex->bind(); tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); tex->m_drmFormat = DRM_FORMAT_ARGB8888; } glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); return tex; } SP CHyprGLRenderer::createTexture(std::span lut3D, size_t N) { g_pHyprOpenGL->makeEGLCurrent(); return makeShared(lut3D, N); } bool CHyprGLRenderer::explicitSyncSupported() { return g_pHyprOpenGL->explicitSyncSupported(); } std::vector CHyprGLRenderer::getDRMFormats() { return g_pHyprOpenGL->getDRMFormats(); } std::vector CHyprGLRenderer::getDRMFormatModifiers(DRMFormat format) { return g_pHyprOpenGL->getDRMFormatModifiers(format); } SP CHyprGLRenderer::createFB(const std::string& name) { g_pHyprOpenGL->makeEGLCurrent(); return makeShared(name); } void CHyprGLRenderer::disableScissor() { g_pHyprOpenGL->scissor(nullptr); } void CHyprGLRenderer::blend(bool enabled) { g_pHyprOpenGL->blend(enabled); } void CHyprGLRenderer::drawShadow(const CBox& box, int round, float roundingPower, int range, CHyprColor color, float a) { g_pHyprOpenGL->renderRoundedShadow(box, round, roundingPower, range, color, a); } SP CHyprGLRenderer::blurFramebuffer(SP source, float a, CRegion* originalDamage) { auto src = GLFB(source); return g_pHyprOpenGL->blurFramebufferWithDamage(a, originalDamage, *src)->getTexture(); } void CHyprGLRenderer::setViewport(int x, int y, int width, int height) { g_pHyprOpenGL->setViewport(x, y, width, height); } bool CHyprGLRenderer::reloadShaders(const std::string& path) { return g_pHyprOpenGL->initShaders(path); } SP CHyprGLRenderer::getBlurTexture(PHLMONITORREF pMonitor) { return pMonitor->resources()->m_blurFB->getTexture(); } void CHyprGLRenderer::unsetEGL() { if (!g_pHyprOpenGL) return; eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } WP CHyprGLRenderer::elementRenderer() { return m_elementRenderer; }