2026-03-07 21:21:23 +03:00
|
|
|
#include "GLRenderer.hpp"
|
2026-03-27 10:08:56 -04:00
|
|
|
#include "decorations/CHyprInnerGlowDecoration.hpp"
|
2026-03-07 21:21:23 +03:00
|
|
|
#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"
|
2026-04-16 19:47:53 +01:00
|
|
|
#include "../debug/Overlay.hpp"
|
2026-03-13 14:53:07 +01:00
|
|
|
#include "../helpers/Monitor.hpp"
|
2026-03-07 21:21:23 +03:00
|
|
|
#include "pass/TexPassElement.hpp"
|
|
|
|
|
#include "pass/SurfacePassElement.hpp"
|
2026-03-13 14:53:07 +01:00
|
|
|
#include "../debug/log/Logger.hpp"
|
2026-03-07 21:21:23 +03:00
|
|
|
#include "../protocols/types/ContentType.hpp"
|
2026-03-13 14:53:07 +01:00
|
|
|
#include "OpenGL.hpp"
|
|
|
|
|
#include "Renderer.hpp"
|
2026-03-29 00:41:45 +03:00
|
|
|
#include "./gl/GLElementRenderer.hpp"
|
|
|
|
|
#include "./gl/GLFramebuffer.hpp"
|
|
|
|
|
#include "./gl/GLTexture.hpp"
|
2026-03-07 21:21:23 +03:00
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <hyprutils/memory/SharedPtr.hpp>
|
2026-03-29 00:41:45 +03:00
|
|
|
#include <hyprutils/memory/UniquePtr.hpp>
|
2026-03-07 21:21:23 +03:00
|
|
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
|
|
|
|
using namespace Hyprutils::Utils;
|
|
|
|
|
using namespace Hyprutils::OS;
|
|
|
|
|
using enum NContentType::eContentType;
|
|
|
|
|
using namespace NColorManagement;
|
2026-03-29 00:41:45 +03:00
|
|
|
using namespace Render;
|
|
|
|
|
using namespace Render::GL;
|
2026-03-07 21:21:23 +03:00
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include <xf86drm.h>
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:41:45 +03:00
|
|
|
CHyprGLRenderer::CHyprGLRenderer() : IHyprRenderer(), m_elementRenderer(makeUnique<CGLElementRenderer>()) {}
|
|
|
|
|
|
|
|
|
|
IHyprRenderer::eType CHyprGLRenderer::type() {
|
|
|
|
|
return RT_GL;
|
|
|
|
|
}
|
2026-03-07 21:21:23 +03:00
|
|
|
|
|
|
|
|
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!");
|
2026-03-29 00:41:45 +03:00
|
|
|
bindFB(fb);
|
2026-03-07 21:21:23 +03:00
|
|
|
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;
|
2026-04-26 15:16:36 +01:00
|
|
|
static auto PNVIDIAANTIFLICKER = CConfigValue<Config::INTEGER>("opengl:nvidia_anti_flicker");
|
2026-03-07 21:21:23 +03:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:41:45 +03:00
|
|
|
auto eglSync = createSyncFDManager();
|
2026-03-07 21:21:23 +03:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:41:45 +03:00
|
|
|
void CHyprGLRenderer::renderOffToMain(SP<IFramebuffer> off) {
|
2026-03-07 21:21:23 +03:00
|
|
|
g_pHyprOpenGL->renderOffToMain(off);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SP<IRenderbuffer> CHyprGLRenderer::getOrCreateRenderbufferInternal(SP<Aquamarine::IBuffer> buffer, uint32_t fmt) {
|
|
|
|
|
g_pHyprOpenGL->makeEGLCurrent();
|
|
|
|
|
return makeShared<CGLRenderbuffer>(buffer, fmt);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:41:45 +03:00
|
|
|
UP<ISyncFDManager> CHyprGLRenderer::createSyncFDManager() {
|
|
|
|
|
return CEGLSync::create();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 21:21:23 +03:00
|
|
|
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>();
|
|
|
|
|
|
2026-04-16 21:19:25 +03:00
|
|
|
tex->allocate({width, height}, DRM_FORMAT_ARGB8888); // FIXME assume DRM_FORMAT_ARGB8888
|
2026-03-07 21:21:23 +03:00
|
|
|
|
|
|
|
|
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);
|
2026-04-16 21:19:25 +03:00
|
|
|
tex->m_drmFormat = DRM_FORMAT_ARGB8888;
|
2026-03-07 21:21:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SP<ITexture> CHyprGLRenderer::getBlurTexture(PHLMONITORREF pMonitor) {
|
2026-03-29 00:41:45 +03:00
|
|
|
return pMonitor->resources()->m_blurFB->getTexture();
|
2026-03-07 21:21:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHyprGLRenderer::unsetEGL() {
|
|
|
|
|
if (!g_pHyprOpenGL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
|
}
|
2026-03-29 00:41:45 +03:00
|
|
|
|
|
|
|
|
WP<IElementRenderer> CHyprGLRenderer::elementRenderer() {
|
|
|
|
|
return m_elementRenderer;
|
|
|
|
|
}
|