renderer: Move to a full Hyprland GL rendering pipeline (#3920)

Also updates wlroots
This commit is contained in:
Vaxry 2023-11-24 10:54:21 +00:00 committed by GitHub
parent e40e486f61
commit 2ebfd0c745
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 453 additions and 230 deletions

View file

@ -62,6 +62,21 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) {
return true;
}
void CFramebuffer::addStencil() {
glBindTexture(GL_TEXTURE_2D, m_pStencilTex->m_iTexID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_vSize.x, m_vSize.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
glBindFramebuffer(GL_FRAMEBUFFER, m_iFb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0);
auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Failed adding a stencil to fbo!", status);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb);
}
void CFramebuffer::bind() {
#ifndef GLES2
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb);

View file

@ -8,6 +8,7 @@ class CFramebuffer {
~CFramebuffer();
bool alloc(int w, int h, uint32_t format = GL_RGBA);
void addStencil();
void bind();
void release();
void reset();

View file

@ -5,6 +5,15 @@
#include "Shaders.hpp"
#include <random>
inline void loadGLProc(void* pProc, const char* name) {
void* proc = (void*)eglGetProcAddress(name);
if (proc == NULL) {
Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
abort();
}
*(void**)pProc = proc;
}
CHyprOpenGLImpl::CHyprOpenGLImpl() {
RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)),
"Couldn't unset current EGL!");
@ -23,6 +32,9 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() {
Debug::log(LOG, "Renderer: {}", (char*)glGetString(GL_RENDERER));
Debug::log(LOG, "Supported extensions size: {}", std::count(m_szExtensions.begin(), m_szExtensions.end(), ' '));
loadGLProc(&m_sProc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
loadGLProc(&m_sProc.eglDestroyImageKHR, "eglDestroyImageKHR");
#ifdef USE_TRACY_GPU
loadGLProc(&glQueryCounter, "glQueryCounterEXT");
@ -106,11 +118,9 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool
void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, bool fake) {
m_RenderData.pMonitor = pMonitor;
TRACY_GPU_ZONE("RenderBegin");
static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue;
if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL)) {
eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
}
TRACY_GPU_ZONE("RenderBegin");
glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
@ -118,19 +128,16 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, bool fake) {
m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor];
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_iCurrentOutputFb);
m_iWLROutputFb = m_iCurrentOutputFb;
// ensure a framebuffer for the monitor exists
if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->primaryFB.m_vSize != pMonitor->vecPixelSize) {
if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) {
m_RenderData.pCurrentMonData->stencilTex.allocate();
m_RenderData.pCurrentMonData->primaryFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
m_RenderData.pCurrentMonData->mirrorFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
m_RenderData.pCurrentMonData->mirrorSwapFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
m_RenderData.pCurrentMonData->offMainFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
m_RenderData.pCurrentMonData->primaryFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->offloadFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
m_RenderData.pCurrentMonData->offMainFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat);
@ -144,19 +151,37 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, bool fake) {
if (!m_RenderData.pCurrentMonData->m_bShadersInitialized)
initShaders();
// bind the primary Hypr Framebuffer
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.damage.set(*pDamage);
m_bFakeFrame = fake;
m_RenderData.currentFB = &m_RenderData.pCurrentMonData->primaryFB;
if (m_bReloadScreenShader) {
m_bReloadScreenShader = false;
applyScreenShader(g_pConfigManager->getString("decoration:screen_shader"));
}
const auto PRBO = g_pHyprRenderer->getCurrentRBO();
if (m_sFinalScreenShader.program > 0 || m_bFakeFrame || m_RenderData.mouseZoomFactor != 1.0 || pMonitor->vecPixelSize != PRBO->getFB()->m_vSize ||
(*PBLUR != 0 && !pMonitor->solitaryClient /* TODO: revisit when possible */)) {
// we have to offload
// bind the primary Hypr Framebuffer
m_RenderData.pCurrentMonData->offloadFB.bind();
m_RenderData.currentFB = &m_RenderData.pCurrentMonData->offloadFB;
m_bOffloadedFramebuffer = true;
} else {
// we can render to the rbo directly
const auto PFBO = PRBO->getFB();
m_RenderData.currentFB = PFBO;
if (PFBO->m_pStencilTex != &m_RenderData.pCurrentMonData->stencilTex) {
PFBO->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex;
PFBO->addStencil();
}
PRBO->bindFB();
m_bOffloadedFramebuffer = false;
}
m_RenderData.mainFB = m_RenderData.currentFB;
}
void CHyprOpenGLImpl::end() {
@ -164,14 +189,15 @@ void CHyprOpenGLImpl::end() {
TRACY_GPU_ZONE("RenderEnd");
if (!m_RenderData.pMonitor->mirrors.empty())
saveBufferForMirror(); // save with original damage region
// end the render, copy the data to the WLR framebuffer
if (!m_bFakeFrame) {
if (m_bOffloadedFramebuffer && !m_bFakeFrame) {
m_RenderData.damage = m_RenderData.pMonitor->lastFrameDamage;
if (!m_RenderData.pMonitor->mirrors.empty())
saveBufferForMirror(); // save with original damage region
g_pHyprRenderer->getCurrentRBO()->bindFB();
glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb);
CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
if (m_RenderData.mouseZoomFactor != 1.f) {
@ -199,9 +225,9 @@ void CHyprOpenGLImpl::end() {
blend(false);
if (m_sFinalScreenShader.program < 1)
renderTexturePrimitive(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox);
renderTexturePrimitive(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox);
else
renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f);
renderTexture(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox, 1.f);
blend(true);
@ -212,15 +238,10 @@ void CHyprOpenGLImpl::end() {
// reset our data
m_RenderData.pMonitor = nullptr;
m_iWLROutputFb = 0;
m_RenderData.mouseZoomFactor = 1.f;
m_RenderData.mouseZoomUseMouse = true;
}
void CHyprOpenGLImpl::bindWlrOutputFb() {
glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb);
}
void CHyprOpenGLImpl::initShaders() {
GLuint prog = createProgram(QUADVERTSRC, QUADFRAGSRC);
m_RenderData.pCurrentMonData->m_shQUAD.program = prog;
@ -501,8 +522,7 @@ void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CColor& col, int round
CFramebuffer* POUTFB = blurMainFramebufferWithDamage(blurA, &damage);
// bind primary
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.currentFB->bind();
// make a stencil for rounded corners to work with blur
scissor((CBox*)nullptr); // allow the entire window and stencil to render
@ -553,7 +573,7 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CColor& col, CRegion
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -640,7 +660,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox*
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -801,7 +821,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) {
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -855,7 +875,7 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -914,7 +934,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o
const auto TRANSFORM = wlr_output_transform_invert(m_RenderData.pMonitor->transform);
float matrix[9];
CBox MONITORBOX = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
wlr_matrix_project_box(matrix, MONITORBOX.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
wlr_matrix_project_box(matrix, MONITORBOX.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data());
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -947,9 +967,9 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o
glActiveTexture(GL_TEXTURE0);
glBindTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTarget, m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTexID);
glBindTexture(m_RenderData.currentFB->m_cTex.m_iTarget, m_RenderData.currentFB->m_cTex.m_iTexID);
glTexParameteri(m_RenderData.pCurrentMonData->primaryFB.m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(m_RenderData.currentFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glUseProgram(m_RenderData.pCurrentMonData->m_shBLURPREPARE.program);
@ -1225,7 +1245,7 @@ void CHyprOpenGLImpl::preBlurForCurrentMonitor() {
renderTextureInternalWithDamage(POUTFB->m_cTex, &wholeMonitor, 1, &fakeDamage, 0, false, true, false);
m_bEndFrame = false;
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.currentFB->bind();
m_RenderData.pCurrentMonData->blurFBDirty = false;
@ -1321,8 +1341,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo
POUTFB = &m_RenderData.pCurrentMonData->blurFB;
}
// bind primary
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.currentFB->bind();
// make a stencil for rounded corners to work with blur
scissor((CBox*)nullptr); // allow the entire window and stencil to render
@ -1404,7 +1423,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -1744,7 +1763,7 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const
float matrix[9];
wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@ -1811,11 +1830,11 @@ void CHyprOpenGLImpl::saveBufferForMirror() {
blend(false);
renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f, 0, false, false);
renderTexture(m_RenderData.currentFB->m_cTex, &monbox, 1.f, 0, false, false);
blend(true);
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.currentFB->bind();
}
void CHyprOpenGLImpl::renderMirrored() {
@ -1974,7 +1993,7 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
wlr_output_attach_render(pMonitor->output, nullptr);
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].mirrorFB.release();
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].primaryFB.release();
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].offloadFB.release();
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].mirrorSwapFB.release();
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].monitorMirrorFB.release();
g_pHyprOpenGL->m_mMonitorRenderResources[pMonitor].blurFB.release();
@ -2014,8 +2033,8 @@ void CHyprOpenGLImpl::renderOffToMain(CFramebuffer* off) {
}
void CHyprOpenGLImpl::bindBackOnMain() {
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.currentFB = &m_RenderData.pCurrentMonData->primaryFB;
m_RenderData.mainFB->bind();
m_RenderData.currentFB = m_RenderData.mainFB;
}
void CHyprOpenGLImpl::setMonitorTransformEnabled(bool enabled) {

View file

@ -14,6 +14,9 @@
#include "Texture.hpp"
#include "Framebuffer.hpp"
#include "Transformer.hpp"
#include "Renderbuffer.hpp"
#include <GLES2/gl2ext.h>
#include "../debug/TracyDefines.hpp"
@ -39,7 +42,7 @@ struct SRenderModifData {
};
struct SMonitorRenderData {
CFramebuffer primaryFB;
CFramebuffer offloadFB;
CFramebuffer mirrorFB; // these are used for some effects,
CFramebuffer mirrorSwapFB; // etc
CFramebuffer offMainFB;
@ -80,6 +83,7 @@ struct SCurrentRenderData {
SMonitorRenderData* pCurrentMonData = nullptr;
CFramebuffer* currentFB = nullptr;
CFramebuffer* mainFB = nullptr;
CRegion damage;
@ -105,7 +109,6 @@ class CHyprOpenGLImpl {
void begin(CMonitor*, CRegion*, bool fake = false);
void end();
void bindWlrOutputFb();
void renderRect(CBox*, const CColor&, int round = 0);
void renderRectWithBlur(CBox*, const CColor&, int round = 0, float blurA = 1.f);
@ -157,7 +160,6 @@ class CHyprOpenGLImpl {
SCurrentRenderData m_RenderData;
GLint m_iCurrentOutputFb = 0;
GLint m_iWLROutputFb = 0;
bool m_bReloadScreenShader = true; // at launch it can be set
@ -169,6 +171,11 @@ class CHyprOpenGLImpl {
std::unordered_map<CMonitor*, SMonitorRenderData> m_mMonitorRenderResources;
std::unordered_map<CMonitor*, CTexture> m_mMonitorBGTextures;
struct {
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr;
} m_sProc;
private:
std::list<GLuint> m_lBuffers;
std::list<GLuint> m_lTextures;
@ -176,10 +183,11 @@ class CHyprOpenGLImpl {
int m_iDRMFD;
std::string m_szExtensions;
bool m_bFakeFrame = false;
bool m_bEndFrame = false;
bool m_bApplyFinalShader = false;
bool m_bBlend = false;
bool m_bFakeFrame = false;
bool m_bEndFrame = false;
bool m_bApplyFinalShader = false;
bool m_bBlend = false;
bool m_bOffloadedFramebuffer = false;
CShader m_sFinalScreenShader;
CTimer m_tGlobalTimer;

View file

@ -0,0 +1,81 @@
#include "Renderbuffer.hpp"
#include "OpenGL.hpp"
#include "../Compositor.hpp"
#include <dlfcn.h>
CRenderbuffer::~CRenderbuffer() {
if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL))
eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
m_sFramebuffer.release();
glDeleteRenderbuffers(1, &m_iRBO);
g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(wlr_egl_get_display(g_pCompositor->m_sWLREGL), m_iImage);
}
CRenderbuffer::CRenderbuffer(wlr_buffer* buffer, uint32_t format) : m_pWlrBuffer(buffer) {
// EVIL, but we can't include a hidden header because nixos is fucking special
static EGLImageKHR (*PWLREGLCREATEIMAGEFROMDMABUF)(wlr_egl*, wlr_dmabuf_attributes*, bool*);
static bool symbolFound = false;
if (!symbolFound) {
PWLREGLCREATEIMAGEFROMDMABUF = reinterpret_cast<EGLImageKHR (*)(wlr_egl*, wlr_dmabuf_attributes*, bool*)>(dlsym(RTLD_DEFAULT, "wlr_egl_create_image_from_dmabuf"));
symbolFound = true;
RASSERT(PWLREGLCREATEIMAGEFROMDMABUF, "wlr_egl_create_image_from_dmabuf was not found in wlroots!");
Debug::log(LOG, "CRenderbuffer: wlr_egl_create_image_from_dmabuf found at {:x}", (uintptr_t)PWLREGLCREATEIMAGEFROMDMABUF);
}
// end evil hack
struct wlr_dmabuf_attributes dmabuf = {0};
if (!wlr_buffer_get_dmabuf(buffer, &dmabuf))
throw std::runtime_error("wlr_buffer_get_dmabuf failed");
bool externalOnly;
m_iImage = PWLREGLCREATEIMAGEFROMDMABUF(g_pCompositor->m_sWLREGL, &dmabuf, &externalOnly);
if (m_iImage == EGL_NO_IMAGE_KHR)
throw std::runtime_error("wlr_egl_create_image_from_dmabuf failed");
glGenRenderbuffers(1, &m_iRBO);
glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO);
g_pHyprOpenGL->m_sProc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)m_iImage);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &m_sFramebuffer.m_iFb);
m_sFramebuffer.m_vSize = {buffer->width, buffer->height};
m_sFramebuffer.bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_iRBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("rbo: glCheckFramebufferStatus failed");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
hyprListener_destroyBuffer.initCallback(
&buffer->events.destroy, [](void* owner, void* data) { g_pHyprRenderer->onRenderbufferDestroy((CRenderbuffer*)owner); }, this, "CRenderbuffer");
}
void CRenderbuffer::bind() {
glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO);
bindFB();
}
void CRenderbuffer::bindFB() {
m_sFramebuffer.bind();
}
void CRenderbuffer::unbind() {
glBindRenderbuffer(GL_RENDERBUFFER, 0);
#ifndef GLES2
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#else
glBindFramebuffer(GL_FRAMEBUFFER, 0);
#endif
}
CFramebuffer* CRenderbuffer::getFB() {
return &m_sFramebuffer;
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "Framebuffer.hpp"
class CMonitor;
class CRenderbuffer {
public:
CRenderbuffer(wlr_buffer* buffer, uint32_t format);
~CRenderbuffer();
void bind();
void bindFB();
void unbind();
CFramebuffer* getFB();
wlr_buffer* m_pWlrBuffer = nullptr;
DYNLISTENER(destroyBuffer);
private:
EGLImageKHR m_iImage = 0;
GLuint m_iRBO = 0;
CFramebuffer m_sFramebuffer;
};

View file

@ -845,8 +845,8 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) {
}
void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
static std::chrono::high_resolution_clock::time_point startRender = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point startRenderOverlay = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now();
static auto* const PDEBUGOVERLAY = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue;
@ -887,7 +887,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
firstLaunchAnimActive = false;
}
startRender = std::chrono::high_resolution_clock::now();
renderStart = std::chrono::high_resolution_clock::now();
if (*PDEBUGOVERLAY == 1) {
g_pDebugOverlay->frameData(pMonitor);
@ -991,7 +991,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
// check the damage
CRegion damage;
bool hasChanged = pMonitor->output->needs_frame || pixman_region32_not_empty(&pMonitor->damage.current);
int bufferAge;
if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0)
return;
@ -1007,16 +1006,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, true);
if (!wlr_output_attach_render(pMonitor->output, &bufferAge)) {
Debug::log(ERR, "Couldn't attach render to display {} ???", pMonitor->szName);
if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false);
return;
}
wlr_damage_ring_get_buffer_damage(&pMonitor->damage, bufferAge, damage.pixman());
wlr_damage_ring_get_buffer_damage(&pMonitor->damage, m_iLastBufferAge, damage.pixman());
pMonitor->renderingActive = true;
@ -1062,7 +1052,25 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
TRACY_GPU_ZONE("Render");
g_pHyprOpenGL->begin(pMonitor, &damage);
if (pMonitor == g_pCompositor->getMonitorFromCursor())
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY);
else
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f;
if (zoomInFactorFirstLaunch > 1.f) {
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = zoomInFactorFirstLaunch;
g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse = false;
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
}
if (!beginRender(pMonitor, damage, RENDER_MODE_NORMAL)) {
Debug::log(ERR, "renderer: couldn't beginRender()!");
if (UNLOCK_SC)
wlr_output_lock_software_cursors(pMonitor->output, false);
return;
}
EMIT_HOOK_EVENT("render", RENDER_BEGIN);
@ -1097,7 +1105,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
// for drawing the debug overlay
if (pMonitor == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) {
startRenderOverlay = std::chrono::high_resolution_clock::now();
renderStartOverlay = std::chrono::high_resolution_clock::now();
g_pDebugOverlay->draw();
endRenderOverlay = std::chrono::high_resolution_clock::now();
}
@ -1118,35 +1126,22 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
renderCursor = renderCursor && shouldRenderCursor();
if (renderCursor && wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y)) {
if (renderCursor) {
TRACY_GPU_ZONE("RenderCursor");
bool lockSoftware = pMonitor == g_pCompositor->getMonitorFromCursor() && *PZOOMFACTOR != 1.f;
if (lockSoftware) {
wlr_output_lock_software_cursors(pMonitor->output, true);
wlr_output_render_software_cursors(pMonitor->output, NULL);
g_pHyprRenderer->renderSoftwareCursors(pMonitor, g_pHyprOpenGL->m_RenderData.damage);
wlr_output_lock_software_cursors(pMonitor->output, false);
} else
wlr_output_render_software_cursors(pMonitor->output, NULL);
wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
}
if (pMonitor == g_pCompositor->getMonitorFromCursor())
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY);
else
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f;
if (zoomInFactorFirstLaunch > 1.f) {
g_pHyprOpenGL->m_RenderData.mouseZoomFactor = zoomInFactorFirstLaunch;
g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse = false;
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
g_pHyprRenderer->renderSoftwareCursors(pMonitor, g_pHyprOpenGL->m_RenderData.damage);
}
EMIT_HOOK_EVENT("render", RENDER_LAST_MOMENT);
g_pHyprOpenGL->end();
endRender();
TRACY_GPU_COLLECT;
@ -1192,12 +1187,12 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) {
pMonitor->pendingFrame = false;
const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f;
const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f;
g_pDebugOverlay->renderData(pMonitor, µs);
if (*PDEBUGOVERLAY == 1) {
if (pMonitor == g_pCompositor->m_vMonitors.front().get()) {
const float µsNoOverlay = µs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - startRenderOverlay).count() / 1000.f;
const float µsNoOverlay = µs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - renderStartOverlay).count() / 1000.f;
g_pDebugOverlay->renderDataNoOverlay(pMonitor, µsNoOverlay);
} else {
g_pDebugOverlay->renderDataNoOverlay(pMonitor, µs);
@ -1957,6 +1952,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
pMonitor->vecPixelSize = Vector2D(transformedBox.width, transformedBox.height);
}
pMonitor->updateMatrix();
// update renderer (here because it will call rollback, so we cannot do this before committing)
g_pHyprOpenGL->destroyMonitorResources(pMonitor);
@ -2191,3 +2188,99 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) {
// found one!
pMonitor->solitaryClient = PCANDIDATE;
}
void CHyprRenderer::renderSoftwareCursors(CMonitor* pMonitor, const CRegion& damage, std::optional<Vector2D> overridePos) {
const auto CURSORPOS = overridePos.value_or(g_pInputManager->getMouseCoordsInternal() - pMonitor->vecPosition);
wlr_output_cursor* cursor;
wl_list_for_each(cursor, &pMonitor->output->cursors, link) {
if (!cursor->enabled || !cursor->visible || pMonitor->output->hardware_cursor == cursor)
continue;
if (!cursor->texture)
continue;
CBox cursorBox = CBox{CURSORPOS.x, CURSORPOS.y, cursor->width, cursor->height}.translate({-cursor->hotspot_x, -cursor->hotspot_y});
g_pHyprOpenGL->renderTexturePrimitive(cursor->texture, &cursorBox);
}
}
CRenderbuffer* CHyprRenderer::getOrCreateRenderbuffer(wlr_buffer* buffer, uint32_t fmt) {
auto it = std::find_if(m_vRenderbuffers.begin(), m_vRenderbuffers.end(), [&](const auto& other) { return other->m_pWlrBuffer == buffer; });
if (it != m_vRenderbuffers.end())
return it->get();
return m_vRenderbuffers.emplace_back(std::make_unique<CRenderbuffer>(buffer, fmt)).get();
}
bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode, wlr_buffer* withBuffer) {
if (eglGetCurrentContext() != wlr_egl_get_context(g_pCompositor->m_sWLREGL))
eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL));
m_eRenderMode = mode;
g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor; // has to be set cuz allocs
if (mode == RENDER_MODE_FULL_FAKE) {
wlr_output_attach_render(pMonitor->output, nullptr);
g_pHyprOpenGL->begin(pMonitor, &damage, true);
return true;
}
if (!withBuffer) {
if (!wlr_output_configure_primary_swapchain(pMonitor->output, &pMonitor->output->pending, &pMonitor->output->swapchain))
return false;
m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge);
if (!m_pCurrentWlrBuffer)
return false;
} else {
m_pCurrentWlrBuffer = withBuffer;
}
try {
m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentWlrBuffer, pMonitor->drmFormat);
} catch (std::exception& e) {
wlr_buffer_unlock(m_pCurrentWlrBuffer);
return false;
}
m_pCurrentRenderbuffer->bind();
g_pHyprOpenGL->begin(pMonitor, &damage);
return true;
}
void CHyprRenderer::endRender() {
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
g_pHyprOpenGL->end();
if (m_eRenderMode == RENDER_MODE_FULL_FAKE) {
wlr_output_rollback(PMONITOR->output);
return;
}
glFlush();
if (m_eRenderMode == RENDER_MODE_NORMAL)
wlr_output_state_set_buffer(&PMONITOR->output->pending, m_pCurrentWlrBuffer);
wlr_buffer_unlock(m_pCurrentWlrBuffer);
m_pCurrentRenderbuffer->unbind();
m_pCurrentRenderbuffer = nullptr;
m_pCurrentWlrBuffer = nullptr;
m_iLastBufferAge = 0;
}
void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) {
std::erase_if(m_vRenderbuffers, [&](const auto& rbo) { return rbo.get() == rb; });
}
CRenderbuffer* CHyprRenderer::getCurrentRBO() {
return m_pCurrentRenderbuffer;
}

