* watchdog: dont detach and cause race condition instead of detaching and causing a race condition on destruction where the thread is alive and watchdog has been destroyed, check if its joinable and join it on destruction. causes heap use after free on exit of compositor. * render: add checks for compositor shutting down avoid member call on null pointer, if the g_pHyprRenderer is destroyed we can call the member makeEGLCurrent on it, causes undefined behaviour on destruction of the compositor/hyprrenderer. found with ubsan.
129 lines
4.4 KiB
C++
129 lines
4.4 KiB
C++
#include "Texture.hpp"
|
|
#include "Renderer.hpp"
|
|
#include "../Compositor.hpp"
|
|
#include "../protocols/types/Buffer.hpp"
|
|
#include "../helpers/Format.hpp"
|
|
|
|
CTexture::CTexture() {
|
|
// naffin'
|
|
}
|
|
|
|
CTexture::~CTexture() {
|
|
if (m_bNonOwning || !g_pCompositor || g_pCompositor->m_bIsShuttingDown || !g_pHyprRenderer)
|
|
return;
|
|
|
|
g_pHyprRenderer->makeEGLCurrent();
|
|
destroyTexture();
|
|
}
|
|
|
|
CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) {
|
|
g_pHyprRenderer->makeEGLCurrent();
|
|
|
|
const auto format = FormatUtils::getPixelFormatFromDRM(drmFormat);
|
|
ASSERT(format);
|
|
|
|
m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX;
|
|
m_vSize = size_;
|
|
allocate();
|
|
|
|
GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
|
#ifndef GLES2
|
|
if (format->flipRB) {
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED));
|
|
}
|
|
#endif
|
|
GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock));
|
|
GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels));
|
|
GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0));
|
|
GLCALL(glBindTexture(GL_TEXTURE_2D, 0));
|
|
}
|
|
|
|
CTexture::CTexture(wlr_texture* tex) {
|
|
RASSERT(wlr_texture_is_gles2(tex), "wlr_texture provided to CTexture that isn't GLES2!");
|
|
wlr_gles2_texture_attribs attrs;
|
|
wlr_gles2_texture_get_attribs(tex, &attrs);
|
|
|
|
m_iTarget = attrs.target;
|
|
m_iTexID = attrs.tex;
|
|
m_bNonOwning = true;
|
|
|
|
if (m_iTarget == GL_TEXTURE_2D)
|
|
m_iType = attrs.has_alpha ? TEXTURE_RGBA : TEXTURE_RGBX;
|
|
else
|
|
m_iType = TEXTURE_EXTERNAL;
|
|
|
|
m_vSize = Vector2D((int)tex->width, (int)tex->height);
|
|
}
|
|
|
|
CTexture::CTexture(const SDMABUFAttrs& attrs, void* image) {
|
|
if (!g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES) {
|
|
Debug::log(ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES");
|
|
return;
|
|
}
|
|
|
|
m_iTarget = GL_TEXTURE_2D;
|
|
m_iType = TEXTURE_RGBA;
|
|
m_vSize = attrs.size;
|
|
m_iType = FormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA;
|
|
allocate();
|
|
m_pEglImage = image;
|
|
|
|
GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
|
GLCALL(g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES(m_iTarget, image));
|
|
GLCALL(glBindTexture(GL_TEXTURE_2D, 0));
|
|
}
|
|
|
|
void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) {
|
|
g_pHyprRenderer->makeEGLCurrent();
|
|
|
|
const auto format = FormatUtils::getPixelFormatFromDRM(drmFormat);
|
|
ASSERT(format);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_iTexID);
|
|
|
|
auto rects = damage.copy().intersect(CBox{{}, m_vSize}).getRects();
|
|
|
|
#ifndef GLES2
|
|
if (format->flipRB) {
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE));
|
|
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED));
|
|
}
|
|
#endif
|
|
|
|
for (auto& rect : rects) {
|
|
GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock));
|
|
GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1));
|
|
GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1));
|
|
|
|
int width = rect.x2 - rect.x1;
|
|
int height = rect.y2 - rect.y1;
|
|
GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels));
|
|
}
|
|
|
|
GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0));
|
|
GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0));
|
|
GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0));
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
void CTexture::destroyTexture() {
|
|
if (m_iTexID) {
|
|
GLCALL(glDeleteTextures(1, &m_iTexID));
|
|
m_iTexID = 0;
|
|
}
|
|
|
|
if (m_pEglImage)
|
|
g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(wlr_egl_get_display(g_pCompositor->m_sWLREGL), m_pEglImage);
|
|
m_pEglImage = nullptr;
|
|
}
|
|
|
|
void CTexture::allocate() {
|
|
if (!m_iTexID)
|
|
GLCALL(glGenTextures(1, &m_iTexID));
|
|
}
|