From 20031cea92417a852410827a5cdb4adbcaee4342 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:15:51 +0000 Subject: [PATCH] pointer: add drm dumb buffers for cursors (#8399) --------- Co-authored-by: Mihai Fufezan --- CMakeLists.txt | 2 +- meson.build | 2 +- src/config/ConfigManager.cpp | 2 +- src/managers/PointerManager.cpp | 132 ++++++++++++++++++------------ src/protocols/core/Compositor.cpp | 26 ++++-- src/protocols/core/Compositor.hpp | 2 +- src/render/Texture.cpp | 21 ++++- src/render/Texture.hpp | 34 ++++---- 8 files changed, 142 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 117932d0..1cbd8b0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4) -pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.2) +pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5) add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}") diff --git a/meson.build b/meson.build index e4abad36..d825f184 100644 --- a/meson.build +++ b/meson.build @@ -31,7 +31,7 @@ if cpp_compiler.check_header('execinfo.h') add_project_arguments('-DHAS_EXECINFO', language: 'cpp') endif -aquamarine = dependency('aquamarine', version: '>=0.4.2') +aquamarine = dependency('aquamarine', version: '>=0.4.5') add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp') xcb_dep = dependency('xcb', required: get_option('xwayland')) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index e3d59874..10299277 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -571,7 +571,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("cursor:sync_gsettings_theme", Hyprlang::INT{1}); m_pConfig->addConfigValue("cursor:hide_on_key_press", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:hide_on_touch", Hyprlang::INT{1}); - m_pConfig->addConfigValue("cursor:allow_dumb_copy", Hyprlang::INT{0}); + m_pConfig->addConfigValue("cursor:use_cpu_buffer", Hyprlang::INT{0}); m_pConfig->addConfigValue("autogenerated", Hyprlang::INT{0}); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 75be235f..0580c9be 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -375,6 +375,8 @@ SP CPointerManager::renderHWCursorBuffer(SPmonitor->output->cursorPlaneSize(); auto const& cursorSize = currentCursorImage.size; + static auto PDUMB = CConfigValue("cursor:use_cpu_buffer"); + if (maxSize == Vector2D{}) return nullptr; @@ -386,10 +388,23 @@ SP CPointerManager::renderHWCursorBuffer(SPmonitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size) { + if (!state->monitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size || + *PDUMB != (state->monitor->cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { - if (!state->monitor->cursorSwapchain) - state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(state->monitor->output->getBackend()->preferredAllocator(), state->monitor->output->getBackend()); + if (!state->monitor->cursorSwapchain || *PDUMB != (state->monitor->cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { + + auto allocator = state->monitor->output->getBackend()->preferredAllocator(); + if (*PDUMB) { + for (const auto& a : state->monitor->output->getBackend()->getAllocators()) { + if (a->type() == Aquamarine::AQ_ALLOCATOR_TYPE_DRM_DUMB) { + allocator = a; + break; + } + } + } + + state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, state->monitor->output->getBackend()); + } auto options = state->monitor->cursorSwapchain->currentOptions(); options.size = maxSize; @@ -397,8 +412,10 @@ SP CPointerManager::renderHWCursorBuffer(SPmonitor->output->getBackend()->preferredAllocator()->drmFD() != g_pCompositor->m_iDRMFD; - // We do not set the format. If it's unset (DRM_FORMAT_INVALID) then the swapchain will pick for us, + // We do not set the format (unless shm). If it's unset (DRM_FORMAT_INVALID) then the swapchain will pick for us, // but if it's set, we don't wanna change it. + if (*PDUMB) + options.format = DRM_FORMAT_ARGB8888; if (!state->monitor->cursorSwapchain->reconfigure(options)) { Debug::log(TRACE, "Failed to reconfigure cursor swapchain"); @@ -420,60 +437,69 @@ SP CPointerManager::renderHWCursorBuffer(SPdataCopy(); + if (texData.empty()) { + if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) { + const auto SURFACE = currentCursorImage.surface->resource(); + auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE); + + bool flipRB = false; + + if (SURFACE->current.texture) { + Debug::log(TRACE, "Cursor CPU surface: format {}, expecting AR24", FormatUtils::drmFormatName(SURFACE->current.texture->m_iDrmFormat)); + if (SURFACE->current.texture->m_iDrmFormat == DRM_FORMAT_ABGR8888) { + Debug::log(TRACE, "Cursor CPU surface format AB24, will flip. WARNING: this will break on big endian!"); + flipRB = true; + } else if (SURFACE->current.texture->m_iDrmFormat != DRM_FORMAT_ARGB8888) { + Debug::log(TRACE, "Cursor CPU surface format rejected, falling back to sw"); + return nullptr; + } + } + + if (shmBuffer.data()) + texData = shmBuffer; + else { + texData.resize(texture->m_vSize.x * 4 * texture->m_vSize.y); + memset(texData.data(), 0x00, texData.size()); + } + + if (flipRB) { + for (size_t i = 0; i < shmBuffer.size(); i += 4) { + std::swap(shmBuffer.at(i), shmBuffer.at(i + 2)); // little-endian!!!!!! + } + } + } else { + Debug::log(TRACE, "Cannot use dumb copy on dmabuf cursor buffers"); + return nullptr; + } + } + + // then, we just yeet it into the dumb buffer + + auto [data, fmt, size] = buf->beginDataPtr(0); + + memset(data, 0, size); + if (buf->dmabuf().size.x > texture->m_vSize.x) { + size_t STRIDE = 4 * texture->m_vSize.x; + for (int i = 0; i < texture->m_vSize.y; i++) + memcpy(data + i * buf->dmabuf().strides[0], texData.data() + i * STRIDE, STRIDE); + } else + memcpy(data, texData.data(), std::min(size, texData.size())); + + buf->endDataPtr(); + + return buf; + } + g_pHyprRenderer->makeEGLCurrent(); g_pHyprOpenGL->m_RenderData.pMonitor = state->monitor; auto RBO = g_pHyprRenderer->getOrCreateRenderbuffer(buf, state->monitor->cursorSwapchain->currentOptions().format); if (!RBO) { Debug::log(TRACE, "Failed to create cursor RB with format {}, mod {}", buf->dmabuf().format, buf->dmabuf().modifier); - static auto PDUMB = CConfigValue("cursor:allow_dumb_copy"); - if (!*PDUMB) - return nullptr; - - auto bufData = buf->beginDataPtr(0); - auto bufPtr = std::get<0>(bufData); - - // clear buffer - memset(bufPtr, 0, std::get<2>(bufData)); - - if (currentCursorImage.pBuffer) { - auto texAttrs = currentCursorImage.pBuffer->shm(); - - if (!texAttrs.success) { - Debug::log(TRACE, "Cannot use dumb copy on dmabuf cursor buffers"); - return nullptr; - } - - auto texData = currentCursorImage.pBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE); - auto texPtr = std::get<0>(texData); - Debug::log(TRACE, "cursor texture {}x{} {} {} {}", texAttrs.size.x, texAttrs.size.y, (void*)texPtr, texAttrs.format, texAttrs.stride); - // copy cursor texture - for (int i = 0; i < texAttrs.size.y; i++) - memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * texAttrs.stride, texAttrs.stride); - } else if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) { - const auto SURFACE = currentCursorImage.surface->resource(); - auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE); - Debug::log(TRACE, "cursor texture pixel data length: {}B", shmBuffer.size()); - - if (shmBuffer.data()) { - // copy cursor texture - // assume format is 32bpp - size_t STRIDE = 4 * SURFACE->current.bufferSize.x; - for (int i = 0; i < SURFACE->current.bufferSize.y; i++) - memcpy(bufPtr + i * buf->dmabuf().strides[0], shmBuffer.data() + i * STRIDE, STRIDE); - } else { - // if there is no data, hide the cursor - memset(bufPtr, '\0', buf->size.x * buf->size.y * 4 /* assume 32bpp */); - } - - } else { - Debug::log(TRACE, "Unsupported cursor buffer/surface, falling back to sw (can't dumb copy)"); - return nullptr; - } - - buf->endDataPtr(); - - return buf; + return nullptr; } RBO->bind(); @@ -773,7 +799,7 @@ SP CPointerManager::getCurrentCursorTexture() { if (currentCursorImage.pBuffer) { if (!currentCursorImage.bufferTex) - currentCursorImage.bufferTex = makeShared(currentCursorImage.pBuffer); + currentCursorImage.bufferTex = makeShared(currentCursorImage.pBuffer, true); return currentCursorImage.bufferTex; } diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 57f61f87..89f2a4cb 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -438,12 +438,13 @@ void CWLSurfaceResource::commitPendingState() { current.texture->m_eTransform = wlTransformToHyprutils(current.transform); if (current.buffer && current.buffer->buffer) { - current.buffer->buffer->update(accumulateCurrentBufferDamage()); + const auto DAMAGE = accumulateCurrentBufferDamage(); + current.buffer->buffer->update(DAMAGE); // if the surface is a cursor, update the shm buffer // TODO: don't update the entire texture - if (role->role() == SURFACE_ROLE_CURSOR) - updateCursorShm(); + if (role->role() == SURFACE_ROLE_CURSOR && !DAMAGE.empty()) + updateCursorShm(DAMAGE); // release the buffer if it's synchronous as update() has done everything thats needed // so we can let the app know we're done. @@ -486,13 +487,12 @@ void CWLSurfaceResource::commitPendingState() { lastBuffer = current.buffer ? current.buffer->buffer : WP{}; } -void CWLSurfaceResource::updateCursorShm() { +void CWLSurfaceResource::updateCursorShm(CRegion damage) { auto buf = current.buffer ? current.buffer->buffer : lastBuffer; if (!buf) return; - // TODO: actually use damage auto& shmData = CCursorSurfaceRole::cursorPixelData(self.lock()); auto shmAttrs = buf->shm(); @@ -501,11 +501,25 @@ void CWLSurfaceResource::updateCursorShm() { return; } + damage.intersect(CBox{0, 0, buf->size.x, buf->size.y}); + // no need to end, shm. auto [pixelData, fmt, bufLen] = buf->beginDataPtr(0); shmData.resize(bufLen); - memcpy(shmData.data(), pixelData, bufLen); + + if (const auto RECTS = damage.getRects(); RECTS.size() == 1 && RECTS.at(0).x2 == buf->size.x && RECTS.at(0).y2 == buf->size.y) + memcpy(shmData.data(), pixelData, bufLen); + else { + for (auto& box : damage.getRects()) { + for (auto y = box.y1; y < box.y2; ++y) { + // bpp is 32 INSALLAH + auto begin = 4 * box.y1 * (box.x2 - box.x1) + box.x1; + auto len = 4 * (box.x2 - box.x1); + memcpy((uint8_t*)shmData.data() + begin, (uint8_t*)pixelData + begin, len); + } + } + } } void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor) { diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index e5bdf082..c036041a 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -148,7 +148,7 @@ class CWLSurfaceResource { void dropCurrentBuffer(); void commitPendingState(); void bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data); - void updateCursorShm(); + void updateCursorShm(CRegion damage = CBox{0, 0, INT16_MAX, INT16_MAX}); friend class CWLPointerResource; }; diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 91e70afa..633f0212 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -3,6 +3,7 @@ #include "../Compositor.hpp" #include "../protocols/types/Buffer.hpp" #include "../helpers/Format.hpp" +#include CTexture::CTexture() { // naffin' @@ -16,7 +17,7 @@ CTexture::~CTexture() { destroyTexture(); } -CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) { +CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy) : m_iDrmFormat(drmFormat), m_bKeepDataCopy(keepDataCopy) { createFromShm(drmFormat, pixels, stride, size_); } @@ -24,7 +25,7 @@ CTexture::CTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image) { createFromDma(attrs, image); } -CTexture::CTexture(const SP buffer) { +CTexture::CTexture(const SP buffer, bool keepDataCopy) : m_bKeepDataCopy(keepDataCopy) { if (!buffer) return; @@ -43,6 +44,8 @@ CTexture::CTexture(const SP buffer) { auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); + m_iDrmFormat = fmt; + createFromShm(fmt, pixelData, bufLen, shm.size); return; } @@ -80,6 +83,11 @@ void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t strid 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)); + + if (m_bKeepDataCopy) { + m_vDataCopy.resize(stride * size_.y); + memcpy(m_vDataCopy.data(), pixels, stride * size_.y); + } } void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) { @@ -135,6 +143,11 @@ void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, cons GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); glBindTexture(GL_TEXTURE_2D, 0); + + if (m_bKeepDataCopy) { + m_vDataCopy.resize(stride * m_vSize.y); + memcpy(m_vDataCopy.data(), pixels, stride * m_vSize.y); + } } void CTexture::destroyTexture() { @@ -152,3 +165,7 @@ void CTexture::allocate() { if (!m_iTexID) GLCALL(glGenTextures(1, &m_iTexID)); } + +const std::vector& CTexture::dataCopy() { + return m_vDataCopy; +} diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index e0ef5503..ae949d5f 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -24,26 +24,32 @@ class CTexture { CTexture(const CTexture&&) = delete; CTexture(const CTexture&) = delete; - CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); + CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false); - CTexture(const SP buffer); + CTexture(const SP buffer, bool keepDataCopy = false); // this ctor takes ownership of the eglImage. CTexture(const Aquamarine::SDMABUFAttrs&, void* image); ~CTexture(); - void destroyTexture(); - void allocate(); - void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); + void destroyTexture(); + void allocate(); + void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); + const std::vector& dataCopy(); - TEXTURETYPE m_iType = TEXTURE_RGBA; - GLenum m_iTarget = GL_TEXTURE_2D; - GLuint m_iTexID = 0; - Vector2D m_vSize = {}; - void* m_pEglImage = nullptr; - eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; - bool m_bOpaque = false; + TEXTURETYPE m_iType = TEXTURE_RGBA; + GLenum m_iTarget = GL_TEXTURE_2D; + GLuint m_iTexID = 0; + Vector2D m_vSize = {}; + void* m_pEglImage = nullptr; + eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_bOpaque = false; + uint32_t m_iDrmFormat = 0; // for shm private: - void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); - void createFromDma(const Aquamarine::SDMABUFAttrs&, void* image); + void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); + void createFromDma(const Aquamarine::SDMABUFAttrs&, void* image); + + bool m_bKeepDataCopy = false; + + std::vector m_vDataCopy; }; \ No newline at end of file