Fixes animated frame timing (#44622)

Summary:
Fixes https://github.com/facebook/react-native/issues/44608 .

## Changelog:

[IOS] [FIXED] - Fixes animated frame timing

Pull Request resolved: https://github.com/facebook/react-native/pull/44622

Test Plan: Example in https://github.com/facebook/react-native/issues/44608 .

Reviewed By: sammy-SC

Differential Revision: D57559884

Pulled By: cipolleschi

fbshipit-source-id: 33eaf71c24eb83715403a67b3471dd1ac2dd9d2f
This commit is contained in:
zhongwuzw
2024-05-29 08:16:42 -07:00
committed by Facebook GitHub Bot
parent f500b47a95
commit 690df7a556
@@ -121,6 +121,8 @@ static NSUInteger RCTDeviceFreeMemory(void)
// Calculate max buffer size
[self calculateMaxBufferCount];
[self prefetchNextFrame:nil fetchFrameIndex:1];
if ([self paused]) {
[self start];
}
@@ -167,6 +169,21 @@ static NSUInteger RCTDeviceFreeMemory(void)
return _displayLink;
}
- (void)prefetchNextFrame:(UIImage *)fetchFrame fetchFrameIndex:(NSInteger)fetchFrameIndex
{
if (!fetchFrame && !(self.frameBuffer.count == self.totalFrameCount) && self.fetchQueue.operationCount == 0) {
// Prefetch next frame in background queue
UIImage<RCTAnimatedImage> *animatedImage = self.animatedImage;
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
UIImage *frame = [animatedImage animatedImageFrameAtIndex:fetchFrameIndex];
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
self.frameBuffer[@(fetchFrameIndex)] = frame;
dispatch_semaphore_signal(self.lock);
}];
[self.fetchQueue addOperation:operation];
}
}
#pragma mark - Animation
- (void)start
@@ -210,6 +227,9 @@ static NSUInteger RCTDeviceFreeMemory(void)
// Do not skip frame
self.currentTime = nextDuration;
}
currentFrameIndex = nextFrameIndex;
self.currentFrameIndex = nextFrameIndex;
nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount;
}
// Update the current frame
@@ -219,20 +239,14 @@ static NSUInteger RCTDeviceFreeMemory(void)
currentFrame = self.frameBuffer[@(currentFrameIndex)];
fetchFrame = currentFrame ? self.frameBuffer[@(nextFrameIndex)] : nil;
dispatch_semaphore_signal(self.lock);
BOOL bufferFull = NO;
if (currentFrame) {
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
// Remove the frame buffer if need
if (self.frameBuffer.count > self.maxBufferCount) {
self.frameBuffer[@(currentFrameIndex)] = nil;
}
// Check whether we can stop fetch
if (self.frameBuffer.count == totalFrameCount) {
bufferFull = YES;
}
dispatch_semaphore_signal(self.lock);
self.currentFrame = currentFrame;
self.currentFrameIndex = nextFrameIndex;
self.bufferMiss = NO;
[self.layer setNeedsDisplay];
} else {
@@ -261,17 +275,7 @@ static NSUInteger RCTDeviceFreeMemory(void)
fetchFrameIndex = nextFrameIndex;
}
if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) {
// Prefetch next frame in background queue
UIImage<RCTAnimatedImage> *animatedImage = self.animatedImage;
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
UIImage *frame = [animatedImage animatedImageFrameAtIndex:fetchFrameIndex];
dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER);
self.frameBuffer[@(fetchFrameIndex)] = frame;
dispatch_semaphore_signal(self.lock);
}];
[self.fetchQueue addOperation:operation];
}
[self prefetchNextFrame:fetchFrame fetchFrameIndex:fetchFrameIndex];
}
#pragma mark - CALayerDelegate