Tăng tốc phần cứng

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, rotationZtransformOrigin) 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 Paint mới hoặc một Path mớ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 DrawScope tiêu chuẩn (chẳng hạn như drawRectdrawCircle) đã sử dụng lại các đối tượng Paint nộ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á Path hiện có thay vì tạo một Path mớ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ột Modifier.Node tuỳ chỉnh bằng cách sử dụng DrawModifierNode để 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.
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.alpha hoặ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 đặt CompositingStrategy.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ới color = Color.Red.copy(alpha = 0.5f)) mà không cần tạo một lớp.

Tài nguyên khác

Xem nội dung