觸控書寫延遲,是觸控式螢幕很核心的引數。從使用者在觸控式螢幕上進行觸控操作到裝置作出響應之間的時間差,這個延遲高低會影響快速反應的應用場景使用如白板書寫、玩遊戲。
而觸控延遲主要影響因素有:觸控框報點率、軟體框架延時(用於觸控資料接收、執行緒切換)、軟體業務邏輯處理
我們這裡介紹下觸控框報點率 Touch Report Rate,觸控式螢幕每秒報告觸控資料的次數(Hz),會根據裝置型別不同而變化
觸控操作TouchMove(StylusMove)是定時上報一次,一次會上報1-N個點。
1 private void MainWindow_OnStylusMove(object sender, StylusEventArgs e) 2 { 3 var currentPointsCount = e.GetStylusPoints(this).Count; 4 }
不同螢幕定時觸控上報時間不同,我們可以透過Stylus事件收集看看
1 private void MainWindow_OnStylusDown(object sender, StylusDownEventArgs e) 2 { 3 _stylusDown = true; 4 _startTick = Environment.TickCount; 5 _stylusEntryCount++; 6 _distinctPoints.AddRange(e.GetStylusPoints(this).Distinct()); 7 } 8 9 private void MainWindow_OnStylusMove(object sender, StylusEventArgs e) 10 { 11 if (!_stylusDown) 12 { 13 return; 14 } 15 _stylusEntryCount++; 16 _distinctPoints.AddRange(e.GetStylusPoints(this).Distinct()); 17 }
輸入平均間隔:var stylusEntryTime = (Environment.TickCount - _startTick) / (double)_stylusEntryCount
Dell觸控式螢幕
我日常用於開發聯調觸控相關功能的觸控式螢幕,戴爾觸控式螢幕Dell-P2418HT 1080P的觸控資料:
1. WPF應用StylusMove輸入,拿到的間隔是33ms,即30幀
2. 一次輸入包含1-7個點
3. 再算個點平均間隔16.7ms -- 這個資料也是有意義的,可以用於評估類似書寫預測1個點可以提升的效能
使用BusHold,我們看下真實點輸入間隔:
BusHold第4列資料,01是指Down/Move事件,00是指Up操作結束。我們看到Move操作輸入間隔基本在16-17ms,說明觸控框真實報點間隔17ms左右,即60幀。
上面WPF監聽Stylus事件,拿到的觸控操作輸入間隔是33ms。為何應用層拿到的觸控資料幀率比觸控框低呢?
我們試試StylusPlugin(StylusPlugin方案可以在WPF路由事件之前拿到觸控資料,可以用於觸控書寫加速方案)拿觸控執行緒的觸控資料,以及WPF路由觸控事件資料對比下:
獲取StylusPlugin外掛事件,程式碼比較簡單。但需要注意的事,事件需要過濾掉主執行緒,過濾後才是觸控工作執行緒的觸控資料:
1 public partial class MainWindow : Window 2 { 3 public MainWindow() 4 { 5 InitializeComponent(); 6 StylusPlugIns.Add(new TestStylusPlugIn(Thread.CurrentThread.ManagedThreadId)); 7 } 8 } 9 class TestStylusPlugIn : StylusPlugIn 10 { 11 private readonly int _uiThreadId; 12 13 public TestStylusPlugIn(int uiThreadId) 14 { 15 _uiThreadId = uiThreadId; 16 } 17 18 protected override void OnStylusMove(RawStylusInput rawStylusInput) 19 { 20 if (Thread.CurrentThread.ManagedThreadId == _uiThreadId) 21 { 22 return; 23 } 24 Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")}:StylusPluginOnStylusMove {rawStylusInput.GetStylusPoints().Count}個點"); 25 base.OnStylusMove(rawStylusInput); 26 } 27 }
與BusHold同樣是7個點,
觸控執行緒事件,只不過2個點合併到一次觸控輸入事件裡了。所以這2個點合併操作不是路由事件內處理的,也不是WPF路由,減少了傳遞給應用程式的事件數量。我估計是InputManager協調輸入並派發事件這塊做了合併操作。我們可以再來一組測試資料,這裡我們看到這之間的輸入間隔不穩定:
但到StylusPlugin幀率減少的並不多,和小夥伴德熙溝通,有歷史點合併但並不多。重要的是,以2個點週期輸入間隔原來的16ms變大到33ms:
UI路由事件,我們看上面控制檯輸出,移動事件間隔30ms左右報個一次輸入事件,最後一個Move事件裡有4個點。這裡的4個點輸入,合併了1+2+1上面觸控執行緒事件的3次輸入,所以WPF路由事件管理也有觸控訊息的佇列,會合並觸控點
那有沒有可能減少觸控資料的合併,與觸控框保持一致的幀率呢?答案是有的,可以在WPF開啟如UWP的Point訊息:
1 public partial class App : Application 2 { 3 public App() 4 { 5 AppContext.SetSwitch("Switch.System.Windows.Input.Stylus.EnablePointerSupport", true); 6 } 7 }
新增EnablePointerSupport後,我們監聽TouchMove事件:
觸控移動事件觸發間隔就減少到16-17ms了,到與觸控框報點率相同幀率,good!
開啟Pointer相關內容也可以看德熙MVP的部落格 WPF dotnet core 如何開啟 Pointer 訊息的支援 (lindexi.com),但開啟Pointer也有很多坑WPF 開啟Pointer訊息存在的坑 (lindexi.com),大家使用時多瞭解下已知缺陷
觸控大屏
再看看目前市面上互動觸控大屏所使用的富創通、華欣主流觸控框產品路由觸控資料:
輸入間隔為15.6ms左右,觸控點之間平均間隔為7ms左右,即觸控框報點140幀以上,應用層觸控資料60幀。
最近有一款改良的富創通觸控G框版本,我們也由應用端收集下資料:
輸入間隔沒變也是15.6ms左右,觸控點之間平均間隔為4.5ms,即觸控框報點220幀左右,應用層觸控資料依然是60幀。說明這款觸控框提升了本身的報點率,但應用層根據硬體條件限制了60幀觸控報點。
至於這三個觸控式螢幕的觸控資料幀數限制,
- Dell觸控式螢幕透過BusHold抓到原始觸控資料是60幀,但WPF處理後得到30幀
- 大屏觸控框透過BusHold抓到原始觸控資料140幀,但WPF處理後得到60幀
- 大屏觸控框升級版透過BusHold抓到原始觸控資料220幀,但WPF處理後也是得到60幀
是螢幕什麼屬性限制WPF內觸控資料幀率的呢?和小夥伴溝通盲猜WPF內可能卡30-60幀選項、根據觸控原始幀數去選30或者60,WPF在Dell觸控式螢幕內最終得到30幀可能是觸控幀數60幀及以下被分配30幀?。。。我還沒搞懂,有知道的朋友可以解惑下,期待你的指導
總結下,
1. 上面我們也介紹了開啟pointer,WPF輸出的觸控輸入間隔與達到觸控框原始幀率。所以開啟Pointer書寫延遲能降低很大一部分
2. 觸控框原始幀率提升後,也能降低觸控輸入間隔,即使不開啟Pointer也能減少與上一次觸控間隔減少。所以可以與觸控框廠商溝通提升報點率,降低書寫延遲
瞭解觸控報點率,才能真正去把書寫效能以及書寫平滑這塊做好。
上面Demo見:kybs00/StylusPointRateDemo: 大屏觸控報點率檢測Demo (github.com)、kybs00/StylusPluginEventDemo: 觸控式螢幕觸筆外掛StylusPlugin事件獲取 (github.com)