From bb6aebca1c493a93f652325188f48c8388c3c9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJonah=5Fxia=E2=80=9D?= <“xiajianchun1@huawei.com”> Date: Wed, 11 Dec 2024 23:15:31 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=B0=B7=E6=AD=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=E5=A4=84=E7=90=86=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=BF=87=E7=A8=8B=E4=B8=AD=E5=90=8C=E6=AD=A5?= =?UTF-8?q?commit=EF=BC=8C=E9=9D=9E=E4=BA=8B=E4=BB=B6=E6=97=B6non-block-co?= =?UTF-8?q?mmit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cc/test/fake_proxy.h | 5 ++++- cc/trees/layer_tree_host.h | 7 ++++++- cc/trees/proxy.h | 7 +++++++ cc/trees/proxy_impl.cc | 32 ++++++++++++++++++++++++++++++-- cc/trees/proxy_main.cc | 25 ++++++++++++++++++++++++- cc/trees/proxy_main.h | 8 ++++++++ cc/trees/single_thread_proxy.cc | 5 +++++ cc/trees/single_thread_proxy.h | 4 ++++ 8 files changed, 88 insertions(+), 5 deletions(-) diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h index e4552eb3bc..d181bed2e9 100644 --- a/cc/test/fake_proxy.h +++ b/cc/test/fake_proxy.h @@ -64,7 +64,10 @@ class FakeProxy : public Proxy { base::OnceClosure callback) override {} double GetPercentDroppedFrames() const override; void SetPauseRendering(bool pause_rendering) override {} - + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override {} + // cherry-pick from google end private: raw_ptr layer_tree_host_; }; diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index 0e92854e7f..48bb5eb965 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -905,7 +905,12 @@ void RegisterClippedVisualViewportSelectionBounds( [[nodiscard]] base::AutoReset SimulateSyncingDeltasForTesting() { return base::AutoReset(&syncing_deltas_for_test_, true); } - + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool WaitedForCommitForTesting() const { + return waited_for_protected_sequence_; + } + // cherry-pick from google end void IncrementVisualUpdateDuration(base::TimeDelta visual_update_duration); #if BUILDFLAG(IS_OHOS) diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index 44f02bf68c..907290491c 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -66,6 +66,13 @@ class CC_EXPORT Proxy { // Pauses all main and impl-side rendering. virtual void SetPauseRendering(bool pause_rendering) = 0; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // Indicates that the next main frame will contain the result of running an + // event handler for an input event. + virtual void SetInputResponsePending() = 0; + // cherry-pick from google end + // Defers commits until at most the given |timeout| period has passed, // but continues to update the document lifecycle in // LayerTreeHost::BeginMainFrameUpdate. If multiple calls are made when diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc index b2952fc5ab..67d76cec8e 100644 --- a/cc/trees/proxy_impl.cc +++ b/cc/trees/proxy_impl.cc @@ -62,17 +62,30 @@ class ScopedCommitCompletionEvent { CompletionEvent* event, base::TimeTicks start_time, base::SingleThreadTaskRunner* main_thread_task_runner, + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool notify_main, + // cherry-pick from google end base::WeakPtr proxy_main_weak_ptr) : event_(event), commit_timestamps_({start_time, base::TimeTicks()}), main_thread_task_runner_(main_thread_task_runner), + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + notify_main_(notify_main), + // cherry-pick from google end proxy_main_weak_ptr_(proxy_main_weak_ptr) {} ScopedCommitCompletionEvent(const ScopedCommitCompletionEvent&) = delete; ~ScopedCommitCompletionEvent() { event_.ExtractAsDangling()->Signal(); + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + if (notify_main_) { main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidCompleteCommit, proxy_main_weak_ptr_, commit_timestamps_)); + } + // cherry-pick from google end } ScopedCommitCompletionEvent& operator=(const ScopedCommitCompletionEvent&) = delete; @@ -85,6 +98,10 @@ class ScopedCommitCompletionEvent { raw_ptr event_; CommitTimestamps commit_timestamps_; raw_ptr main_thread_task_runner_; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool notify_main_; + // cherry-pick from google end base::WeakPtr proxy_main_weak_ptr_; }; @@ -374,8 +391,15 @@ void ProxyImpl::NotifyReadyToCommitOnImpl( // variable on the call stack of the main thread. If NonBlockingCommit is // enabled, then the commit timestamps are transmitted back to the main thread // by ScopedCommitCompletionEvent. - DCHECK_NE((bool)commit_timestamps, - base::FeatureList::IsEnabled(features::kNonBlockingCommit)); + + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // DCHECK_NE((bool)commit_timestamps, + // base::FeatureList::IsEnabled(features::kNonBlockingCommit)); + DCHECK_EQ((bool)commit_timestamps, + task_runner_provider_->IsMainThreadBlocked()); + // cherry-pick from google end + base::TimeTicks start_time = base::TimeTicks::Now(); if (commit_timestamps) commit_timestamps->start = start_time; @@ -399,6 +423,10 @@ void ProxyImpl::NotifyReadyToCommitOnImpl( data_for_commit_ = std::make_unique( std::make_unique( completion_event, start_time, MainThreadTaskRunner(), + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + /*notify_main*/!commit_timestamps, + // cherry-pick from google end proxy_main_weak_ptr_), std::move(commit_state), unsafe_state, commit_timestamps); hung_commit_timer_.Start( diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc index d48bf2e191..bda4accdc4 100644 --- a/cc/trees/proxy_main.cc +++ b/cc/trees/proxy_main.cc @@ -245,6 +245,13 @@ void ProxyMain::BeginMainFrame( commit_timeout = true; } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit) || + block_on_next_commit_; + block_on_next_commit_ = false; + // cherry-pick from google end + bool scroll_and_viewport_changes_synced = false; if (!IsDeferringCommits()) { // Synchronizes scroll offsets and page scale deltas (for pinch zoom) from @@ -434,7 +441,12 @@ void ProxyMain::BeginMainFrame( // point of view, but asynchronously performed on the impl thread, // coordinated by the Scheduler. CommitTimestamps commit_timestamps; - bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit); + + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit); + // cherry-pick from google end + { TRACE_EVENT_WITH_FLOW0("viz,benchmark", "MainFrame.NotifyReadyToCommitOnMain", @@ -647,6 +659,17 @@ void ProxyMain::SetPauseRendering(bool pause_rendering) { base::Unretained(proxy_impl_.get()), pause_rendering_)); } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void ProxyMain::SetInputResponsePending() { + // If the next main frame will contain the visual response to an input event, + // we pause execution on the main thread until the compositor thread finishes + // processing the commit. This is done to minimize thread contention while + // the compositor is doing critical-path work. + block_on_next_commit_ = true; +} + // cherry-pick from google end + bool ProxyMain::StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h index 359bf6964c..32a41eda4e 100644 --- a/cc/trees/proxy_main.h +++ b/cc/trees/proxy_main.h @@ -111,6 +111,10 @@ class CC_EXPORT ProxyMain : public Proxy { bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; void SetPauseRendering(bool pause_rendering) override; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override; + // cherry-pick from google end bool StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; @@ -181,6 +185,10 @@ class CC_EXPORT ProxyMain : public Proxy { absl::optional paint_holding_reason_; bool pause_rendering_; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool block_on_next_commit_ = false; + // cherry-pick from google end // Only used when defer_commits_ is active and must be set in such cases. base::TimeTicks commits_restart_time_; diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 797a4a0c98..551a3050d1 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc @@ -374,6 +374,11 @@ void SingleThreadProxy::SetPauseRendering(bool pause_rendering) { scheduler_on_impl_thread_->SetPauseRendering(pause_rendering_); } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SingleThreadProxy::SetInputResponsePending() {} + // cherry-pick from google end + bool SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index a148dd2839..e155cee446 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -63,6 +63,10 @@ class CC_EXPORT SingleThreadProxy : public Proxy, bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; void SetPauseRendering(bool pause_rendering) override; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override; + // cherry-pick from google end bool StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; -- Gitee From c362d2fb39aebc6ae7dd6e77a4693493b9c167f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJonah=5Fxia=E2=80=9D?= <“xiajianchun1@huawei.com”> Date: Wed, 11 Dec 2024 23:15:31 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=B0=B7=E6=AD=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=E5=A4=84=E7=90=86=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=BF=87=E7=A8=8B=E4=B8=AD=E5=90=8C=E6=AD=A5?= =?UTF-8?q?commit=EF=BC=8C=E9=9D=9E=E4=BA=8B=E4=BB=B6=E6=97=B6non-block-co?= =?UTF-8?q?mmit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: “Jonah_xia” <“xiajianchun1@huawei.com”> 同步谷歌修改:处理输入事件过程中同步commit,非事件时non-block-commit Signed-off-by: “Jonah_xia” <“xiajianchun1@huawei.com”> --- cc/test/fake_proxy.h | 5 ++++- cc/trees/layer_tree_host.h | 7 ++++++- cc/trees/proxy.h | 7 +++++++ cc/trees/proxy_impl.cc | 32 ++++++++++++++++++++++++++++++-- cc/trees/proxy_main.cc | 25 ++++++++++++++++++++++++- cc/trees/proxy_main.h | 8 ++++++++ cc/trees/single_thread_proxy.cc | 5 +++++ cc/trees/single_thread_proxy.h | 4 ++++ 8 files changed, 88 insertions(+), 5 deletions(-) diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h index e4552eb3bc..d181bed2e9 100644 --- a/cc/test/fake_proxy.h +++ b/cc/test/fake_proxy.h @@ -64,7 +64,10 @@ class FakeProxy : public Proxy { base::OnceClosure callback) override {} double GetPercentDroppedFrames() const override; void SetPauseRendering(bool pause_rendering) override {} - + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override {} + // cherry-pick from google end private: raw_ptr layer_tree_host_; }; diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index 0e92854e7f..48bb5eb965 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -905,7 +905,12 @@ void RegisterClippedVisualViewportSelectionBounds( [[nodiscard]] base::AutoReset SimulateSyncingDeltasForTesting() { return base::AutoReset(&syncing_deltas_for_test_, true); } - + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool WaitedForCommitForTesting() const { + return waited_for_protected_sequence_; + } + // cherry-pick from google end void IncrementVisualUpdateDuration(base::TimeDelta visual_update_duration); #if BUILDFLAG(IS_OHOS) diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index 44f02bf68c..907290491c 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -66,6 +66,13 @@ class CC_EXPORT Proxy { // Pauses all main and impl-side rendering. virtual void SetPauseRendering(bool pause_rendering) = 0; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // Indicates that the next main frame will contain the result of running an + // event handler for an input event. + virtual void SetInputResponsePending() = 0; + // cherry-pick from google end + // Defers commits until at most the given |timeout| period has passed, // but continues to update the document lifecycle in // LayerTreeHost::BeginMainFrameUpdate. If multiple calls are made when diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc index b2952fc5ab..67d76cec8e 100644 --- a/cc/trees/proxy_impl.cc +++ b/cc/trees/proxy_impl.cc @@ -62,17 +62,30 @@ class ScopedCommitCompletionEvent { CompletionEvent* event, base::TimeTicks start_time, base::SingleThreadTaskRunner* main_thread_task_runner, + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool notify_main, + // cherry-pick from google end base::WeakPtr proxy_main_weak_ptr) : event_(event), commit_timestamps_({start_time, base::TimeTicks()}), main_thread_task_runner_(main_thread_task_runner), + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + notify_main_(notify_main), + // cherry-pick from google end proxy_main_weak_ptr_(proxy_main_weak_ptr) {} ScopedCommitCompletionEvent(const ScopedCommitCompletionEvent&) = delete; ~ScopedCommitCompletionEvent() { event_.ExtractAsDangling()->Signal(); + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + if (notify_main_) { main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::DidCompleteCommit, proxy_main_weak_ptr_, commit_timestamps_)); + } + // cherry-pick from google end } ScopedCommitCompletionEvent& operator=(const ScopedCommitCompletionEvent&) = delete; @@ -85,6 +98,10 @@ class ScopedCommitCompletionEvent { raw_ptr event_; CommitTimestamps commit_timestamps_; raw_ptr main_thread_task_runner_; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool notify_main_; + // cherry-pick from google end base::WeakPtr proxy_main_weak_ptr_; }; @@ -374,8 +391,15 @@ void ProxyImpl::NotifyReadyToCommitOnImpl( // variable on the call stack of the main thread. If NonBlockingCommit is // enabled, then the commit timestamps are transmitted back to the main thread // by ScopedCommitCompletionEvent. - DCHECK_NE((bool)commit_timestamps, - base::FeatureList::IsEnabled(features::kNonBlockingCommit)); + + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // DCHECK_NE((bool)commit_timestamps, + // base::FeatureList::IsEnabled(features::kNonBlockingCommit)); + DCHECK_EQ((bool)commit_timestamps, + task_runner_provider_->IsMainThreadBlocked()); + // cherry-pick from google end + base::TimeTicks start_time = base::TimeTicks::Now(); if (commit_timestamps) commit_timestamps->start = start_time; @@ -399,6 +423,10 @@ void ProxyImpl::NotifyReadyToCommitOnImpl( data_for_commit_ = std::make_unique( std::make_unique( completion_event, start_time, MainThreadTaskRunner(), + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + /*notify_main*/!commit_timestamps, + // cherry-pick from google end proxy_main_weak_ptr_), std::move(commit_state), unsafe_state, commit_timestamps); hung_commit_timer_.Start( diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc index d48bf2e191..bda4accdc4 100644 --- a/cc/trees/proxy_main.cc +++ b/cc/trees/proxy_main.cc @@ -245,6 +245,13 @@ void ProxyMain::BeginMainFrame( commit_timeout = true; } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit) || + block_on_next_commit_; + block_on_next_commit_ = false; + // cherry-pick from google end + bool scroll_and_viewport_changes_synced = false; if (!IsDeferringCommits()) { // Synchronizes scroll offsets and page scale deltas (for pinch zoom) from @@ -434,7 +441,12 @@ void ProxyMain::BeginMainFrame( // point of view, but asynchronously performed on the impl thread, // coordinated by the Scheduler. CommitTimestamps commit_timestamps; - bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit); + + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + // bool blocking = !base::FeatureList::IsEnabled(features::kNonBlockingCommit); + // cherry-pick from google end + { TRACE_EVENT_WITH_FLOW0("viz,benchmark", "MainFrame.NotifyReadyToCommitOnMain", @@ -647,6 +659,17 @@ void ProxyMain::SetPauseRendering(bool pause_rendering) { base::Unretained(proxy_impl_.get()), pause_rendering_)); } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void ProxyMain::SetInputResponsePending() { + // If the next main frame will contain the visual response to an input event, + // we pause execution on the main thread until the compositor thread finishes + // processing the commit. This is done to minimize thread contention while + // the compositor is doing critical-path work. + block_on_next_commit_ = true; +} + // cherry-pick from google end + bool ProxyMain::StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h index 359bf6964c..32a41eda4e 100644 --- a/cc/trees/proxy_main.h +++ b/cc/trees/proxy_main.h @@ -111,6 +111,10 @@ class CC_EXPORT ProxyMain : public Proxy { bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; void SetPauseRendering(bool pause_rendering) override; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override; + // cherry-pick from google end bool StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; @@ -181,6 +185,10 @@ class CC_EXPORT ProxyMain : public Proxy { absl::optional paint_holding_reason_; bool pause_rendering_; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + bool block_on_next_commit_ = false; + // cherry-pick from google end // Only used when defer_commits_ is active and must be set in such cases. base::TimeTicks commits_restart_time_; diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 797a4a0c98..551a3050d1 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc @@ -374,6 +374,11 @@ void SingleThreadProxy::SetPauseRendering(bool pause_rendering) { scheduler_on_impl_thread_->SetPauseRendering(pause_rendering_); } + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SingleThreadProxy::SetInputResponsePending() {} + // cherry-pick from google end + bool SingleThreadProxy::StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) { DCHECK(task_runner_provider_->IsMainThread()); diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index a148dd2839..e155cee446 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -63,6 +63,10 @@ class CC_EXPORT SingleThreadProxy : public Proxy, bool RequestedAnimatePending() override; void SetDeferMainFrameUpdate(bool defer_main_frame_update) override; void SetPauseRendering(bool pause_rendering) override; + // cherry-pick from google begin + // https://chromium-review.googlesource.com/c/chromium/src/+/4546241 + void SetInputResponsePending() override; + // cherry-pick from google end bool StartDeferringCommits(base::TimeDelta timeout, PaintHoldingReason reason) override; void StopDeferringCommits(PaintHoldingCommitTrigger) override; -- Gitee