View file

@ -6,6 +6,7 @@
#include "../helpers/Workspace.hpp"
#include "../Window.hpp"
#include "OpenGL.hpp"
#include "Renderbuffer.hpp"
#include "../helpers/Timer.hpp"
#include "../helpers/Region.hpp"
@ -27,6 +28,13 @@ enum eRenderPassMode
RENDER_PASS_POPUP
};
enum eRenderMode
{
RENDER_MODE_NORMAL = 0,
RENDER_MODE_FULL_FAKE = 1,
RENDER_MODE_TO_BUFFER = 2
};
class CToplevelExportProtocolManager;
class CInputManager;
struct SSessionLockSurface;
@ -58,6 +66,12 @@ class CHyprRenderer {
void recheckSolitaryForMonitor(CMonitor* pMonitor);
void setCursorSurface(wlr_surface* surf, int hotspotX, int hotspotY);
void setCursorFromName(const std::string& name);
void renderSoftwareCursors(CMonitor* pMonitor, const CRegion& damage, std::optional<Vector2D> overridePos = {});
void onRenderbufferDestroy(CRenderbuffer* rb);
CRenderbuffer* getCurrentRBO();
bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, wlr_buffer* withBuffer = nullptr);
void endRender();
bool m_bWindowRequestedCursorHide = false;
bool m_bBlockSurfaceFeedback = false;
@ -89,19 +103,26 @@ class CHyprRenderer {
} m_sLastCursorData;
private:
void arrangeLayerArray(CMonitor*, const std::vector<std::unique_ptr<SLayerSurface>>&, bool, CBox*);
void renderWorkspaceWindowsFullscreen(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
void renderWorkspaceWindows(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special)
void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool ignoreAllGeometry = false);
void renderLayer(SLayerSurface*, CMonitor*, timespec*);
void renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*);
void renderDragIcon(CMonitor*, timespec*);
void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);
void renderWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const CBox& geometry);
void renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f);
void arrangeLayerArray(CMonitor*, const std::vector<std::unique_ptr<SLayerSurface>>&, bool, CBox*);
void renderWorkspaceWindowsFullscreen(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special)
void renderWorkspaceWindows(CMonitor*, CWorkspace*, timespec*); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special)
void renderWindow(CWindow*, CMonitor*, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool ignoreAllGeometry = false);
void renderLayer(SLayerSurface*, CMonitor*, timespec*);
void renderSessionLockSurface(SSessionLockSurface*, CMonitor*, timespec*);
void renderDragIcon(CMonitor*, timespec*);
void renderIMEPopup(SIMEPopup*, CMonitor*, timespec*);
void renderWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const CBox& geometry);
void renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f);
bool m_bHasARenderedCursor = true;
bool m_bCursorHasSurface = false;
bool m_bHasARenderedCursor = true;
bool m_bCursorHasSurface = false;
CRenderbuffer* m_pCurrentRenderbuffer = nullptr;
wlr_buffer* m_pCurrentWlrBuffer = nullptr;
eRenderMode m_eRenderMode = RENDER_MODE_NORMAL;
int m_iLastBufferAge = 0;
CRenderbuffer* getOrCreateRenderbuffer(wlr_buffer* buffer, uint32_t fmt);
std::vector<std::unique_ptr<CRenderbuffer>> m_vRenderbuffers;
friend class CHyprOpenGLImpl;
friend class CToplevelExportProtocolManager;