• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <fstream>
17 #include <random>
18 
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 
22 #define private public
23 #define protected public
24 
25 #include "core/components_ng/event/touch_event.h"
26 #include "core/components_v2/inspector/inspector_constants.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 #include "frameworks/core/components_ng/pattern/root/root_pattern.h"
29 #include "core/pipeline/base/constants.h"
30 
31 using namespace testing;
32 using namespace testing::ext;
33 using OHOS::Ace::TimeStamp;
34 
35 namespace OHOS::Ace {
CalculateParabola(double a,double b,double c,double t,double angle)36 PointerEvent CalculateParabola(double a, double b, double c, double t, double angle)
37 {
38     double d = a * t * t + b * t + c;
39     PointerEvent p;
40     p.x = d * std::cos(angle);
41     p.y = d * std::sin(angle);
42     return p;
43 }
44 
Get1stOrderDiff(const std::vector<TouchEvent> & events)45 std::vector<TouchEvent> Get1stOrderDiff(const std::vector<TouchEvent>& events)
46 {
47     std::vector<TouchEvent> diff;
48     for (int32_t i = 1; i < events.size(); ++i) {
49         TouchEvent p;
50         p.x = events[i].x - events[i - 1].x;
51         p.y = events[i].y - events[i - 1].y;
52         diff.push_back(p);
53     }
54     return diff;
55 }
56 
Get2ndOrderDiff(const std::vector<TouchEvent> & events)57 std::vector<TouchEvent> Get2ndOrderDiff(const std::vector<TouchEvent>& events)
58 {
59     std::vector<TouchEvent> diff = Get1stOrderDiff(events);
60     return Get1stOrderDiff(diff);
61 }
62 
GetStdDev(const std::vector<TouchEvent> & diff)63 double GetStdDev(const std::vector<TouchEvent>& diff)
64 {
65     double xAccumulate = 0;
66     double yAccumulate = 0;
67     for (auto& item : diff) {
68         xAccumulate += item.x;
69         yAccumulate += item.y;
70     }
71     double xMean = xAccumulate / diff.size();
72     double yMean = yAccumulate / diff.size();
73 
74     xAccumulate = 0;
75     yAccumulate = 0;
76     for (auto& item : diff) {
77         xAccumulate += std::pow(item.x - xMean, 2);
78         yAccumulate += std::pow(item.y - yMean, 2);
79     }
80     return std::sqrt((xAccumulate + yAccumulate) / diff.size());
81 }
82 
83 // using Parabola
SmoothBy2ndOrderDiff(const std::vector<TouchEvent> & events)84 double SmoothBy2ndOrderDiff(const std::vector<TouchEvent>& events)
85 {
86     auto diff = Get2ndOrderDiff(events);
87     return GetStdDev(diff);
88 }
89 
90 // using linear
Monotonicity(const std::vector<TouchEvent> & events)91 double Monotonicity(const std::vector<TouchEvent>& events)
92 {
93     auto diff = Get1stOrderDiff(events);
94     if (diff.empty()) {
95         return 0.0;
96     }
97     PointerEvent& sign = diff[0];
98     for (auto& item : diff) {
99         if (item.x * sign.x < 0 || item.y * sign.y < 0) {
100             ADD_FAILURE();
101         }
102     }
103     return 0.0;
104 }
105 
GetDeltaT(TimeStamp time,TimeStamp base)106 int64_t GetDeltaT(TimeStamp time, TimeStamp base)
107 {
108     auto deltaT = time - base;
109     return std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
110 }
111 
GenerateCSV(const std::string & fileName,TimeStamp baseTime,const std::vector<TouchEvent> & tp,const std::vector<TouchEvent> & common,const std::vector<TouchEvent> & accelarate)112 void GenerateCSV(const std::string& fileName, TimeStamp baseTime, const std::vector<TouchEvent>& tp,
113     const std::vector<TouchEvent>& common, const std::vector<TouchEvent>& accelarate)
114 {
115     std::ofstream file(fileName);
116     if (!file.is_open()) {
117         std::cerr << "Cannot open the file: " << fileName << std::endl;
118         return;
119     }
120 
121     // write head
122     file << "TpTime,TpDisplacement,"
123          << "CommonTime,CommonDisplacement,"
124          << "AccelarateTime,AccelarateDisplacement\n";
125     size_t maxSize = std::max({ tp.size(), common.size(), accelarate.size() });
126 
127     // write data
128     for (size_t i = 0; i < maxSize; ++i) {
129         if (i < tp.size()) {
130             double d = std::sqrt(tp[i].x * tp[i].x + tp[i].y * tp[i].y);
131             file << GetDeltaT(tp[i].time, baseTime) << "," << d;
132         } else {
133             file << ",";
134         }
135 
136         if (i < common.size()) {
137             double d = std::sqrt(common[i].x * common[i].x + common[i].y * common[i].y);
138             file << "," << GetDeltaT(common[i].time, baseTime) << "," << d;
139         } else {
140             file << ",,";
141         }
142 
143         if (i < accelarate.size()) {
144             double d = std::sqrt(accelarate[i].x * accelarate[i].x + accelarate[i].y * accelarate[i].y);
145             file << "," << GetDeltaT(accelarate[i].time, baseTime) << "," << d << "\n";
146         } else {
147             file << ",,\n";
148         }
149     }
150     file.close();
151 }
152 
153 struct TestResult {
154     double stddevOfAcc;          // evaluate smooth
155     double stddevAgainstPhy;     // evaluate accuracy
156     double stddevOfUniformSpeed; // evalueate stability
157 };
158 
159 class MockEventManager : public EventManager {
160     DECLARE_ACE_TYPE(MockEventManager, OHOS::Ace::EventManager);
161 
162 public:
163     MockEventManager() = default;
164     ~MockEventManager() override = default;
165 
166     MOCK_METHOD(bool, DispatchTouchEvent, (const TouchEvent&, bool));
167 };
168 
169 using NiceEventManager = NiceMock<MockEventManager>;
170 
171 namespace NG {
172 using Generator = std::function<PointerEvent(double)>;
173 class ResampleTestNg : public testing::Test {
174 public:
175     static void SetUpTestSuite();
176     static void TearDownTestSuite();
177 
178     static bool TestOnTouchEvent(const TouchEvent& event, bool sendOnTouch);
179     static vector<TouchEvent> events_;
180     static vector<TouchEvent> oriEvents_;
181     static RefPtr<PipelineContext> pipeline_;
182 
183     void SetUp() override;
184     void TearDown() override;
185 
186     /* simulate functions */
187     void SetGenerator(Generator&& f);
188     void GenerateTouchEvents(uint32_t duration);
189     void RunVsync(int32_t vsyncPeriodMs);
190     PointerEvent GetPhysicPoint(TimeStamp time);
191     TouchEvent GetOriginPoint(TimeStamp time);
192 
193     /* test functions */
194     double GetCoordsDiffAgainstPhy();
195     void TestFastFlick(int32_t vsyncPeriod, TestResult& result);
196     void TestDeceleratingSlide(int32_t vsyncPeriod, TestResult& result);
197     void TestConstantSpeedSlide(int32_t vsyncPeriod, TestResult& result);
198     void TestSlowConstantSpeedSlide(int32_t vsyncPeriod, TestResult& result);
199 
200     static constexpr uint32_t TP_RATE = 140;
201     static constexpr double NOISE_STD_DEV = 0.7;
202     static constexpr float NEAR_ZERO_DEV = 0.001;
203 
204     TimeStamp nowTime_;
205     Generator func_;
206 };
207 
208 std::vector<TouchEvent> ResampleTestNg::events_;
209 std::vector<TouchEvent> ResampleTestNg::oriEvents_;
210 RefPtr<PipelineContext> ResampleTestNg::pipeline_;
211 
SetUpTestSuite()212 void ResampleTestNg::SetUpTestSuite()
213 {
214     pipeline_ = AceType::MakeRefPtr<PipelineContext>();
215     pipeline_->touchAccelarate_ = true;
216     pipeline_->rootNode_ = FrameNode::CreateFrameNodeWithTree(
217         V2::ROOT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<RootPattern>());
218     auto mockEventManager = AceType::MakeRefPtr<NiceEventManager>();
219     pipeline_->eventManager_ = mockEventManager;
220     ON_CALL((*mockEventManager), DispatchTouchEvent(testing::_, testing::_))
221         .WillByDefault(testing::Invoke(
222             [](const TouchEvent& event, bool sendOnTouch) { return TestOnTouchEvent(event, sendOnTouch); }));
223 }
224 
TearDownTestSuite()225 void ResampleTestNg::TearDownTestSuite()
226 {
227     pipeline_ = nullptr;
228 }
229 
SetUp()230 void ResampleTestNg::SetUp()
231 {
232     nowTime_ = std::chrono::high_resolution_clock::now();
233 }
234 
TearDown()235 void ResampleTestNg::TearDown()
236 {
237     events_.clear();
238     oriEvents_.clear();
239     pipeline_->touchEvents_.clear();
240     func_ = nullptr;
241 }
242 
GetPhysicPoint(TimeStamp time)243 PointerEvent ResampleTestNg::GetPhysicPoint(TimeStamp time)
244 {
245     auto deltaT = time - nowTime_;
246     int64_t t = std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
247     return func_(t);
248 }
249 
GetOriginPoint(TimeStamp time)250 TouchEvent ResampleTestNg::GetOriginPoint(TimeStamp time)
251 {
252     auto deltaT = time - nowTime_;
253     int64_t t = std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
254     return oriEvents_[t * TP_RATE / 1000];
255 }
256 
SetGenerator(Generator && f)257 void ResampleTestNg::SetGenerator(Generator&& f)
258 {
259     func_ = std::move(f);
260 }
261 
262 // Generate a touch sequence with specific duration
GenerateTouchEvents(uint32_t duration)263 void ResampleTestNg::GenerateTouchEvents(uint32_t duration)
264 {
265     std::vector<TouchEvent> events;
266     uint32_t samplingRate = TP_RATE;
267     std::chrono::microseconds dt(1000000 / samplingRate);
268     std::chrono::microseconds deltaT(0);
269     std::chrono::milliseconds durationMs(duration);
270     std::random_device rd;
271     std::mt19937 gen(rd());
272     std::normal_distribution<double> noise(0.0, NOISE_STD_DEV);
273 
274     // touch down
275     TouchEvent event;
276     event.SetId(0.0).SetX(0.0).SetY(0.0).SetType(TouchType::DOWN).SetTime(nowTime_);
277     oriEvents_.clear();
278     oriEvents_.emplace_back(event);
279 
280     event.SetType(TouchType::MOVE);
281     do {
282         deltaT += dt;
283         double t = deltaT.count() / 1000;
284         PointerEvent p = func_(t);
285         event.SetX(p.x);
286         event.SetY(p.y);
287         event.SetTime(deltaT + nowTime_);
288         oriEvents_.emplace_back(event);
289     } while (deltaT <= durationMs);
290 }
291 
RunVsync(int32_t vsyncHz)292 void ResampleTestNg::RunVsync(int32_t vsyncHz)
293 {
294     std::chrono::microseconds vsyncPeriod(1000000 / vsyncHz);
295     constexpr int64_t delay = 2000000; // simulate the 2ms delay between vsync and sensor time of event
296     int32_t i = 0;
297     auto vsyncTime = nowTime_;
298     int32_t cnt = 0;
299     events_.clear();
300     pipeline_->touchEvents_.clear();
301     while (i < oriEvents_.size()) {
302         while (i < oriEvents_.size() && oriEvents_[i].time < vsyncTime) {
303             pipeline_->touchEvents_.emplace_back(oriEvents_[i]);
304             i++;
305         }
306         int64_t vsynTimeNs = vsyncTime.time_since_epoch().count();
307         pipeline_->SetVsyncTime(vsynTimeNs + delay);
308         pipeline_->FlushTouchEvents();
309         pipeline_->resampleTimeStamp_ = vsynTimeNs + delay + 1000000; // instead of mock FlushVsync
310         vsyncTime += vsyncPeriod;
311         ++cnt;
312     }
313 }
314 
TestOnTouchEvent(const TouchEvent & event,bool sendOnTouch)315 bool ResampleTestNg::TestOnTouchEvent(const TouchEvent& event, bool sendOnTouch)
316 {
317     events_.emplace_back(event);
318     return true;
319 }
320 
GetCoordsDiffAgainstPhy()321 double ResampleTestNg::GetCoordsDiffAgainstPhy()
322 {
323     double variance = 0;
324     for (int32_t i = 0; i < events_.size(); ++i) {
325         auto base = GetPhysicPoint(events_[i].time);
326         double dx = events_[i].x - base.x;
327         double dy = events_[i].y - base.y;
328         variance += dx * dx + dy * dy;
329     }
330     return variance / events_.size();
331 }
332 
TestFastFlick(int32_t vsyncPeriod,TestResult & result)333 void ResampleTestNg::TestFastFlick(int32_t vsyncPeriod, TestResult& result)
334 {
335     constexpr int32_t repeatTimes = 1;
336     double accumulateVar = 0;
337     double accumulateSmooth = 0;
338     SetGenerator([](double t) { return CalculateParabola(-0.0005, 0.8, 0, t, ACE_PI / 3); });
339     for (int32_t i = 0; i < repeatTimes; ++i) {
340         GenerateTouchEvents(500);
341         RunVsync(vsyncPeriod);
342         accumulateVar += GetCoordsDiffAgainstPhy();
343         accumulateSmooth += SmoothBy2ndOrderDiff(events_);
344     }
345     result.stddevAgainstPhy = std::sqrt(accumulateVar / repeatTimes);
346     result.stddevOfAcc = accumulateSmooth / repeatTimes;
347 }
348 
349 /**
350  * @tc.name: ResampleTestFastFlick
351  * @tc.desc: Construct touch events and test resample sequence.
352  * @tc.type: FUNC
353  */
354 HWTEST_F(ResampleTestNg, ResampleTestFastFlick, TestSize.Level1)
355 {
356     TestResult result { 0 };
357     pipeline_->touchAccelarate_ = true;
358     TestFastFlick(60, result);
359     EXPECT_LT(result.stddevAgainstPhy, 0.157);
360     EXPECT_LT(result.stddevOfAcc, 0.537);
361     TestFastFlick(140, result);
362     EXPECT_LT(result.stddevAgainstPhy, 0.322);
363     EXPECT_LT(result.stddevOfAcc, 3.956);
364 
365     pipeline_->touchAccelarate_ = false;
366     TestFastFlick(60, result);
367     EXPECT_LT(result.stddevAgainstPhy, 0.212);
368     EXPECT_LT(result.stddevOfAcc, 1.634);
369     TestFastFlick(140, result);
370     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
371     EXPECT_LT(result.stddevOfAcc, 0.288);
372 }
373 
TestDeceleratingSlide(int32_t vsyncPeriod,TestResult & result)374 void ResampleTestNg::TestDeceleratingSlide(int32_t vsyncPeriod, TestResult& result)
375 {
376     constexpr int32_t repeatTimes = 1;
377     double accumulateVar = 0;
378     double accumulateSmooth = 0;
379     SetGenerator([](double t) { return CalculateParabola(-0.0001, 0.3, 0, t, ACE_PI / 6); });
380     for (int32_t i = 0; i < repeatTimes; ++i) {
381         GenerateTouchEvents(500);
382         RunVsync(60);
383         accumulateVar += GetCoordsDiffAgainstPhy();
384         accumulateSmooth += SmoothBy2ndOrderDiff(events_);
385     }
386     result.stddevAgainstPhy = std::sqrt(accumulateVar / repeatTimes);
387     result.stddevOfAcc = accumulateSmooth / repeatTimes;
388 }
389 
390 /**
391  * @tc.name: ResampleTestDeceleratingSlide
392  * @tc.desc: Construct touch events and test resample sequence.
393  * @tc.type: FUNC
394  */
395 HWTEST_F(ResampleTestNg, ResampleTestDeceleratingSlide, TestSize.Level1)
396 {
397     TestResult result { 0 };
398     pipeline_->touchAccelarate_ = true;
399     TestDeceleratingSlide(60, result);
400     EXPECT_LT(result.stddevAgainstPhy, 0.075);
401     EXPECT_LT(result.stddevOfAcc, 0.357);
402     TestDeceleratingSlide(140, result);
403     EXPECT_LT(result.stddevAgainstPhy, 0.075);
404     EXPECT_LT(result.stddevOfAcc, 0.357);
405 
406     pipeline_->touchAccelarate_ = false;
407     TestDeceleratingSlide(60, result);
408     EXPECT_LT(result.stddevAgainstPhy, 0.098);
409     EXPECT_LT(result.stddevOfAcc, 0.625);
410     TestDeceleratingSlide(140, result);
411     EXPECT_LT(result.stddevAgainstPhy, 0.098);
412     EXPECT_LT(result.stddevOfAcc, 0.625);
413 }
414 
TestConstantSpeedSlide(int32_t vsyncPeriod,TestResult & result)415 void ResampleTestNg::TestConstantSpeedSlide(int32_t vsyncPeriod, TestResult& result)
416 {
417     constexpr int32_t repeatTimes = 1;
418     double accumulateVar = 0;
419     double accumulateSmooth = 0;
420     pipeline_->touchAccelarate_ = false;
421     SetGenerator([](double t) { return CalculateParabola(0, 0.2, 0, t, 0); });
422     for (int32_t i = 0; i < repeatTimes; ++i) {
423         GenerateTouchEvents(1000);
424         RunVsync(60);
425         accumulateVar += GetCoordsDiffAgainstPhy();
426         accumulateSmooth += Monotonicity(events_);
427     }
428     result.stddevAgainstPhy = std::sqrt(accumulateVar / repeatTimes);
429     result.stddevOfUniformSpeed = accumulateSmooth / repeatTimes;
430 }
431 
432 /**
433  * @tc.name: ResampleTestConstantSpeedSlide
434  * @tc.desc: Construct touch events and test resample sequence.
435  * @tc.type: FUNC
436  */
437 HWTEST_F(ResampleTestNg, ResampleTestConstantSpeedSlide, TestSize.Level1)
438 {
439     TestResult result { 0 };
440     pipeline_->touchAccelarate_ = true;
441     TestConstantSpeedSlide(60, result);
442     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
443     EXPECT_LT(result.stddevOfUniformSpeed, 0.656);
444     TestConstantSpeedSlide(140, result);
445     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
446     EXPECT_LT(result.stddevOfUniformSpeed, 0.656);
447 
448     pipeline_->touchAccelarate_ = false;
449     TestConstantSpeedSlide(60, result);
450     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
451     EXPECT_LT(result.stddevOfUniformSpeed, 0.656);
452     TestConstantSpeedSlide(140, result);
453     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
454     EXPECT_LT(result.stddevOfUniformSpeed, 0.656);
455 }
456 
TestSlowConstantSpeedSlide(int32_t vsyncPeriod,TestResult & result)457 void ResampleTestNg::TestSlowConstantSpeedSlide(int32_t vsyncPeriod, TestResult& result)
458 {
459     constexpr int32_t repeatTimes = 1;
460     double accumulateVar = 0;
461     double accumulateSmooth = 0;
462     SetGenerator([](double t) { return CalculateParabola(0, 0.05, 0, t, 0); });
463     for (int32_t i = 0; i < repeatTimes; ++i) {
464         GenerateTouchEvents(1000);
465         RunVsync(60);
466         accumulateVar += GetCoordsDiffAgainstPhy();
467         accumulateSmooth += Monotonicity(events_);
468     }
469     result.stddevAgainstPhy = std::sqrt(accumulateVar / repeatTimes);
470     result.stddevOfUniformSpeed = accumulateSmooth / repeatTimes;
471 }
472 
473 /**
474  * @tc.name: ResampleTestSlowConstantSpeedSlide
475  * @tc.desc: Construct touch events and test resample sequence.
476  * @tc.type: FUNC
477  */
478 HWTEST_F(ResampleTestNg, ResampleTestSlowConstantSpeedSlide, TestSize.Level1)
479 {
480     TestResult result { 0 };
481     pipeline_->touchAccelarate_ = true;
482     TestSlowConstantSpeedSlide(60, result);
483     EXPECT_LT(result.stddevAgainstPhy, 0.013);
484     EXPECT_LT(result.stddevOfUniformSpeed, 0.086);
485     TestSlowConstantSpeedSlide(140, result);
486     EXPECT_LT(result.stddevAgainstPhy, 0.013);
487     EXPECT_LT(result.stddevOfUniformSpeed, 0.086);
488 
489     pipeline_->touchAccelarate_ = false;
490     TestSlowConstantSpeedSlide(60, result);
491     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
492     EXPECT_LT(result.stddevOfUniformSpeed, 0.164);
493     TestSlowConstantSpeedSlide(140, result);
494     EXPECT_LT(result.stddevAgainstPhy, NEAR_ZERO_DEV);
495     EXPECT_LT(result.stddevOfUniformSpeed, 0.164);
496 }
497 
498 HWTEST_F(ResampleTestNg, ResampleTestRealData01, TestSize.Level1)
499 {
500     pipeline_->touchAccelarate_ = true;
501     std::vector<TouchEvent> events;
502 
503     TouchEvent event;
504     int64_t sinceEpoch = 343641809000; // real trace data
505     std::chrono::microseconds us_since_epoch(sinceEpoch);
506     TimeStamp stamp(us_since_epoch);
507     nowTime_ = stamp;
508     event.SetId(0.0).SetX(0.0).SetY(0.0).SetType(TouchType::DOWN).SetTime(stamp);
509     oriEvents_.clear();
510     oriEvents_.emplace_back(event);
511 
512     event.SetType(TouchType::MOVE);
513     // real trace data
514     std::vector<std::vector<int64_t>> logPoints { { 343641816000, 1735, 300 }, { 343641823000, 1736, 300 },
515         { 343641830000, 1738, 300 }, { 343641837000, 1739, 300 }, { 343641844000, 1740, 300 },
516         { 343641851000, 1742, 300 }, { 343641858000, 1743, 300 }, { 343641865000, 1745, 300 },
517         { 343641872000, 1746, 300 }, { 343641879000, 1747, 300 }, { 343641886000, 1749, 300 },
518         { 343641893000, 1750, 300 }, { 343641900000, 1752, 300 }, { 343641907000, 1753, 300 },
519         { 343641914000, 1754, 300 }, { 343641921000, 1756, 300 }, { 343641928000, 1757, 300 },
520         { 343641935000, 1759, 300 }, { 343641942000, 1760, 300 }, { 343641949000, 1761, 300 },
521         { 343641958000, 1763, 300 }, { 343641963000, 1764, 300 } };
522     for (const auto& item : logPoints) {
523         std::chrono::microseconds msEpoch(item[0]);
524         TimeStamp t(msEpoch);
525         event.SetX(item[1]).SetY(item[2]).SetTime(t);
526         oriEvents_.emplace_back(event);
527     }
528     events_.clear();
529     pipeline_->touchEvents_.clear();
530 
531     // real trace data
532     std::vector<int64_t> vsyncTime { 343641803424377, 343641820086409, 343641836748819, 343641853410582,
533         343641870072479, 343641886735032, 343641903397442, 343641920059816, 343641936722405, 343641953686073,
534         343641970348577, 343641987010997, 343642003671915 };
535     int32_t vsyncIdx = 0;
536     int32_t i = 0;
537     while (i < oriEvents_.size()) {
538         std::chrono::nanoseconds nsEpoch(vsyncTime[vsyncIdx]);
539         TimeStamp t(nsEpoch);
540         while (i < oriEvents_.size() && oriEvents_[i].time < t) {
541             pipeline_->touchEvents_.emplace_back(oriEvents_[i]);
542             i++;
543         }
544         pipeline_->SetVsyncTime(vsyncTime[vsyncIdx]);
545         pipeline_->FlushTouchEvents();
546         ++vsyncIdx;
547     }
548     Monotonicity(events_);
549 }
550 } // namespace NG
551 } // namespace OHOS::Ace