[Unity Series 5] Tối ưu hiệu năng Ứng dụng game Unity - Tối ưu GPU

Trong phần của Series tối ưu hiệu năng ứng dụng game Unity, mình sẽ giới thiệu các phương pháp giải quyết GPU bound: Fill rate, Memory bandwidth và vertex processing.

7 min read
Bởi Datbq
[Unity Series 5] Tối ưu hiệu năng Ứng dụng game Unity - Tối ưu GPU

[Unity Series 4] Tối ưu hiệu năng Ứng dụng game Unity - Tối ưu CPU (tiếp)

Trong phần trước, mình đã giới thiệu tới các bạn các phương pháp tối ưu khi ứng dụng game của bạn gặp CPU bound. Trong phần này, mình sẽ giới thiệu các phương pháp giải quyết GPU bound. Thông thường, GPU bound xảy ra trong 3 trường hợp sau: Fill rate, Memory bandwidth và vertex processing. Đặc biệt, trong thiết bị điện thoại, Fill rate là vấn đề thường xuyên gặp nhất gây ra GPU bound.

Fill rate

Fill rate là số pixel GPU cần render trên Screen mỗi giây. Do vậy nên nếu game của bạn đang bị GPU bound do Fill rate, có nghĩa là ứng dụng của bạn đang cố gắng vẽ quá nhiều pixel mỗi frame mà GPU có thể xử lý.
fillrate = screen pixels * shader complexity * overdraw (OverDraw mình sẽ giới thiệu ở bên dưới).
Cách kiểm tra ứng dụng của bạn có bị GPU bound do Fill rate như sau:

  • Profile game và note lại thời gian xử lý mỗi Frame của GPU ( sử dụng công cụ Profiler mình đã giới thiệu ở phần 1).
  • Giảm độ phân giải (display resolution) trong Player Settings.
  • Profile lại game và so sánh với thời gian xử lý mỗi frame của GPU mà bạn đã note.

Nếu hiệu năng được cải thiện, có lẽ bạn sẽ cần quan tâm đến những phương pháp tối ưu Fill rate bên dưới.
Tối ưu Fragment Shader: Fragment shader là một phần của shader code, có nhiệm vụ chỉ cho GPU làm thế nào để vẽ một pixel đơn lẻ. Code này được thực hiện bởi GPU cho mỗi pixel mà nó phải vẽ, cho nên nếu code không tối ưu sẽ dễ dàng tạo ra các vấn đề về hiệu năng.

  • Nếu game của bạn đang sử dụng các shader có sẵn trong Unity, bạn nên sử dụng những shader đơn giản và tối ưu nhất mà có thể tạo các hiệu ứng mà bạn mong muốn. Ví dụ như các mobile shader được tối ưu rất tốt. Bạn nên thử nghiệm chúng trong game của mình, kiểm tra việc cải thiện hiệu suất từ nó cũng như chất lượng hình ảnh có chấp nhận được không. Nếu được, hãy sử dụng chúng, ứng dụng của bạn sẽ được cải thiện hiệu năng đáng kể. Tuy những shader này được sử dụng hướng đến nền tảng di động, tuy nhiên chúng vẫn phù hợp cho bất kỳ nền tảng nào. Do vậy bạn có thể hoàn toàn sử dụng những shader này trong các dự án hướng tới nền tảng khác.
    topic5_1
  • Một shader có thể dùng chung cho nhiều material và nó phụ thuộc vào material. Nếu các object trong game của bạn sử dụng Unity Standard Shader, có một điều quan trọng bạn phải nhớ rằng Unity sẽ biên dịch (compile) shader code dựa trên những cài đặt của material được sử dụng. Chỉ những chức năng nào đang được sử dụng trong material mới được biên dịch. Điều này có nghĩa là việc loại bỏ các chức năng không cần thiết trong từng object sẽ làm giảm độ phức tạp của fragment shader đi rất nhiều, dẫn đến hiệu năng sẽ được cải thiện đáng kể (Bạn hãy thử tắt Detail map hay normal map xem thử xD). Một lần nữa, mình xin nhắc lại, công việc bạn cần làm là thử nghiệm các phương pháp, kiểm tra hiệu quả của hiệu suất và sự ảnh hưởng của việc tối ưu đến chất lượng ứng dụng của bạn, để quyết định xem có nên sử dụng các phương pháp này hay không.
  • Ngoài ra, bạn cũng nên tránh sử dụng những hàm tính toán phức tạp trong shader code (như pow, exp,log.cos,tan,…) và tránh sử dụng alpha-testing shaders. Thay vào đó, bạn có thể sử dụng alpha-blend shader.

