[Unity Series 2] Tối ưu hiệu năng Ứng dụng game Unity - Rendering Pipeline

CPU được coi là não bộ của phần cứng, có chức năng xử lý mọi thông tin và dữ liệu nhập vào. Giúp thiết bị có thể vận hành và xử lý chơn chu mọi tác vụ yêu cầu. Do vậy việc tối ưu các tác vụ của CPU là một trong những công việc cần được ưu tiên hàng đầu.

Trong phần trước, mình đã giới thiệu về quá trình Rendering và các khái niệm quan trọng như Draw Call, SetPass Call hay Batch. Chắc hẳn các bạn đã nhìn thấy những khái niệm này trong các công cụ tối ưu trong phần 1. Giờ đây các bạn có thể thông qua những khái niệm này để đối chiếu xác định xem ứng dụng của mình đang gặp CPU bound hay GPU bound, từ đó lập kế hoạch tối ưu hiệu năng ứng dụng. Bài viết được tham khảo từ nguồn Performance Optimization Tutorial của Unity.
Tuỳ thuộc vào phần cứng và loại ứng dụng mà bạn hướng tới, sẽ có các quy chuẩn hiệu năng của ứng dụng khác nhau. Trong phần này, mình sẽ đi vào các phương pháp tối ưu cụ thể CPU.
Đầu tiên, mình xin nhắc lại vài kiến thức cũ trong phần trước. Nhìn chung, công việc của CPU trong quá trình Rendering được chia thành ba bước như sau:

  • Xác định xem những object nào cần được vẽ.
  • Thu thập thông tin các object cần được vẽ.
  • Dựa vào các thông tin đó, gửi lệnh tới GPU.

Multithreaded Rendering

Những công việc này sẽ chứa nhiều tác vụ riêng lẻ và những tác vụ này có thể thực hiện trên nhiều luồng (Multiple Threads). Các thread cho phép các tác vụ xảy ra đồng thời. Trong Unity, có ba loại thread được sử dụng trong quá trình Rendering là Main thread, Render Thread và Worker Thread. Ba loại thread này mình đã giới thiệu trong phần 1, các bạn có thể xem lại. Khi các tác vụ Rendering được phân chia giữa các thread riêng biệt, được gọi là Multithreaded Rendering.
Tính năng này sẽ chuyển các tác vụ render graphic từ main thread sang worker thread, rất hữu ích trong trường hợp main thread phải xử lý quá nhiều tác vụ.

Để bật chức năng Multithreaded Rendering, các bạn có thể vào Edit > Project Settings > Player > Android Setting > Other Settings > Multithreaded Rendering.


Tuy nhiên, không phải tất cả các platform đều hỗ trợ Multithreaded Rendering (WebGL), do vậy không phải lúc nào tính năng này cũng sẽ hoạt động như những gì chúng ta mong muốn. Trên các platform không hỗ trợ Multithreaded Rendering, tất cả các tác vụ CPU sẽ đều thực hiện trên cùng một loại thread. Vì vậy, khi đó việc tối ưu bất kỳ tác vụ nào của CPU sẽ đều làm tăng hiệu năng của ứng dụng.

Giảm số lượng SetPass Call

Trong hoạt động gửi lệnh tới GPU, các tác vụ tốn kém nhất chính là các cuộc gọi SetPass Call. Nếu ứng dụng của chúng ta đang bị CPU bound ở các tác vụ gửi lệnh tới GPU, thì việc giảm số lượng SetPass Calls là cách tốt nhất trong hầu hết các trường hợp để cải thiện hiệu suất. Tuy số lượng SetPass Call và mối quan hệ của nó với số lượng Batches phụ thuộc vào nhiều yếu tố, nhưng thông thường chúng ta có thể giảm số lượng SetPass Calls bằng cách giảm số lượng các Batches hoặc tạo ra nhiều objects cùng sử dụng chung RenderState (Batching). Nếu việc giảm số lượng các Batches không làm giảm số lượng SetPass Call, nó vẫn có thể cải thiện hiệu năng. Điều này là do CPU có thể xử lý một Batch đơn lẻ hiệu quả hơn là một tập hợp các Batch, ngay cả khi chúng có cùng một dữ liệu Mesh giống nhau.

Để giảm số lượng Batch và SetPass Call, nhìn chung có một vài phương pháp cơ bản như sau:

  • Giảm số lượng object được render > giảm cả Batch và SetPass Call.
  • Giảm số lần mỗi object được render > giảm SetPass Call.
  • Kết hợp dữ liệu từ các object được render > giảm Batch.

Bởi vì bài viết này khá dài, nên mình xin phép được chia bài tối ưu CPU thành 2 phần. Trong phần tiếp theo mình sẽ đi sâu vào các cách giải quyết cụ thể của các phương pháp giảm số lượng SetPass Call và một vài kĩ thuật tối ưu với Skinned Mesh. Các bạn có thể xem phần tiếp theo của bài viết ở link dưới:
[Unity Series 4] Tối ưu hiệu năng Ứng dụng game Unity - Tối ưu CPU (tiếp)