drm/renderer: depend on gles3 and use vertex array objects (#173)

* drm/renderer: use gles3 vertex array objects

depend on gles3 and use vertex array objects in shaders, and upon drawing
only bind/unbind the vao instead of calling glVertexAttribPointer every blit.

* renderer: ensure same program is used once

ensure we only call glUseProgram on same program once.

* renderer: remove gles2 context creation

we are now using gles3 functions that will not work in a gles2 context,
remove any attempts of using it.

* renderer: move SShader functions to source

move SShader out of gl struct and move createVao and destructor to
source file.
This commit is contained in:
Tom Englund 2025-05-21 23:54:09 +02:00 committed by GitHub
parent 9d38b6a888
commit 389372c5f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 100 additions and 63 deletions

View file

@ -19,7 +19,7 @@ set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED COMPONENTS "GLES2")
find_package(OpenGL REQUIRED COMPONENTS "GLES3")
find_package(hyprwayland-scanner 0.4.0 REQUIRED)
pkg_check_modules(
deps

View file

@ -169,6 +169,54 @@ static bool drmDeviceHasName(const drmDevice* device, const std::string& name) {
// -------------------
CDRMRenderer::SShader::~SShader() {
if (program == 0)
return;
if (shaderVao)
glDeleteVertexArrays(1, &shaderVao);
if (shaderVboPos)
glDeleteBuffers(1, &shaderVboPos);
if (shaderVboUv)
glDeleteBuffers(1, &shaderVboUv);
glDeleteProgram(program);
program = 0;
}
void CDRMRenderer::SShader::createVao() {
const float fullVerts[] = {
1, 0, // top right
0, 0, // top left
1, 1, // bottom right
0, 1, // bottom left
};
glGenVertexArrays(1, &shaderVao);
glBindVertexArray(shaderVao);
if (posAttrib != -1) {
glGenBuffers(1, &shaderVboPos);
glBindBuffer(GL_ARRAY_BUFFER, shaderVboPos);
glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts, GL_STATIC_DRAW);
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
if (texAttrib != -1) {
glGenBuffers(1, &shaderVboUv);
glBindBuffer(GL_ARRAY_BUFFER, shaderVboUv);
glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts, GL_STATIC_DRAW);
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
EGLDeviceEXT CDRMRenderer::eglDeviceFromDRMFD(int drmFD) {
EGLint nDevices = 0;
if (!proc.eglQueryDevicesEXT(0, nullptr, &nDevices)) {
@ -249,6 +297,13 @@ std::optional<std::vector<std::pair<uint64_t, bool>>> CDRMRenderer::getModsForFo
return result;
}
void CDRMRenderer::useProgram(GLuint prog) {
if (m_currentProgram == prog)
return;
GLCALL(glUseProgram(prog));
}
bool CDRMRenderer::initDRMFormats() {
std::vector<EGLint> formats;
@ -367,7 +422,7 @@ void CDRMRenderer::loadEGLAPI() {
RASSERT(eglBindAPI(EGL_OPENGL_ES_API) != EGL_FALSE, "Couldn't bind to EGL's opengl ES API. This means your gpu driver f'd up. This is not a Hyprland or Aquamarine issue.");
}
void CDRMRenderer::initContext(bool GLES2) {
void CDRMRenderer::initContext() {
RASSERT(egl.display != nullptr && egl.display != EGL_NO_DISPLAY, "CDRMRenderer: Can't create EGL context without display");
EGLint major, minor;
@ -402,27 +457,15 @@ void CDRMRenderer::initContext(bool GLES2) {
auto attrsNoVer = attrs;
if (GLES2) {
attrs.push_back(EGL_CONTEXT_MAJOR_VERSION);
attrs.push_back(2);
attrs.push_back(EGL_CONTEXT_MINOR_VERSION);
attrs.push_back(0);
} else {
attrs.push_back(EGL_CONTEXT_MAJOR_VERSION);
attrs.push_back(3);
attrs.push_back(EGL_CONTEXT_MINOR_VERSION);
attrs.push_back(2);
}
attrs.push_back(EGL_CONTEXT_MAJOR_VERSION);
attrs.push_back(3);
attrs.push_back(EGL_CONTEXT_MINOR_VERSION);
attrs.push_back(2);
attrs.push_back(EGL_NONE);
egl.context = eglCreateContext(egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
if (egl.context == EGL_NO_CONTEXT) {
if (GLES2) {
backend->log(AQ_LOG_ERROR, "CDRMRenderer: Can't create renderer, eglCreateContext failed with GLES 2.0");
return;
}
backend->log(AQ_LOG_ERROR, "CDRMRenderer: eglCreateContext failed with GLES 3.2, retrying GLES 3.0");
attrs = attrsNoVer;
@ -460,7 +503,7 @@ void CDRMRenderer::initContext(bool GLES2) {
free(drmName);
}
backend->log(AQ_LOG_DEBUG, std::format("Creating {}CDRMRenderer on gpu {}", GLES2 ? "GLES2 " : "", gpuName));
backend->log(AQ_LOG_DEBUG, std::format("Creating CDRMRenderer on gpu {}", gpuName));
backend->log(AQ_LOG_DEBUG, std::format("Using: {}", (char*)glGetString(GL_VERSION)));
backend->log(AQ_LOG_DEBUG, std::format("Vendor: {}", (char*)glGetString(GL_VENDOR)));
backend->log(AQ_LOG_DEBUG, std::format("Renderer: {}", (char*)glGetString(GL_RENDERER)));
@ -476,26 +519,28 @@ void CDRMRenderer::initResources() {
if (!exts.EXT_image_dma_buf_import || !initDRMFormats())
backend->log(AQ_LOG_ERROR, "CDRMRenderer: initDRMFormats failed, dma-buf won't work");
gl.shader.program = createProgram(VERT_SRC, FRAG_SRC);
if (gl.shader.program == 0)
shader.program = createProgram(VERT_SRC, FRAG_SRC);
if (shader.program == 0)
backend->log(AQ_LOG_ERROR, "CDRMRenderer: texture shader failed");
gl.shader.proj = glGetUniformLocation(gl.shader.program, "proj");
gl.shader.posAttrib = glGetAttribLocation(gl.shader.program, "pos");
gl.shader.texAttrib = glGetAttribLocation(gl.shader.program, "texcoord");
gl.shader.tex = glGetUniformLocation(gl.shader.program, "tex");
shader.proj = glGetUniformLocation(shader.program, "proj");
shader.posAttrib = glGetAttribLocation(shader.program, "pos");
shader.texAttrib = glGetAttribLocation(shader.program, "texcoord");
shader.tex = glGetUniformLocation(shader.program, "tex");
shader.createVao();
gl.shaderExt.program = createProgram(VERT_SRC, FRAG_SRC_EXT);
if (gl.shaderExt.program == 0)
shaderExt.program = createProgram(VERT_SRC, FRAG_SRC_EXT);
if (shaderExt.program == 0)
backend->log(AQ_LOG_ERROR, "CDRMRenderer: external texture shader failed");
gl.shaderExt.proj = glGetUniformLocation(gl.shaderExt.program, "proj");
gl.shaderExt.posAttrib = glGetAttribLocation(gl.shaderExt.program, "pos");
gl.shaderExt.texAttrib = glGetAttribLocation(gl.shaderExt.program, "texcoord");
gl.shaderExt.tex = glGetUniformLocation(gl.shaderExt.program, "tex");
shaderExt.proj = glGetUniformLocation(shaderExt.program, "proj");
shaderExt.posAttrib = glGetAttribLocation(shaderExt.program, "pos");
shaderExt.texAttrib = glGetAttribLocation(shaderExt.program, "texcoord");
shaderExt.tex = glGetUniformLocation(shaderExt.program, "tex");
shaderExt.createVao();
}
SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, int drmFD, bool GLES2) {
SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, int drmFD) {
SP<CDRMRenderer> renderer = SP<CDRMRenderer>(new CDRMRenderer());
renderer->drmFD = drmFD;
renderer->backend = backend_;
@ -528,7 +573,7 @@ SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, int drmFD, bool GL
return nullptr;
}
renderer->initContext(GLES2);
renderer->initContext();
if (renderer->egl.context == nullptr || renderer->egl.context == EGL_NO_CONTEXT)
return nullptr;
@ -537,7 +582,7 @@ SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, int drmFD, bool GL
return renderer;
}
SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, bool GLES2) {
SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_) {
SP<CDRMRenderer> renderer = SP<CDRMRenderer>(new CDRMRenderer());
renderer->drmFD = allocator_->drmFD();
renderer->backend = backend_;
@ -564,7 +609,7 @@ SP<CDRMRenderer> CDRMRenderer::attempt(SP<CBackend> backend_, Hyprutils::Memory:
return nullptr;
}
renderer->initContext(GLES2);
renderer->initContext();
if (renderer->egl.context == nullptr || renderer->egl.context == EGL_NO_CONTEXT)
return nullptr;
@ -735,13 +780,6 @@ void CDRMRenderer::readBuffer(Hyprutils::Memory::CSharedPointer<IBuf
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
inline const float fullVerts[] = {
1, 0, // top right
0, 0, // top left
1, 1, // bottom right
0, 1, // bottom left
};
void CDRMRenderer::waitOnSync(int fd) {
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (waitOnSync): attempting to wait on fd {}", fd)));
@ -984,7 +1022,7 @@ CDRMRenderer::SBlitResult CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to, S
float monitorProj[9];
matrixIdentity(base);
auto& SHADER = fromTex.target == GL_TEXTURE_2D ? gl.shader : gl.shaderExt;
auto& SHADER = fromTex.target == GL_TEXTURE_2D ? shader : shaderExt;
// KMS uses flipped y, we have to do FLIPPED_180
matrixTranslate(base, toDma.size.x / 2.0, toDma.size.y / 2.0);
@ -1009,7 +1047,7 @@ CDRMRenderer::SBlitResult CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to, S
if (!intermediateBuf.empty())
GLCALL(glTexImage2D(fromTex.target, 0, PIXEL_BUFFER_FORMAT, fromDma.size.x, fromDma.size.y, 0, PIXEL_BUFFER_FORMAT, GL_UNSIGNED_BYTE, intermediateBuf.data()));
GLCALL(glUseProgram(SHADER.program));
useProgram(SHADER.program);
GLCALL(glDisable(GL_BLEND));
GLCALL(glDisable(GL_SCISSOR_TEST));
@ -1017,18 +1055,11 @@ CDRMRenderer::SBlitResult CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to, S
GLCALL(glUniformMatrix3fv(SHADER.proj, 1, GL_FALSE, glMtx));
GLCALL(glUniform1i(SHADER.tex, 0));
GLCALL(glVertexAttribPointer(SHADER.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));
GLCALL(glVertexAttribPointer(SHADER.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));
GLCALL(glEnableVertexAttribArray(SHADER.posAttrib));
GLCALL(glEnableVertexAttribArray(SHADER.texAttrib));
GLCALL(glBindVertexArray(SHADER.shaderVao));
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
GLCALL(glDisableVertexAttribArray(SHADER.posAttrib));
GLCALL(glDisableVertexAttribArray(SHADER.texAttrib));
GLCALL(glBindVertexArray(0));
GLCALL(glBindTexture(fromTex.target, 0));
// get an explicit sync fd for the secondary gpu.

View file

@ -4,7 +4,9 @@
#include "FormatUtils.hpp"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#define __gl2_h_ // define guard for gl2ext.h
#include <GLES2/gl2ext.h>
#include <gbm.h>
#include <optional>
@ -66,9 +68,9 @@ namespace Aquamarine {
public:
~CDRMRenderer();
static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_, int drmFD, bool GLES2 = true);
static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_, int drmFD);
static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_,
Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, bool GLES2 = true);
Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_);
int drmFD = -1;
@ -84,12 +86,14 @@ namespace Aquamarine {
void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment);
struct {
struct SShader {
GLuint program = 0;
GLint proj = -1, tex = -1, posAttrib = -1, texAttrib = -1;
} shader, shaderExt;
} gl;
struct SShader {
~SShader();
void createVao();
GLuint program = 0;
GLint proj = -1, tex = -1, posAttrib = -1, texAttrib = -1;
GLuint shaderVao = 0, shaderVboPos = 0, shaderVboUv = 0;
} shader, shaderExt;
struct {
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
@ -144,11 +148,13 @@ namespace Aquamarine {
void loadEGLAPI();
EGLDeviceEXT eglDeviceFromDRMFD(int drmFD);
void initContext(bool GLES2);
void initContext();
void initResources();
bool initDRMFormats();
std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
bool hasModifiers = false;
void useProgram(GLuint prog);
GLuint m_currentProgram = 0;
Hyprutils::Memory::CWeakPointer<CBackend> backend;