/*
* Copyright (c) 2023-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rs_vsync_client_ios.h"
#include <chrono>
#include <Foundation/Foundation.h>
#include <QuartzCore/CADisplayLink.h>
#include <UIKit/UIKit.h>
#include "common/rs_common_def.h"
#include "platform/common/rs_log.h"
/* the coefficient of converting seconds to nanoseconds */
constexpr int64_t SEC_TO_NANOSEC = 1000000000;
constexpr float FRAME_RATE = 60;
@interface RSVsyncIOS : NSObject
- (instancetype)init;
- (void)requestNextVsync;
- (void)setVsyncCallback:(OHOS::Rosen::RSVsyncClient::VsyncCallback) callback;
- (void)invalidate;
@end
namespace OHOS {
namespace Rosen {
std::unique_ptr<RSVsyncClient> RSVsyncClient::Create()
{
return std::make_unique<RSVsyncClientIOS>();
}
RSVsyncClientIOS::RSVsyncClientIOS()
: vsyncIOS_([[RSVsyncIOS alloc]init])
{
}
RSVsyncClientIOS::~RSVsyncClientIOS()
{
ROSEN_LOGI("RSVsyncClientIOS::dealloc");
[vsyncIOS_ invalidate];
[vsyncIOS_ release];
}
void RSVsyncClientIOS::RequestNextVsync()
{
[vsyncIOS_ requestNextVsync];
}
void RSVsyncClientIOS::SetVsyncCallback(VsyncCallback callback)
{
[vsyncIOS_ setVsyncCallback:callback];
}
} // namespace Rosen
} // namespace OHOS
@implementation RSVsyncIOS {
OHOS::Rosen::RSVsyncClient::VsyncCallback callback_;
NSLock *lock_;
CADisplayLink* displayLink_;
}
- (instancetype)init {
if (self = [super init]) {
/* not from alloc init, need retain, or displayLink will release */
displayLink_ = [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain];
displayLink_.paused = YES;
[displayLink_ addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
float mainMaxFrameRate = [UIScreen mainScreen].maximumFramesPerSecond;
if (@available(iOS 15.0,*)) {
float maxFrameRate = fmax(mainMaxFrameRate, FRAME_RATE);
ROSEN_LOGI("RSVsyncIOS::maxFrameRate = %{public}f",maxFrameRate);
displayLink_.preferredFrameRateRange = CAFrameRateRangeMake(maxFrameRate, maxFrameRate, maxFrameRate);
} else {
displayLink_.preferredFramesPerSecond = mainMaxFrameRate;
}
lock_ = [[NSLock alloc] init];
}
return self;
}
- (void)requestNextVsync {
displayLink_.paused = NO;
}
- (void)setVsyncCallback:(OHOS::Rosen::RSVsyncClient::VsyncCallback) callback {
[lock_ lock];
callback_ = callback;
[lock_ unlock];
}
- (void)onDisplayLink:(CADisplayLink*)link {
displayLink_.paused = YES;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t now = ts.tv_sec * SEC_TO_NANOSEC + ts.tv_nsec;
[lock_ lock];
callback_(now, 0);
[lock_ unlock];
}
- (void)invalidate {
[displayLink_ invalidate];
}
- (void)dealloc {
ROSEN_LOGI("RSVsyncIOS::dealloc");
[displayLink_ release];
[lock_ release];
[super dealloc];
}
@end