From 0342863df2c45b5c1f3d2caba3fb9cb498ad42d6 Mon Sep 17 00:00:00 2001 From: Tom Englund Date: Fri, 25 Jul 2025 15:20:44 +0200 Subject: [PATCH] texture: avoid issuing many glTexSubImage2D calls applications that use QtWidgets like dolphin adds each folder item or individual file to the damage region and gives us a lot of different regions. issuing multiple glTexSubImage2D calls is slow but on the other hand smaller regions can still be faster by simply calling glTexSubImage2D on each of them. the tradeoff is uploading the extent region a few pixels that perhaps wasnt damaged yet again. but should be fine doing so. so if case the rects size and pack the buffer extents and upload it in one call, and if few rects do it as before. --- src/render/Texture.cpp | 66 +++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index b97977512db..658f02be8ba 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -112,7 +112,6 @@ void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { g_pHyprRenderer->makeEGLCurrent(); - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); ASSERT(format); @@ -123,25 +122,64 @@ void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, cons setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); } - damage.copy().intersect(CBox{{}, m_size}).forEachRect([&format, &stride, &pixels](const auto& rect) { - 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)); + auto clippedDamage = damage.copy().intersect(CBox{{}, m_size}); + const auto rects = clippedDamage.getRects(); + const auto pixelSize = format->bytesPerBlock; - 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)); - }); + constexpr size_t MAX_RECTS_BEFORE_PACKING = 2; + const auto usePackedUpload = rects.size() > MAX_RECTS_BEFORE_PACKING; - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); + if (usePackedUpload) { + const auto extents = clippedDamage.getExtents(); + const auto xMin = static_cast(extents.x); + const auto yMin = static_cast(extents.y); + const auto width = static_cast(extents.width); + const auto height = static_cast(extents.height); + + if (width <= 0 || height <= 0) { + unbind(); + return; + } + + const auto rowPitch = static_cast(width) * pixelSize; + std::vector packedBuffer(static_cast(height) * rowPitch); + + for (int32_t row = 0; row < height; ++row) { + const auto* src = pixels + (static_cast(yMin + row) * stride) + (static_cast(xMin) * pixelSize); + auto* dst = packedBuffer.data() + (static_cast(row) * rowPitch); + + std::memcpy(dst, src, rowPitch); + } + + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, xMin, yMin, width, height, format->glFormat, format->glType, packedBuffer.data())); + } else { + for (const auto& rect : rects) { + const auto x = rect.x1; + const auto y = rect.y1; + const auto width = rect.x2 - rect.x1; + const auto height = rect.y2 - rect.y1; + + if (width <= 0 || height <= 0) + continue; + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / pixelSize)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, x)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, y)); + + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 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)); + } unbind(); if (m_keepDataCopy) { - m_dataCopy.resize(stride * m_size.y); - memcpy(m_dataCopy.data(), pixels, stride * m_size.y); + const size_t fullSize = static_cast(stride) * m_size.y; + m_dataCopy.resize(fullSize); + std::memcpy(m_dataCopy.data(), pixels, fullSize); } }