#include "Texture.hpp" #include "Renderer.hpp" #include "../Compositor.hpp" #include "../protocols/types/Buffer.hpp" #include "../helpers/Format.hpp" #include CTexture::CTexture() = default; CTexture::~CTexture() { if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) return; g_pHyprRenderer->makeEGLCurrent(); destroyTexture(); } CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy) : m_drmFormat(drmFormat), m_keepDataCopy(keepDataCopy) { createFromShm(drmFormat, pixels, stride, size_); } CTexture::CTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image) { createFromDma(attrs, image); } CTexture::CTexture(const SP buffer, bool keepDataCopy) : m_keepDataCopy(keepDataCopy) { if (!buffer) return; m_opaque = buffer->opaque; auto attrs = buffer->dmabuf(); if (!attrs.success) { // attempt shm auto shm = buffer->shm(); if (!shm.success) { Log::logger->log(Log::ERR, "Cannot create a texture: buffer has no dmabuf or shm"); return; } auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); m_drmFormat = fmt; createFromShm(fmt, pixelData, bufLen, shm.size); return; } auto image = g_pHyprOpenGL->createEGLImage(buffer->dmabuf()); if (!image) { Log::logger->log(Log::ERR, "Cannot create a texture: failed to create an EGLImage"); return; } createFromDma(attrs, image); } void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) { g_pHyprRenderer->makeEGLCurrent(); const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); ASSERT(format); m_type = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; m_size = size_; m_isSynchronous = true; m_target = GL_TEXTURE_2D; allocate(); bind(); setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (format->swizzle.has_value()) swizzle(format->swizzle.value()); bool alignmentChanged = false; if (format->bytesPerBlock != 4) { const GLint alignment = (stride % 4 == 0) ? 4 : 1; GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); alignmentChanged = true; } 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)); if (alignmentChanged) GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); unbind(); if (m_keepDataCopy) { m_dataCopy.resize(stride * size_.y); memcpy(m_dataCopy.data(), pixels, stride * size_.y); } } void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) { if (!g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES) { Log::logger->log(Log::ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); return; } m_opaque = NFormatUtils::isFormatOpaque(attrs.format); // #TODO external only formats should be external aswell. // also needs a seperate color shader. /*if (NFormatUtils::isFormatYUV(attrs.format)) { m_target = GL_TEXTURE_EXTERNAL_OES; m_type = TEXTURE_EXTERNAL; } else {*/ m_target = GL_TEXTURE_2D; m_type = NFormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; //} m_size = attrs.size; allocate(); m_eglImage = image; bind(); setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GLCALL(g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES(m_target, image)); unbind(); } void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { if (damage.empty()) return; g_pHyprRenderer->makeEGLCurrent(); const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); ASSERT(format); bind(); if (format->swizzle.has_value()) swizzle(format->swizzle.value()); bool alignmentChanged = false; if (format->bytesPerBlock != 4) { const GLint alignment = (stride % 4 == 0) ? 4 : 1; GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); alignmentChanged = true; } GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); damage.copy().intersect(CBox{{}, m_size}).forEachRect([&format, &pixels](const auto& rect) { 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)); }); if (alignmentChanged) GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); unbind(); if (m_keepDataCopy) { m_dataCopy.resize(stride * m_size.y); memcpy(m_dataCopy.data(), pixels, stride * m_size.y); } } void CTexture::destroyTexture() { if (m_texID) { GLCALL(glDeleteTextures(1, &m_texID)); m_texID = 0; } if (m_eglImage) g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_eglImage); m_eglImage = nullptr; m_cachedStates.fill(std::nullopt); } void CTexture::allocate() { if (!m_texID) GLCALL(glGenTextures(1, &m_texID)); } const std::vector& CTexture::dataCopy() { return m_dataCopy; } void CTexture::bind() { GLCALL(glBindTexture(m_target, m_texID)); } void CTexture::unbind() { GLCALL(glBindTexture(m_target, 0)); } constexpr std::optional CTexture::getCacheStateIndex(GLenum pname) { switch (pname) { case GL_TEXTURE_WRAP_S: return TEXTURE_PAR_WRAP_S; case GL_TEXTURE_WRAP_T: return TEXTURE_PAR_WRAP_T; case GL_TEXTURE_MAG_FILTER: return TEXTURE_PAR_MAG_FILTER; case GL_TEXTURE_MIN_FILTER: return TEXTURE_PAR_MIN_FILTER; case GL_TEXTURE_SWIZZLE_R: return TEXTURE_PAR_SWIZZLE_R; case GL_TEXTURE_SWIZZLE_B: return TEXTURE_PAR_SWIZZLE_B; default: return std::nullopt; } } void CTexture::setTexParameter(GLenum pname, GLint param) { const auto cacheIndex = getCacheStateIndex(pname); if (!cacheIndex) { GLCALL(glTexParameteri(m_target, pname, param)); return; } const auto idx = cacheIndex.value(); if (m_cachedStates[idx] == param) return; m_cachedStates[idx] = param; GLCALL(glTexParameteri(m_target, pname, param)); } void CTexture::swizzle(const std::array& colors) { setTexParameter(GL_TEXTURE_SWIZZLE_R, colors.at(0)); setTexParameter(GL_TEXTURE_SWIZZLE_G, colors.at(1)); setTexParameter(GL_TEXTURE_SWIZZLE_B, colors.at(2)); setTexParameter(GL_TEXTURE_SWIZZLE_A, colors.at(3)); }