Quy trình kết xuất hình ảnh 2D của Android có hỗ trợ tăng tốc phần cứng, nghĩa là tất cả các thao tác vẽ được thực hiện trên canvas đều sử dụng GPU. Do đã tăng tài nguyên cần thiết để bật tính năng tăng tốc phần cứng, nên ứng dụng sẽ tốn nhiều RAM hơn.
Tính năng tăng tốc phần cứng được bật theo mặc định. Nếu ứng dụng của bạn chỉ sử dụng các thành phần kết hợp chuẩn, thì việc bật tính năng này trên toàn cục sẽ không gây ra hiệu ứng bất lợi nào về mặt hình ảnh. Tuy nhiên, vì tính năng tăng tốc phần cứng không hỗ trợ toàn bộ thao tác vẽ 2D, nên việc bật tính năng này có thể ảnh hưởng đến một số lệnh gọi vẽ tuỳ chỉnh. Sự cố sẽ xuất hiện dưới dạng các phần tử bị ẩn đi, ngoại lệ hoặc pixel kết xuất không chính xác. Để khắc phục điều này, Android cung cấp cho bạn tuỳ chọn bật hoặc tắt tính năng tăng tốc phần cứng ở nhiều cấp độ. Hãy xem phần Kiểm soát tính năng tăng tốc phần cứng.
Nếu ứng dụng của bạn có thực hiện thao tác vẽ tuỳ chỉnh, hãy kiểm thử ứng dụng đó trên các thiết bị phần cứng thực tế đã bật tính năng tăng tốc phần cứng để phát hiện mọi vấn đề. Phần Hỗ trợ cho hoạt động vẽ mô tả các vấn đề đã biết với tính năng tăng tốc phần cứng, cũng như cách khắc phục.
Ngoài ra, hãy xem bài viết Sử dụng OpenGL với các API Khung.
Kiểm soát tính năng tăng tốc phần cứng
Bạn có thể kiểm soát tính năng tăng tốc phần cứng ở các cấp độ sau:
- Application (Ứng dụng)
- Activity (Hoạt động)
- Cửa sổ
- Thành phần kết hợp
Cấp độ ứng dụng
Trong tệp kê khai Android, hãy thêm thuộc tính sau vào thẻ <application> để bật tính năng tăng tốc phần cứng cho toàn bộ ứng dụng:
<application android:hardwareAccelerated="true" ...>
Cấp độ hoạt động
Nếu tính năng tăng tốc phần cứng được bật trên toàn cục mà ứng dụng của bạn hoạt động không đúng cách, thì bạn cũng có thể kiểm soát tính năng này đối với các hoạt động riêng lẻ. Để bật hoặc tắt tính năng tăng tốc phần cứng ở cấp độ hoạt động, bạn có thể sử dụng thuộc tính android:hardwareAccelerated cho phần tử <activity>. Ví dụ sau đây sẽ bật tính năng tăng tốc phần cứng cho toàn bộ ứng dụng nhưng tắt tính năng này đối với một hoạt động:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Cấp độ cửa sổ
Nếu cần kiểm soát chi tiết hơn nữa, bạn có thể bật tính năng tăng tốc phần cứng cho một cửa sổ cụ thể bằng mã sau:
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
Cấp thành phần kết hợp
Trong Compose, không có công tắc cho mỗi thành phần kết hợp để tắt tính năng tăng tốc phần cứng.
Để kết xuất một thành phần kết hợp vào lớp riêng, hãy dùng Modifier.graphicsLayer. Điều này cho phép các thuộc tính biến đổi (chẳng hạn như alpha, scaleX, scaleY, translationX, translationY, rotationX, rotationY, rotationZ và transformOrigin) thay đổi mà không cần chạy lại mã vẽ của thành phần kết hợp. Để có hiệu suất tốt nhất, hãy luôn sử dụng dạng lambda của đối tượng sửa đổi để đặt các thuộc tính này.
Để buộc một vùng đệm ngoài màn hình cho các thao tác vẽ nâng cao (chẳng hạn như trộn tuỳ chỉnh trong lớp), hãy dùng CompositingStrategy.Offscreen.
Để biết thêm thông tin, hãy xem phần Đối tượng sửa đổi đồ hoạ.
Nếu có một thao tác vẽ tuỳ chỉnh bắt buộc phải kết xuất phần mềm, bạn có thể lưu trữ một Khung hiển thị cũ bằng AndroidView và gọi setLayerType(View.LAYER_TYPE_SOFTWARE, null) trên khung hiển thị đó.
Hỗ trợ các thao tác vẽ
Khi tăng tốc phần cứng, quy trình kết xuất hình ảnh 2D hỗ trợ các thao tác vẽ Canvas phổ biến nhất cũng như nhiều thao tác ít sử dụng hơn. Tất cả thao tác vẽ được dùng để kết xuất các ứng dụng xuất hiện trên Android, các thành phần kết hợp tiêu chuẩn và các hiệu ứng hình ảnh nâng cao phổ biến như phản chiếu và hoạ tiết xếp kề đều được hỗ trợ.
Bảng sau đây mô tả mức độ hỗ trợ đối với các thao tác khác nhau theo các cấp độ API:
| Cấp độ API đầu tiên được hỗ trợ | ||||
| Canvas | ||||
| drawBitmapMesh() (mảng màu) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() có chế độ xoay/góc nhìn | 18 | |||
| Paint | ||||
| setAntiAlias() (dành cho văn bản) | 18 | |||
| setAntiAlias() (dành cho các đường thẳng) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (dành cho các đường thẳng) | 28 | |||
| setShadowLayer() (không dành cho văn bản) | 28 | |||
| setStrokeCap() (dành cho các đường thẳng) | 18 | |||
| setStrokeCap() (dành cho các điểm) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (bộ đệm khung) | 28 | |||
| PorterDuff.Mode.LIGHTEN (bộ đệm khung) | 28 | |||
| PorterDuff.Mode.OVERLAY (bộ đệm khung) | 28 | |||
| Trình đổ bóng | ||||
| ComposeShader bên trong ComposeShader | 28 | |||
| Các trình đổ bóng cùng kiểu bên trong ComposeShader | 28 | |||
| Ma trận cục bộ trên ComposeShader | 18 | |||
Điều chỉnh tỷ lệ Canvas
Ban đầu, tính năng tăng tốc phần cứng được xây dựng để hỗ trợ quy trình kết xuất hình ảnh 2D đối với các thao tác vẽ chưa điều chỉnh tỷ lệ. Tuy nhiên, một số thao tác vẽ bị giảm chất lượng đáng kể ở những hệ số tỷ lệ cao hơn. Các thao tác này được triển khai bằng cách vẽ các hoạ tiết ở tỷ lệ 1.0, sau đó sẽ được GPU chuyển đổi. Kể từ cấp độ API 28, bạn có thể điều chỉnh tỷ lệ mọi thao tác vẽ mà không gặp vấn đề gì.
Bảng sau đây cho biết thời điểm thay đổi quy trình triển khai để xử lý chính xác trên những hệ số tỷ lệ lớn:
| Thao tác vẽ được điều chỉnh theo tỷ lệ | Cấp độ API đầu tiên được hỗ trợ |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| Hình dạng đơn giản | 17 |
| Hình dạng phức tạp | 28 |
| drawPath() | 28 |
| Lớp đổ bóng | 28 |
Nếu một thao tác vẽ mà bạn phụ thuộc vào không được tăng tốc phần cứng, hãy kết xuất bản vẽ chịu ảnh hưởng vào một phần mềm Bitmap (hoặc ImageBitmap) ngoài màn hình và vẽ kết quả. Phần còn lại của giao diện người dùng sẽ giữ nguyên đường dẫn được tăng tốc phần cứng.
Mẹo và thủ thuật
Việc chuyển sang đồ hoạ 2D được tăng tốc phần cứng có thể làm tăng hiệu suất ngay lập tức, nhưng bạn vẫn nên thiết kế ứng dụng để dùng GPU sao cho hiệu quả bằng cách làm theo các đề xuất sau:
- Giảm thiểu độ phức tạp của bố cục và quá trình kết hợp lại
- Giữ cho cây bố cục có độ sâu vừa phải và hạn chế số lần kết hợp lại. Trì hoãn các lần đọc trạng thái đến phạm vi hẹp nhất, để một thay đổi vẽ lại vùng nhỏ nhất có thể. Ví dụ: đọc trạng thái động bên trong
Modifier.graphicsLayer { }thay vì trong phần nội dung của một thành phần kết hợp. Để biết thêm thông tin, hãy xem phần Hiệu suất của Jetpack Compose. - Tránh vẽ nhiều lần
- Không vẽ quá nhiều lớp phủ chồng lên nhau. Xoá mọi phần tử trên giao diện người dùng bị che khuất hoàn toàn bởi các phần tử mờ khác ở phía trên. Nếu bạn cần vẽ nhiều lớp phủ chồng lên nhau, hãy cân nhắc hợp nhất các lớp phủ này thành một lớp phủ duy nhất. Một nguyên tắc hay là phần cứng hiện tại không nên vẽ quá 2,5 lần số pixel trên màn hình cho mỗi khung hình (kể cả số pixel trong suốt mà bitmap tính đến).
- Không tạo đối tượng kết xuất trong các phương thức vẽ
- Một lỗi thường gặp là tạo một
Paintmới hoặc mộtPathmới mỗi khi gọi một phương thức kết xuất hình ảnh. Điều này buộc trình thu gom rác phải chạy thường xuyên hơn, đồng thời bỏ qua các bộ nhớ đệm và tính năng tối ưu hoá trong quy trình phần cứng. Để tránh điều này, hãy sử dụng lại và thay đổi các đối tượng của bạn:- Sử dụng các phương thức tiêu chuẩn: Các phương thức
DrawScopetiêu chuẩn (chẳng hạn nhưdrawRectvàdrawCircle) đã sử dụng lại các đối tượngPaintnội bộ mà không yêu cầu phân bổ cho nhà phát triển. - Thay đổi thay vì phân bổ lại: Khi viết logic tuỳ chỉnh, hãy dùng
path.rewindđể xoáPathhiện có thay vì tạo mộtPathmới. - Giữ trạng thái một cách hiệu quả: Bên trong một thành phần kết hợp, hãy phân bổ các đối tượng một lần bằng cách sử dụng
remember { Path() }. Nếu bạn đang tạo các tiện ích sửa đổi tuỳ chỉnh có thể dùng lại, hãy triển khai mộtModifier.Nodetuỳ chỉnh bằng cách sử dụngDrawModifierNodeđể phân bổ và dùng lại các đối tượng mà không gây ra việc phân bổ vùng nhớ khối xếp mới.
- Sử dụng các phương thức tiêu chuẩn: Các phương thức
- Không chỉnh sửa hình dạng quá thường xuyên
- Ví dụ: các hình dạng phức tạp, đường dẫn và vòng tròn được kết xuất bằng mặt nạ hoạ tiết. Mỗi lần bạn tạo hoặc sửa đổi một đường dẫn, quy trình phần cứng sẽ tạo một mặt nạ mới. Điều này có thể gây hao tốn tài nguyên.
- Không sửa đổi bitmap quá thường xuyên
- Mỗi khi bạn thay đổi nội dung của một bitmap, nội dung đó sẽ được tải lên lại dưới dạng một hoạ tiết GPU vào lần tiếp theo bạn vẽ.
- Thận trọng khi sử dụng alpha
- Khi bạn tạo một thành phần kết hợp trong suốt bằng cách sử dụng
Modifier.alphahoặc API ảnh động Compose, thành phần đó thường được kết xuất trong vùng đệm ngoài màn hình và làm tăng gấp đôi tỷ lệ đáp ứng bắt buộc. Để tránh hao tổn vùng đệm ngoài màn hình cho nội dung không chồng chéo, hãy đặtCompositingStrategy.ModulateAlpha. Đối với từng lệnh vẽ, hãy áp dụng giá trị alpha trực tiếp cho lệnh vẽ (chẳng hạn như vớicolor = Color.Red.copy(alpha = 0.5f)) mà không cần tạo một lớp.