OverDraw là một thuật ngữ được định nghĩa khi các pixel được vẽ nhiều lần. Điều này xảy ra khi các object được vẽ đè lên các object khác, do đó tạo nên các vấn đề về fillrate. Để hiểu được vấn đề về OverDraw, trước hết bạn phải hiểu được thứ tự mà Unity vẽ các object trong Scene. Shader code trong object sẽ xác định thứ tự vẽ của nó, thường được xác định tại thành phần Render Queue trong shader. Ngoài ra, các object trong các render queue khác nhau sẽ được sắp xếp khác nhau. Ví dụ, Unity sẽ sắp xếp các object từ trước ra sau trong Geometry queue để giảm thiểu hết mức OverDraw, nhưng sẽ sắp xếp từ sau ra trước trong Transparent queue để đạt được các hiệu ứng thị giác. OverDraw là một chủ đề phức tạp, tuy nhiên việc giảm thiểu số object chồng lên nhau mà Unity không thể tự động sắp xếp là chìa khoá để giải quyết vấn đề.
topic5_2
Cách render của các Render Queue:

  • Background: Render queue này sẽ được render đầu tiên trong tất cả các trường hợp. Bạn nên sử dụng queue này cho các object đóng vai trò như background.
  • Geometry: Đây là Render Queue mặc định, được sử dụng cho hầu hết object.
  • AlphaTest: Đây là một queue riêng biệt, rất hữu ích để render các alpha-test object.
  • Transparent: render queue này sẽ được render sau Geometry và AlphaTest với thứ tự từ sau ra trước. Thường được sử dụng cho các vật thể trong suốt như kính hay các hiệu ứng particle.
  • Overlay: được sử dụng cho các hiệu ứng overlay như lens flare.

Để kiểm tra OverDraw, bạn có thể mở OverDraw Draw Mode trong cửa sổ Scene. Draw Mode view cho phép bạn nhìn thấy OverDraw ở trong Scene, và từ đó xác định chỗ nào chúng ta cần tối ưu. Thủ phạm phổ biến nhất của OverDraw là Transparent material, các hạt particles chưa được tối ưu và các thành phần UI chồng lên nhau. Riêng về UI mình sẽ viết một bài hướng dẫn tối ưu UI trong các phần sau của Series.
topic5_3
Nếu bạn đang sử dụng Unity 3D, chắc hẳn các bạn đã nghe đến khái niệm Image Effect. Chúng đơn giản là những script, được gắn vào trong Main Camera và tạo ra các hiệu ứng rất ấn tượng. Ví dụ như bloom effect có thể đưa ánh sáng khắc nghiệt vào một ngày nắng ấm, hay một effect như color correction có thể tạo hiệu ứng cảnh quan như độc (toxic). Do vậy, việc sử dụng Image Effect có thể là một phần nguyên nhân gây nên vấn đề về Fill rate, đặc biệt khi sử dụng nhiều hơn một Image Effect trong Scene. Nếu Image Effect là nguyên nhân, chúng ta có thể thử nghiệm các thiết lập khác nhau hoặc sử dụng các phiên bản tối ưu hoá của các Image Effect (Ví dụ như sử dụng Bloom Optimized thay vì Bloom). Nếu game của bạn có nhiều hơn một Image Effect ở trong cùng 1 camera, điều này sẽ tạo ra nhiều shader pass. Trong trường hợp này, bạn có thể kết hợp shader code của các Image Effect đó vào trong một pass riêng (Bạn có thể tham khảo phương pháp này tại Unity’s PostProcessing Stack).
image-effects-folder
Sau khi thực hiện những phương pháp này mà vẫn chưa giải quyết được vấn đề, bạn có thể nên xem xét việc loại bỏ những Image Effect này, đặc biệt là trong các thiết bị cấu hình yếu.
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 GPU 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 2 vấn đề còn lại là Memory bandwidth và vertex processing.

[Unity Series 6] Tối ưu hiệu năng Ứng dụng game Unity - Tối ưu GPU (tiếp)