粘貼不了新的東西(粘貼突然不能用了)
1025
2022-05-30
Windows的圖形界面架構
window圖像渲染的基本流程
window桌面程序UI自動化測試技術
Win32程序
WPF程序
UIAutomation
Windows的圖形界面架構
window圖像渲染的基本流程
window桌面程序UI自動化測試技術
Win32程序
WPF程序
UIAutomation
桌面程序圖像渲染性能測試實踐
普通PC桌面WPF應用
大屏幕可視化WPF應用
Windows的圖形界面架構
從Windows Vista之后,Desktop composition的部分就由Desktop Window Manager完成了(當然是啟用Aero的情況下,Windows 8起DWM是必須開啟的
如上圖,應用程序畫完了界面,告訴DWM把它放到桌面上去
DWM本身是基于Direct3D的,D3D下面是WDDM驅(qū)動
至于應用程序,絕大多數(shù)win桌面應用都是基于GDI的,很老的圖形庫 (從某個版本起GDI也是跑在D3D之上了,于是顯卡廠家就不用寫GDI驅(qū)動了),D3D(比如基于WPF的應用,今天主要介紹的應用),OpenGL(現(xiàn)在的Windows的圖形架構是以DirectX為主,OpenGL支持需要OpenGL installable client driver)
window圖像渲染的基本流程
從程序中提交一個Draw,數(shù)據(jù)需要經(jīng)過:
A
p
p
?
>
D
X
r
u
n
t
i
m
e
?
>
U
s
e
r
m
o
d
e
d
r
i
v
e
r
?
>
d
x
g
k
r
n
l
?
>
K
e
r
n
e
l
m
o
d
e
d
r
i
v
e
r
?
>
G
P
U
App->DX runtime->User mode driver->dxgkrnl->Kernel mode driver->GPU
App?>DXruntime?>Usermodedriver?>dxgkrnl?>Kernelmodedriver?>GPU
在到達GPU之前,全都是在CPU上執(zhí)行的,所以從程序本身是無法獲取渲染結果
到這里就為我們做window桌面程序圖像渲染性能測試帶來兩個問題:
怎么檢查圖像渲染的質(zhì)量?
怎么獲取圖像渲染的響應時間?
由于需要桌面UI自動化測試的技術,所以下面我們介紹window桌面程序UI自動化測試技術
window桌面程序UI自動化測試技術
Win32程序
使用 Win32 API 來創(chuàng)建的程序成為Win32程序。
提供 Win32 API的dll被加載到應用程序的進程中,應用程序通過這些API來創(chuàng)建線程、窗口和控件。
Win32程序中,所有窗口和控件都是一個窗口類的實例,都擁有一個窗口句柄,窗口對象屬于內(nèi)核對象,由Windows子系統(tǒng)來維護。
Windows子系統(tǒng)為標準控件定義了窗口類,并使用GDI來繪制這些標準控件。
Win32程序采用消息循環(huán)機制:
WPF程序
WPF的控件不再是通過Win32 API來創(chuàng)建窗口,使用Win32 API并不能查找和操作WPF控件
WPF所有控件和動畫都是使用DirectX 繪制
WPF控件不直接支持MSAA,而是通過 UIA 用橋轉(zhuǎn)換技術來支持MSAA
WPF用AutomationPeer類支持自動化,每一種控件都有對應的 AutomationPeer類。AutomationPeer不直接暴露給測試客戶端,而是通過UIA來使用。UIA向應用程序窗口發(fā)送WM_GetObject消息,獲得由AutomationPeer實現(xiàn)的UIA Server端Provider。AutomationPeer由控件創(chuàng)建(OnCreateAutomationPeer)
UIAutomation
UIAutomation是微軟從Windows Vista開始推出的一套全新UI自動化測試技術, 簡稱UIA。
UIA定義了全新的、針對UI自動化的接口和模式。測試程序可以通過這些接口來查找和操作控件。
遍歷和條件化查詢:TreeWalker/FindAll
UI元素屬性的UIA Property, 包括Name、 ID、Type、ClassName、Location、 Visibility等等。
UIA Pattern(控件的行為模式), 比如Select、Expand、Resize、 Check、Value等等。
UIA的兩種實現(xiàn)方法:
Server-Side Provider:
由被測程序?qū)崿F(xiàn)UIA定義的接口,返回給測試程序。WPF程序通過這種方式來支持UIA。
Client-Side Provider:
測試程序沒有實現(xiàn)UIA定義的接口。由UIA Runtime或測試程序自己來實現(xiàn)。比如Win32和WinForm程序,UIA Runtime通過MSAA來實現(xiàn)UIA定義的接口。UIA定義了全新的、針對UI自動化的接口和模式。測試程序可以通過這些接口來查找和操作控件。
遍歷和條件化查詢:TreeWalker/FindAll
UI元素屬性的UIA Property, 包括Name、 ID、Type、ClassName、Location、 Visibility等等。
UIA Pattern(控件的行為模式), 比如Select、Expand、Resize、 Check、Value等等。
UIA驅(qū)動計算器示例:
using System.Windows.Automation; PropertyCondition conditionName = new PropertyCondition(AutomationElement.NameProperty, "計算器"); AutomationElement calcWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, conditionName); //Button 1 PropertyCondition conditionBtn1 = new PropertyCondition(AutomationElement.AutomationIdProperty, "131"); AutomationElement button1 = calcWindow.FindFirst(TreeScope.Descendants, conditionBtn1); //點擊Button1 InvokePattern invokePatternBtn1 = (InvokePattern)button1.GetCurrentPattern(InvokePattern.Pattern); invokePatternBtn1.Invoke();
桌面程序圖像渲染性能測試實踐
因為我們的性能測試是基于部分UI自動化測試技術落地的,在此介紹一下我們的UI自動化測試解決方案
測試解決方案應至少包括5個項目,其中前兩個是和其他測試解決方案共享的。5個項目均為類庫,不能直接執(zhí)行。
AI.Robot為UI驅(qū)動框架。 AI.Utilities項目里是一些輔助類,如數(shù)據(jù)庫讀寫、圖片對比等(性能測試需用到)。
AI.App.UIObjects項目里放置UI對象。把UI對象集中放置到此項目中是為了減少界面更改帶來的維護工作量。
AI.App.BusinessLogic項目里放置可重復用到的界面元素操作的集合,通常是為了完成一項特定的業(yè)務的步驟的集合。
AI.App.TestCases里放置測試用例。并按照MSTest單元測試框架組織測試類和測試方法。包含測試類和測試方法的.net類庫稱為測試程序集。
今天討論的桌面程序圖像渲染性能測試主要應用于以下兩種應用:
普通PC桌面WPF應用(分辨率<2K)
大屏幕可視化WPF應用(分辨率>8K)
普通PC桌面WPF應用
首先,回到之前的兩個問題:
怎么檢查圖像渲染的質(zhì)量?
怎么獲取圖像渲染的響應時間?
首先將正常渲染完的控件輸出成圖片
// 將控件uiElement輸出到圖片aa.bmp uiElement.CaptureBitmap(@"D:\aa.bmp");
使用測試工具驅(qū)動啟動被測應用并開始計時,在渲染過程中快速截圖,實時比較兩幅圖片是否完全相等,如果相等并結束計時并寫入響應時間。
// 比較兩幅圖片是否完全相同(所有像素點都相同) bool isEqual = ImageHelper.IsEqual(@"D:\image1.bmp", @"D:\image2.bmp");
判斷兩幅圖是否完全相同
///
影響圖片輸出的因素:
1.顯卡,不同顯卡輸出文字和漸變色的時候有細微的差別,所以不同機器上顯示的控件和輸出的圖片通常不完全相同,特別是當控件上有文字的時候。
2.DPI設置,將機器的DPI設置為120%時,100x100大小的控件將顯示為120x120像素
3.當在遠程桌面上運行測試時,遠程連接的選項“字體平滑”會影響控件顯示和輸出的圖片
大屏幕可視化WPF應用
由于大屏幕的分辨率8K起步,也就不適應上面的截圖判斷方法了,為什么呢?
我們簡單來計算8K圖片的大小吧
分辨率:
7680
×
4320
=
33177600
像素
≈
95
M
B
分辨率:7680×4320=33177600像素≈95MB
分辨率:7680×4320=33177600像素≈95MB
我們常見顯示器用256種狀態(tài)標識屏幕上某種顏色的灰度,而屏幕采用三基色紅綠藍(RGB),不壓縮的情況下一個像素需要占用24bit(3字節(jié)),這個就是常說的24位真彩色。
近100MB的圖片實時截圖并進行判斷,本身兩個動作就會對機器的計算資源消耗巨大,會嚴重影響性能測試準確性。
這里我們折中使用實時判斷標志位RGB像素點的方法來判斷圖片渲染的結果
首先,我們會使用取色器采樣幾個最后圖像渲染完成的坐標像素點RGB值
原理其實很簡單,只需要兩步:
鼠標移動的時候獲取鼠標光標的位置
鼠標單擊獲取當前鼠標光標的位置的RGB顏色值到粘貼板
涉及HookManager技術
namespace GetColor { public partial class Form1 : Form { //顯示設備上下文環(huán)境的句柄 private IntPtr hdc = IntPtr.Zero; private int maxY, maxX; public Form1() { InitializeComponent(); this.BackColor = Color.Brown; this.TransparencyKey = Color.Brown; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.WindowState = FormWindowState.Maximized; this.TopMost = true; this.Cursor = Cursors.Cross; HookManager.MouseMove += OnMouseMove; this.KeyPress += OnKeyPress; this.MouseClick += OnMouseDown; Size screenSize = Screen.PrimaryScreen.Bounds.Size; this.maxX = screenSize.Width - this.label1.Width; this.maxY = screenSize.Height - this.label1.Height; this.SetLabel(Control.MousePosition.X, Control.MousePosition.Y); } //鼠標移動,實時獲取鼠標位置 private void OnMouseMove(object sender, MouseEventArgs e) { this.SetLabel(Control.MousePosition.X, Control.MousePosition.Y); } private void SetLabel(int x, int y) { if(this.hdc == IntPtr.Zero) { this.hdc = GetDC(IntPtr.Zero); this.label1.Location = new Point(Math.Min(this.maxX, x + 10), Math.Min(this.maxY, y + 10)); int color = GetPixel(this.hdc, x, y); this.label1.Text = string.Format("X={0},Y={1}\r\nColor:#{2}\r\n{3}", x, y, Convert.ToString(color, 16).PadLeft(6, '0'), color); this.Update(); ReleaseDC(IntPtr.Zero, this.hdc); DeleteDC(this.hdc); this.hdc = IntPtr.Zero; } } private void OnKeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == (char)Keys.Escape) { UnHook(); Application.Exit(); } } private void OnMouseDown(object sender, MouseEventArgs e) { //檢索一指定窗口的客戶區(qū)或整個屏幕的顯示設備上下文環(huán)境的句柄 this.hdc = GetDC(IntPtr.Zero); //指定坐標點的像素的RGB顏色值。 int color = GetPixel(this.hdc, e.X, e.Y); //鼠標單擊拷貝值 if (e.Button == MouseButtons.Left) { Clipboard.SetText(string.Format("
小程序截圖:
把圖像渲染結果采樣點填入測試工具的XML配置文件后,我們使用測試工具啟動程序開始計時并實判斷采樣標志位像素點的RGB值,如果全部通過結束計時并寫入渲染響應時間
public void ValidateStage(Screen screen) { bool allReady = false; foreach (var stage in screen.ValidationStages) { stage.IsReady = false; } DateTime now = DateTime.Now; while (!allReady && (DateTime.Now - now).TotalSeconds < this.Config.Timeout) { allReady = true; foreach (var stage in screen.ValidationStages) { if(!stage.IsReady) { stage.IsReady = this.ValidatePointsOneTime(stage.ValidatePoints); if (stage.IsReady) { TimeSpan cost = DateTime.Now - now; this.logger.LogInfo(string.Format("{0}耗時{1}", stage.Name, cost.TotalSeconds)); this.logData += string.Format(",{0}", cost.TotalSeconds); } else { allReady = false; } } } Thread.Sleep(this.Config.TryInterval); } foreach (var stage in screen.ValidationStages) { if(!stage.IsReady) { foreach (ValidatePoint point in stage.ValidatePoints) { int color = Root.GetPointColor(point.X, point.Y); this.logger.LogInfo(string.Format("點({0}, {1})的顏色為{2}", point.X, point.Y, color)); } this.logger.LogInfo(string.Format("{0}秒內(nèi){1}未繪制完", this.Config.Timeout.ToString(), stage.Name)); this.logData += string.Format(",>{0}", this.Config.Timeout); } } } public void ValidateStage(Screen screen, ValidationStage stage) { DateTime now = DateTime.Now; bool screenReady = this.ValidatePoints(stage.ValidatePoints); if (screenReady) { TimeSpan cost = DateTime.Now - now; this.logger.LogInfo(string.Format("{0}耗時{1}", stage.Name, cost.TotalSeconds)); this.logData += string.Format(",{0}", cost.TotalSeconds); } else { this.logger.LogInfo(string.Format("{0}秒內(nèi){1}未繪制完", this.Config.Timeout.ToString(), stage.Name)); this.logData += string.Format(",>{0}", this.Config.Timeout); } } public bool ValidatePointsOneTime(List
實際效果:
參考資料:
[1]:Windows Display Driver Model (WDDM) Architecture (Windows Drivers)
[2]:The Desktop Window Manager (Windows)
API GUI Windows 云性能測試服務 CPTS 渲染
版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。