summaryrefslogtreecommitdiff
path: root/src/render/GLRenderer.cpp
diff options
context:
space:
mode:
authorUjinT34 <41110182+UjinT34@users.noreply.github.com>2026-03-07 21:21:23 +0300
committerGitHub <noreply@github.com>2026-03-07 18:21:23 +0000
commit02f30ea15b349d1ed98b608ed06ec88c834592dd (patch)
tree5148a3935c43b64f209f2b13e2a0b1bbe987444b /src/render/GLRenderer.cpp
parentb227efc8492468cc925014a6c8446a3d044b01e8 (diff)
renderer: refactor gl renderer (#13488)
Diffstat (limited to 'src/render/GLRenderer.cpp')
-rw-r--r--src/render/GLRenderer.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/src/render/GLRenderer.cpp b/src/render/GLRenderer.cpp
new file mode 100644
index 00000000..c1f64837
--- /dev/null
+++ b/src/render/GLRenderer.cpp
@@ -0,0 +1,405 @@
+#include "GLRenderer.hpp"
+#include <aquamarine/output/Output.hpp>
+#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/ClearPassElement.hpp"
+#include "pass/RectPassElement.hpp"
+#include "pass/SurfacePassElement.hpp"
+#include "debug/log/Logger.hpp"
+#include "../protocols/types/ContentType.hpp"
+#include "render/OpenGL.hpp"
+#include "render/Renderer.hpp"
+#include "render/gl/GLFramebuffer.hpp"
+#include "render/gl/GLTexture.hpp"
+#include "decorations/CHyprDropShadowDecoration.hpp"
+
+#include <cstdint>
+#include <hyprutils/memory/SharedPtr.hpp>
+#include <hyprutils/utils/ScopeGuard.hpp>
+using namespace Hyprutils::Utils;
+using namespace Hyprutils::OS;
+using enum NContentType::eContentType;
+using namespace NColorManagement;
+
+extern "C" {
+#include <xf86drm.h>
+}
+
+CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer() {}
+
+void CHyprGLRenderer::initRender() {
+ g_pHyprOpenGL->makeEGLCurrent();
+ g_pHyprRenderer->m_renderData.pMonitor = renderData().pMonitor;
+}
+
+bool CHyprGLRenderer::initRenderBuffer(SP<Aquamarine::IBuffer> 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<IFramebuffer> fb, bool simple) {
+ initRender();
+
+ RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!");
+ fb->bind();
+ 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<void()>& renderingDoneCallback) {
+ const auto PMONITOR = g_pHyprRenderer->m_renderData.pMonitor;
+ static auto PNVIDIAANTIFLICKER = CConfigValue<Hyprlang::INT>("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;
+ }
+
+ UP<CEGLSync> eglSync = CEGLSync::create();
+ 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(IFramebuffer* off) {
+ g_pHyprOpenGL->renderOffToMain(off);
+}
+
+SP<IRenderbuffer> CHyprGLRenderer::getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ return makeShared<CGLRenderbuffer>(buffer, fmt);
+}
+
+SP<ITexture> CHyprGLRenderer::createStencilTexture(const int width, const int height) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ auto tex = makeShared<CGLTexture>();
+ tex->allocate({width, height});
+
+ return tex;
+}
+
+SP<ITexture> CHyprGLRenderer::createTexture(bool opaque) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ return makeShared<CGLTexture>(opaque);
+}
+
+SP<ITexture> CHyprGLRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ return makeShared<CGLTexture>(drmFormat, pixels, stride, size, keepDataCopy, opaque);
+}
+
+SP<ITexture> CHyprGLRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ const auto image = g_pHyprOpenGL->createEGLImage(attrs);
+ if (!image)
+ return nullptr;
+ return makeShared<CGLTexture>(attrs, image, opaque);
+}
+
+SP<ITexture> CHyprGLRenderer::createTexture(const int width, const int height, unsigned char* const data) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ SP<ITexture> tex = makeShared<CGLTexture>();
+
+ tex->allocate({width, height});
+
+ 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<ITexture> CHyprGLRenderer::createTexture(cairo_surface_t* cairo) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ const auto CAIROFORMAT = cairo_image_surface_get_format(cairo);
+ auto tex = makeShared<CGLTexture>();
+
+ 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);
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA);
+
+ return tex;
+}
+
+SP<ITexture> CHyprGLRenderer::createTexture(std::span<const float> lut3D, size_t N) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ return makeShared<CGLTexture>(lut3D, N);
+}
+
+bool CHyprGLRenderer::explicitSyncSupported() {
+ return g_pHyprOpenGL->explicitSyncSupported();
+}
+
+std::vector<SDRMFormat> CHyprGLRenderer::getDRMFormats() {
+ return g_pHyprOpenGL->getDRMFormats();
+}
+
+std::vector<uint64_t> CHyprGLRenderer::getDRMFormatModifiers(DRMFormat format) {
+ return g_pHyprOpenGL->getDRMFormatModifiers(format);
+}
+
+SP<IFramebuffer> CHyprGLRenderer::createFB(const std::string& name) {
+ g_pHyprOpenGL->makeEGLCurrent();
+ return makeShared<CGLFramebuffer>(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<ITexture> CHyprGLRenderer::blurFramebuffer(SP<IFramebuffer> 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);
+}
+
+void CHyprGLRenderer::draw(CBorderPassElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+ if (m_data.hasGrad2)
+ g_pHyprOpenGL->renderBorder(
+ m_data.box, m_data.grad1, m_data.grad2, m_data.lerp,
+ {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound});
+ else
+ g_pHyprOpenGL->renderBorder(
+ m_data.box, m_data.grad1,
+ {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound});
+};
+
+void CHyprGLRenderer::draw(CClearPassElement* element, const CRegion& damage) {
+ g_pHyprOpenGL->clear(element->m_data.color);
+};
+
+void CHyprGLRenderer::draw(CFramebufferElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+ SP<IFramebuffer> fb = nullptr;
+
+ if (m_data.main) {
+ switch (m_data.framebufferID) {
+ case FB_MONITOR_RENDER_MAIN: fb = g_pHyprRenderer->m_renderData.mainFB; break;
+ case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprRenderer->m_renderData.currentFB; break;
+ case FB_MONITOR_RENDER_OUT: fb = g_pHyprRenderer->m_renderData.outFB; break;
+ default: fb = nullptr;
+ }
+
+ if (!fb) {
+ Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: main but null");
+ return;
+ }
+
+ } else {
+ switch (m_data.framebufferID) {
+ case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = g_pHyprRenderer->m_renderData.pMonitor->m_offloadFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = m_renderData.pMonitor->m_mirrorFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = m_renderData.pMonitor->m_mirrorSwapFB; break;
+ case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = g_pHyprRenderer->m_renderData.pMonitor->m_offMainFB; break;
+ case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = g_pHyprRenderer->m_renderData.pMonitor->m_monitorMirrorFB; break;
+ case FB_MONITOR_RENDER_EXTRA_BLUR: fb = g_pHyprRenderer->m_renderData.pMonitor->m_blurFB; break;
+ default: fb = nullptr;
+ }
+
+ if (!fb) {
+ Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: not main but null");
+ return;
+ }
+ }
+
+ fb->bind();
+};
+
+void CHyprGLRenderer::draw(CPreBlurElement* element, const CRegion& damage) {
+ auto dmg = damage;
+ g_pHyprRenderer->preBlurForCurrentMonitor(&dmg);
+};
+
+void CHyprGLRenderer::draw(CRectPassElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+
+ if (m_data.color.a == 1.F || !m_data.blur)
+ g_pHyprOpenGL->renderRect(m_data.box, m_data.color, {.damage = &damage, .round = m_data.round, .roundingPower = m_data.roundingPower});
+ else
+ g_pHyprOpenGL->renderRect(m_data.box, m_data.color,
+ {.round = m_data.round, .roundingPower = m_data.roundingPower, .blur = true, .blurA = m_data.blurA, .xray = m_data.xray});
+};
+
+void CHyprGLRenderer::draw(CShadowPassElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+ m_data.deco->render(g_pHyprRenderer->m_renderData.pMonitor.lock(), m_data.a);
+};
+
+void CHyprGLRenderer::draw(CTexPassElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+
+ g_pHyprOpenGL->renderTexture( //
+ m_data.tex, m_data.box,
+ {
+ // blur settings for m_data.blur == true
+ .blur = m_data.blur,
+ .blurA = m_data.blurA,
+ .overallA = m_data.overallA,
+ .blockBlurOptimization = m_data.blockBlurOptimization.value_or(false),
+ .blurredBG = m_data.blurredBG,
+
+ // common settings
+ .damage = m_data.damage.empty() ? &damage : &m_data.damage,
+ .surface = m_data.surface,
+ .a = m_data.a,
+ .round = m_data.round,
+ .roundingPower = m_data.roundingPower,
+ .discardActive = m_data.discardActive,
+ .allowCustomUV = m_data.allowCustomUV,
+ .cmBackToSRGB = m_data.cmBackToSRGB,
+ .cmBackToSRGBSource = m_data.cmBackToSRGBSource,
+ .discardMode = m_data.ignoreAlpha.has_value() ? sc<uint32_t>(DISCARD_ALPHA) : m_data.discardMode,
+ .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity,
+ .clipRegion = m_data.clipRegion,
+ .currentLS = m_data.currentLS,
+
+ .primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft,
+ .primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight,
+ });
+};
+
+void CHyprGLRenderer::draw(CTextureMatteElement* element, const CRegion& damage) {
+ const auto m_data = element->m_data;
+
+ g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb);
+};
+
+SP<ITexture> CHyprGLRenderer::getBlurTexture(PHLMONITORREF pMonitor) {
+ return pMonitor->m_blurFB->getTexture();
+}
+
+void CHyprGLRenderer::unsetEGL() {
+ if (!g_pHyprOpenGL)
+ return;
+
+ eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+}