Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement VisibilityThresholdModifier #173

Merged

Conversation

ipavlidakis
Copy link
Collaborator

@ipavlidakis ipavlidakis commented Oct 4, 2023

🎯 Goal

Reduce CPU usage demands during a call with more than 5 participants.

📝 Summary

When in a call with more than 5 participants we use a GridView to show and arrange video tracks. This view leverages the onAppear & onDisappear hooks to disable tracks that are not on screen. Unfortunately, as we are using LazyVStack to wrap our video tracks, iOS preloads views that aren't on screen, resulting in rendering video tracks that aren't really on user's viewport.

🛠 Implementation

To resolve this problem, we introduce a ViewModifier that internally uses a GeometryReader to compute if it's in its parent's viewport. If not the View is considered hidden and we propagate a visibility change that stops renderings for the View's video track.

🎨 Showcase

Add relevant screenshots and/or videos/gifs to easily see what this PR changes, if applicable.

Before After
image image

Note

The smaller CPU Usage and Energy impact is because the app was unresponsive and render requests were dropping (as can be seen from the FPS counter).

🧪 Manual Testing Notes

Explain how this change can be tested manually, if applicable.

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change follows zero ⚠️ policy (required)
  • This change should receive manual QA
  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (Docusaurus, tutorial, CMS)

🎁 Meme

@ipavlidakis ipavlidakis requested a review from a team as a code owner October 4, 2023 09:45
@ipavlidakis ipavlidakis self-assigned this Oct 4, 2023
@ipavlidakis ipavlidakis added the enhancement New feature or request label Oct 4, 2023
@ipavlidakis ipavlidakis force-pushed the improvements/visibility-observation-for-video-track-views branch from cf92b10 to 4d8c789 Compare October 4, 2023 12:09
Copy link
Collaborator

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the screenshots, the device would heat more with the new approach, only the FPS is better, right?

@@ -159,18 +159,11 @@ public struct CallView<Factory: ViewFactory>: View {
viewFactory.makeVideoParticipantsView(
viewModel: viewModel,
availableSize: size,
onViewRendering: handleViewRendering(_:participant:),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do this without breaking changes to the public API?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remove this one as it's not being used anywhere. We can definitely go ahead without this change but makes our API confusing as we are asking for something what we won't be using.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other things to keep in mind when looking on the screenshots (as you may missed the note below them):

  • The before screenshot shows lower consumption because the app at that point was unresponsive. Nothing was updating on the screen and that was causing consumption to drop.
  • The after screenshot was captured after 55minutes in a call while the before it was on minute 18.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, you should sell it better then 😄

Sources/StreamVideoSwiftUI/ViewFactory.swift Show resolved Hide resolved
@ipavlidakis
Copy link
Collaborator Author

According to the screenshots, the device would heat more with the new approach, only the FPS is better, right?

The energy impact isn't really representative for the thermal state. The device feels cooler with the latest changes. That doesn't mean that processing video isn't an energy impactful operation. The difference is that it takes more time to jump the thermal state levels.

@ipavlidakis ipavlidakis force-pushed the improvements/visibility-observation-for-video-track-views branch 2 times, most recently from b6c17b3 to 49dce2a Compare October 4, 2023 15:36
Sources/StreamVideo/WebRTC/StreamVideoCaptureHandler.swift Outdated Show resolved Hide resolved
} else {
VStack {
participantsContent
GeometryReader { geometryProxy in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we might have an extra geometry reader? The availableSize value should also come from a geometry reader. Ideally, we should use only one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

availableSize is indeed coming from a GeometryReader in the CallView. We can't really use it here as we don't need only the size but the origin too.

We can potentially use only one GeometryReader but that will require a change on the API that will allow us to pass availableRect/availableViewport: CGRect instead of availableSize: CGSize.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say it's better to break the API at this point, since we are still in beta. Geometry readers can be expensive, best to use them only when needed. Wdyt?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this one I think will incur many changes. How do you feel about having it in another PR?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe another PR towards this PR, to see how much it is? Since if we do breaking changes, we should do them very soon.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A PR to this branch sounds good. Here it is #176

StreamVideoTests/Utils/SerialActor_Tests.swift Outdated Show resolved Hide resolved
StreamVideoTests/Utils/SerialActor_Tests.swift Outdated Show resolved Hide resolved
Task { [weak self] in
guard let self else { return }
Task { [serialActor] in
await serialActor.enqueue { [weak self] in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure why enqueuing async tasks helps performance, can you please elaborate one more time? 🙏

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that Tasks not running on a specific actor will run as part of the GlobalActor. The previous days I remember reading that the GlobalActor is concurrent. That meant that the Tasks created here (outside of an Actor) will run as part of the GlobalActor. As they will run concurrently we may doing processing for more that one frame at the same time which can be consuming.

However, looking again now I couldn't find what I was reading and i ended up asking ChatGPT. The answer is that the GlobalActor is serial. So I will remove this one.

@ipavlidakis ipavlidakis force-pushed the improvements/visibility-observation-for-video-track-views branch from 3e63270 to 68f4c85 Compare October 6, 2023 12:01
@ipavlidakis ipavlidakis force-pushed the improvements/visibility-observation-for-video-track-views branch from 04371c5 to a423ee4 Compare October 6, 2023 13:30
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is higher as now we are using the frame from global and in the empty space is where we have the controls.

@github-actions
Copy link

github-actions bot commented Oct 6, 2023

1 Warning
⚠️ Big PR

Generated by 🚫 Danger

@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 6, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

71.9% 71.9% Coverage
0.0% 0.0% Duplication

Copy link
Collaborator

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! ✅

@ipavlidakis ipavlidakis merged commit 95a0a5b into main Oct 9, 2023
14 of 16 checks passed
@ipavlidakis ipavlidakis deleted the improvements/visibility-observation-for-video-track-views branch October 9, 2023 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants