diff --git a/app/streaming/video/ffmpeg-renderers/drm.cpp b/app/streaming/video/ffmpeg-renderers/drm.cpp index edec022f..2e8dd8d4 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.cpp +++ b/app/streaming/video/ffmpeg-renderers/drm.cpp @@ -1480,25 +1480,15 @@ void DrmRenderer::blitOverlayToCompositionSurface(Overlay::OverlayType type, SDL } } - // Dirty the modified portion of the FB - drmModeClip clip; - clip.x1 = overlayUnionRect.x; - clip.x2 = overlayUnionRect.x + overlayUnionRect.w; - clip.y1 = overlayUnionRect.y; - clip.y2 = overlayUnionRect.y + overlayUnionRect.h; - drmModeDirtyFB(m_DrmFd, m_OverlayCompositionSurfaceFbId, &clip, 1); + // Dirty the modified portion of the plane + m_PropSetter.damagePlane(m_OverlayPlanes[0], overlayUnionRect); } else { // Clear the pixels where this overlay was drawn before SDL_FillRect(m_OverlayCompositionSurface, &m_OverlayRects[type], 0); - // Dirty the modified portion of the FB - drmModeClip clip; - clip.x1 = m_OverlayRects[type].x; - clip.x2 = m_OverlayRects[type].x + m_OverlayRects[type].w; - clip.y1 = m_OverlayRects[type].y; - clip.y2 = m_OverlayRects[type].y + m_OverlayRects[type].h; - drmModeDirtyFB(m_DrmFd, m_OverlayCompositionSurfaceFbId, &clip, 1); + // Dirty the modified portion of the plane + m_PropSetter.damagePlane(m_OverlayPlanes[0], m_OverlayRects[type]); } } diff --git a/app/streaming/video/ffmpeg-renderers/drm.h b/app/streaming/video/ffmpeg-renderers/drm.h index 850e0db6..492efd5f 100644 --- a/app/streaming/video/ffmpeg-renderers/drm.h +++ b/app/streaming/video/ffmpeg-renderers/drm.h @@ -213,6 +213,8 @@ class DrmRenderer : public IFFmpegRenderer { // Atomic only uint32_t pendingFbId; uint32_t pendingDumbBuffer; + uint32_t fbDamageClipsPropId; + std::vector pendingFbDamageRects; bool modified; }; @@ -270,7 +272,7 @@ class DrmRenderer : public IFFmpegRenderer { } } - bool set(const DrmProperty& prop, uint64_t value, bool verbose = true) { + int set(uint32_t objectId, uint32_t objectType, uint32_t propId, uint64_t value) { int err; if (m_Atomic) { @@ -281,7 +283,7 @@ class DrmRenderer : public IFFmpegRenderer { m_AtomicReq = drmModeAtomicAlloc(); } - err = drmModeAtomicAddProperty(m_AtomicReq, prop.objectId(), prop.id(), value); + err = drmModeAtomicAddProperty(m_AtomicReq, objectId, propId, value); // This returns the new count of properties on success, // so normalize it to 0 like drmModeObjectSetProperty() @@ -290,9 +292,14 @@ class DrmRenderer : public IFFmpegRenderer { } } else { - err = drmModeObjectSetProperty(m_Fd, prop.objectId(), prop.objectType(), prop.id(), value); + err = drmModeObjectSetProperty(m_Fd, objectId, objectType, propId, value); } + return err; + } + + bool set(const DrmProperty& prop, uint64_t value, bool verbose = true) { + int err = set(prop.objectId(), prop.objectType(), prop.id(), value); if (verbose && err == 0) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Set property '%s': %" PRIu64, @@ -440,6 +447,26 @@ class DrmRenderer : public IFFmpegRenderer { } } + // The damage rect is relative to the FB + void damagePlane(const DrmPropertyMap& plane, const SDL_Rect& rect) { + SDL_assert(m_Atomic); + + if (auto prop = plane.property("FB_DAMAGE_CLIPS")) { + std::lock_guard lg { m_Lock }; + auto& pb = m_PlaneBuffers[plane.objectId()]; + + // Append this new damage rect to our existing list + drm_mode_rect drmRect; + drmRect.x1 = rect.x; + drmRect.x2 = rect.x + rect.w; + drmRect.y1 = rect.y; + drmRect.y2 = rect.y + rect.h; + pb.pendingFbDamageRects.emplace_back(std::move(drmRect)); + + pb.fbDamageClipsPropId = prop->id(); + } + } + bool testPlane(const DrmPropertyMap& plane, uint32_t crtcId, uint32_t fbId, int32_t crtcX, int32_t crtcY, @@ -500,11 +527,41 @@ class DrmRenderer : public IFFmpegRenderer { drmModeAtomicReqPtr req = nullptr; std::unordered_map pendingBuffers; + std::vector damageBlobIds; { + std::lock_guard lg { m_Lock }; + + // Create property blobs for any pending FB damage clip rects + for (auto &[planeId, pb] : m_PlaneBuffers) { + uint32_t damageBlob; + + if (!pb.pendingFbDamageRects.empty()) { + int err = drmModeCreatePropertyBlob(m_Fd, + pb.pendingFbDamageRects.data(), + pb.pendingFbDamageRects.size() * sizeof(drm_mode_rect), + &damageBlob); + if (err < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "drmModeCreatePropertyBlob(FB_DAMAGE_CLIPS) failed: %d", + err); + continue; + } + + err = set(planeId, DRM_MODE_OBJECT_PLANE, pb.fbDamageClipsPropId, damageBlob); + if (err < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Failed to set property 'FB_DAMAGE_CLIPS': %d", + err); + // Fall-through + } + + damageBlobIds.emplace_back(damageBlob); + } + } + // Take ownership of the current atomic request to commit it and // allow other threads to queue up changes for the next one. - std::lock_guard lg { m_Lock }; std::swap(req, m_AtomicReq); std::swap(pendingBuffers, m_PlaneBuffers); } @@ -541,6 +598,11 @@ class DrmRenderer : public IFFmpegRenderer { err); } + // Free the damage clip blobs + for (auto blobId : damageBlobIds) { + drmModeDestroyPropertyBlob(m_Fd, blobId); + } + // Update the buffer state for any modified planes std::lock_guard lg { m_Lock }; for (auto it = pendingBuffers.begin(); it != pendingBuffers.end(); it++) {