Mẹo tối ưu hoá CPU và GPU

Tài liệu này hướng dẫn bạn cách tối ưu hoá hiệu suất trò chơi bằng cách sử dụng các công cụ để xác định và giải quyết các điểm tắc nghẽn về CPU và GPU.

Tối ưu hoá CPU

Nếu phân tích cho thấy trò chơi bị giới hạn bởi CPU, bạn cần phải điều tra thêm. Điều này đòi hỏi bạn phải xác định các luồng hoặc API cụ thể gây ra tình trạng tắc nghẽn và giảm tốc độ khung hình.

Đối với việc tối ưu hoá CPU, một giải pháp chung thường không hiệu quả. Thay vào đó, bạn phải xác định khối lượng công việc đòi hỏi nhiều tài nguyên nhất dựa trên trò chơi hoặc cảnh, sau đó tối ưu hoá logic và các hàm có liên quan.

Công cụ theo dõi thời gian của công cụ phát triển trò chơi

Các công cụ sau đây có thể hỗ trợ bạn phân tích:

Thông tin chi tiết về Unreal

Trong các dự án Unreal Engine, Unreal Insight Tool hỗ trợ việc phân tích thông tin dấu vết thời gian cho từng luồng tạo nên một khung hình.

Để minh hoạ, GameThread thường sử dụng tỷ lệ lớn nhất về thời gian CPU, chủ yếu là do Tick Time. Hơn nữa, một phần đáng kể của Tick Time được dùng cho các tác vụ liên kết với FActorComponentTickFunction.

Để tối ưu hoá FActorComponentTick, bạn bắt buộc phải loại trừ các phép tính và triển khai tính năng loại bỏ cho các nhân vật và đối tượng nằm ngoài trường nhìn của camera. Ngoài ra, việc tận dụng ảnh động dựa trên LOD (Mức độ chi tiết) có thể giúp nâng cao hiệu suất hơn nữa.

Dòng thời gian theo dõi Unreal Insight cho biết thời gian thực thi GameThread, RenderThread và RHIThread
Dấu vết thông tin chi tiết của Unreal với GameThread, RenderThread và RHIThread (nhấp để phóng to).

Trình phân tích tài nguyên Unity (Unity)

Phân tích bằng Unity Profiler cho thấy Luồng chính tiêu thụ hơn 45 mili giây, trong đó PostLateUpdate.FinishFrameRendering chiếm 16,23 mili giây, khiến đây trở thành thao tác tốn nhiều thời gian nhất. Trong đó, có nhiều lệnh gọi Inl_RenderCameraStack. Bạn nên xác định sự cần thiết của các camera đã bật và tối ưu hoá chúng cho phù hợp.

Dòng thời gian của Unity Profiler cho thấy Luồng chính đang chờ Gfx.WaitForPresentOnGfxThread
Ví dụ về giới hạn GPU cho Trình phân tích tài nguyên Unity (nhấp để phóng to).

Công cụ lập hồ sơ ở cấp hệ thống

Sử dụng các công cụ phân tích tài nguyên sau đây:

Perfetto

Bằng cách sử dụng dấu vết Perfetto, bạn có thể xác định việc chỉ định lõi CPU và thông tin chi tiết về quá trình thực thi của từng luồng trên một thiết bị chạy Android. Điều này giúp bạn xác định các điểm tắc nghẽn về hiệu suất bằng cách phân tích dữ liệu thực thi luồng.

Trường hợp hao tổn CPU

Dấu vết cho thấy khối lượng công việc trên GameThread và RenderThread đang gây ra sự chậm trễ trong QueuePresent của RHI Thread, dẫn đến một tình huống bị giới hạn bởi CPU, dựa trên VSync.

Dấu vết Perfetto cho thấy thời gian thực thi cho GameThread, RenderThread và RHIThread
Dấu vết Perfetto có thông tin chi tiết về quá trình thực thi CPU (nhấp để phóng to).

Trường hợp có chi phí GPU

Dấu vết cho thấy bản thân quá trình hoàn tất GPU vượt quá 25 mili giây, cho thấy một tình huống bị giới hạn bởi GPU.

Dấu vết Perfetto cho thấy khối hoàn tất GPU đang chờ GPU hoàn tất
Dấu vết Perfetto có thông tin chi tiết về mức hao tổn GPU (nhấp để phóng to).

Simpleperf

Để xác định các hàm có mức sử dụng CPU hiện tại cao nhất, bạn có thể sử dụng simpleperf. Để đạt được kết quả tối ưu, bạn nên sắp xếp các chức năng này để ưu tiên và giải quyết những chức năng có mức sử dụng cao nhất trước.

Đầu ra của Simpleperf cho thấy các hàm có mức sử dụng CPU cao nhất
Phân tích tài nguyên CPU bằng Simpleperf: Phân tích hệ thống phân cấp lệnh gọi hàm và mức sử dụng tài nguyên (nhấp để phóng to).

Simpleperf giúp bạn kiểm tra dữ liệu về các hàm sử dụng nhiều thời gian CPU nhất. Để tối ưu hoá mức sử dụng CPU, hãy bắt đầu với những hàm sử dụng nhiều CPU nhất. Trong ví dụ này, USkeletalMeshComponent (được liên kết với ảnh động trong ActorComponentTickFunctions) sử dụng nhiều CPU nhất.

Tối ưu hoá GPU

Nếu kết quả phân tích cho thấy trò chơi bị ràng buộc bởi GPU, thì bạn cần phải điều tra thêm. Điều này đòi hỏi bạn phải sử dụng nhiều công cụ và kỹ thuật để tối ưu hoá và phân tích GPU.

Để tối ưu hoá GPU, hãy sử dụng một trình gỡ lỗi khung hình để phân tích quy trình kết xuất và các lệnh gọi vẽ cho từng cảnh. Ngoài ra, bạn phải hiểu rõ cấu trúc GPU và hành vi của quy trình để xác định các thao tác không cần thiết hoặc các khu vực cần tối ưu hoá.

Các phần sau đây giải thích các phương pháp và công cụ để tối ưu hoá GPU.

Loại bỏ RenderPasses không cần thiết

Để cải thiện hiệu suất kết xuất và giảm tải cho GPU, hãy loại bỏ các lượt kết xuất không cần thiết. Những thành phần này bao gồm mọi lượt kết xuất thiếu lệnh vẽ hoặc đầu ra không được dùng trong khung hình cuối cùng.

Sử dụng một trình gỡ lỗi GPU, chẳng hạn như RenderDoc, để phân tích quy trình kết xuất và xác định các cơ hội tối ưu hoá.

  1. Không có lệnh gọi vẽ: Kiểm tra xem đường truyền kết xuất có bao gồm lệnh gọi vẽ nào không. Nếu không có lệnh gọi vẽ, hãy xoá đường chuyền.

  2. Đầu ra không dùng đến: Kiểm tra xem các lượt truyền tiếp theo có truy cập hoặc hiển thị đầu ra của lượt kết xuất hay không, ví dụ: màu sắc hoặc độ sâu. Nếu không, hãy xoá thẻ và vé.

  3. Thẻ và vé có thể hợp nhất: Xác định những thẻ và vé mà bạn có thể hợp nhất:

    • Cùng một vùng đệm khung hoặc tệp đính kèm
    • Các thao tác tải hoặc lưu trữ tương thích
    • Không có rào cản phụ thuộc nào ở giữa
Trình duyệt sự kiện RenderDoc hiển thị các lệnh gọi kết xuất và vẽ Vulkan
RenderPass và chuỗi lệnh GPU trong RenderDoc (nhấp để phóng to).

Giảm thiểu hoạt động tải hoặc lưu trữ

Các thao tác tải hoặc lưu tốn nhiều tài nguyên vì chúng sử dụng nhiều bộ nhớ. Giảm thiểu các thao tác tải và lưu trữ không cần thiết. Chỉ thực hiện các thao tác này khi cần có tệp đính kèm trong RenderPass. Nếu không, hãy thay thế các thao tác này bằng Clear hoặc Don't care để giảm mức hao tổn.

Cách tối ưu hoá

Sử dụng một trình gỡ lỗi GPU, chẳng hạn như RenderDoc, để phân tích quy trình kết xuất và xác định các cơ hội tối ưu hoá sau đây:

  1. Tải: Nếu một tệp đính kèm của lượt kết xuất không sử dụng dữ liệu từ một lượt hoặc tệp đính kèm trước đó, thì bạn không cần thực hiện thao tác tải. Trong những trường hợp như vậy, việc sử dụng Don't care hoặc Clear có thể giảm mức hao tổn.

  2. Lưu trữ: Nếu một tệp đính kèm đường kết xuất không được dùng sau đường kết xuất hiện tại, thì thao tác lưu trữ là không cần thiết. Trong những trường hợp như vậy, hãy sử dụng Don't care hoặc Clear.

  3. Thay thế: Xác định xem chế độ cài đặt tải hoặc lưu trữ hiện tại có thể được thay thế bằng Clear hoặc Don't Care mà không ảnh hưởng đến khung hình cuối cùng hay không.

Trình duyệt sự kiện và trình kiểm tra tài nguyên RenderDoc phân tích bố cục hình ảnh và các lượt kết xuất
Phân tích quy trình kết xuất RenderDoc (nhấp để phóng to).

Tránh loại bỏ để bật Early-Z

Early-Z giúp cải thiện hiệu suất trên các nền tảng di động. Tuy nhiên, một chỉ dẫn discard trong chương trình đổ bóng sẽ tự động tắt Early-Z. Nếu discardhướng dẫn này không cần thiết, hãy xoá hướng dẫn đó.

Gia tốc Early-Z

Phương pháp tối ưu hoá này giúp giảm đáng kể các thao tác của chương trình đổ bóng theo mảnh và cải thiện hiệu suất của GPU.

Early-Z Kiểm thử độ sâu và khuôn tô

Bảng so sánh các chỉ số hiệu suất CPU và GPU khi bật và tắt Early-Z
Tác động đến hiệu suất của tính năng tăng tốc Early-Z (nhấp để phóng to).

Cách tối ưu hoá

Sử dụng một trình gỡ lỗi GPU, chẳng hạn như RenderDoc, để phân tích quy trình kết xuất và xác định các cơ hội tối ưu hoá sau đây:

  1. Sử dụng discard trong chương trình đổ bóng mảnh: Từ khoá discard ngăn GPU thực hiện các kiểm thử độ sâu ban đầu vì không biết trước khả năng hiển thị của mảnh.

  2. Sửa đổi gl_FragDepth: Việc sửa đổi gl_FragDepth một cách linh động sẽ làm thay đổi độ sâu của một fragment, điều này sẽ vô hiệu hoá tính năng tối ưu hoá Early-Z vì độ sâu cuối cùng không xác định được trước khi xử lý fragment.

  3. Đã bật tính năng chuyển đổi từ alpha sang độ phủ: Khi tính năng chuyển đổi từ alpha sang độ phủ được bật (thường dùng trong quá trình kết xuất MSAA), độ phủ của mảnh phụ thuộc vào giá trị alpha. Điều này có thể làm chậm quá trình kiểm thử độ sâu và vô hiệu hoá Early-Z.

So sánh các mảnh trên mỗi pixel khi có và không có từ khoá discard shader
Trình gỡ lỗi GPU RenderDoc để phân tích (nhấp để phóng to).

Tối ưu hoá định dạng hoạ tiết

Việc chọn định dạng hoạ tiết tối ưu giúp giảm mức tiêu thụ bộ nhớ, nâng cao hiệu quả băng thông và cải thiện hiệu suất kết xuất. Việc sử dụng các định dạng có độ chính xác quá cao có thể lãng phí tài nguyên GPU mà không mang lại lợi thế về hình ảnh.

Cách tối ưu hoá

Sử dụng một trình gỡ lỗi GPU, chẳng hạn như RenderDoc, để phân tích quy trình kết xuất và xác định các cơ hội tối ưu hoá sau đây:

  1. Sử dụng D24S8 thay vì D32S8 cho vùng đệm độ sâu-khuôn tô: Việc sử dụng D24S8 cho vùng đệm độ sâu-khuôn tô giúp giảm mức tiêu thụ bộ nhớ xuống 20% so với D32S8, với rất ít hoặc không có sự khác biệt đáng chú ý về chất lượng hình ảnh trên hầu hết các ứng dụng.
  2. Sử dụng chế độ nén ASTC cho hoạ tiết màu: Chế độ nén ASTC giúp giảm đáng kể mức sử dụng bộ nhớ hoạ tiết (giảm đến 8 lần so với các định dạng không nén) mà vẫn duy trì chất lượng hình ảnh cao.
  3. Sử dụng định dạng bán chính xác thay vì định dạng chính xác: Sử dụng R16F hoặc RG16F để giảm mức tiêu thụ băng thông bộ nhớ và bộ nhớ. Những định dạng này rất phù hợp với các vùng đệm xử lý hậu kỳ.

Tối ưu hoá độ phức tạp của hình học

Việc giảm thiểu độ phức tạp về hình học giúp cải thiện hiệu suất kết xuất, đặc biệt là trên các thiết bị di động có khả năng GPU hạn chế. Việc này bao gồm sử dụng số lượng đỉnh và tam giác ít hơn, hợp nhất các đối tượng để giảm số lượng lệnh gọi vẽ và loại bỏ hình học không cần thiết hoặc chưa được kết xuất. Các kỹ thuật như đơn giản hoá lưới, Cấp độ chi tiết (LOD) và loại bỏ phần bị che khuất hoặc bị cắt có thể giảm đáng kể khối lượng công việc của GPU và tăng tốc độ khung hình.

Cách tối ưu hoá

Sử dụng các công cụ phân tích tài nguyên và trình gỡ lỗi GPU (chẳng hạn như RenderDoc, Android GPU Inspector hoặc các trình phân tích hiệu suất khác) để xác định các điểm tắc nghẽn hiệu suất liên quan đến hình học.

  1. Giảm số lượng tam giác: Giảm thiểu việc sử dụng đa giác, đặc biệt là đối với các đối tượng nhỏ hoặc ở xa.

  2. Sử dụng Mức độ chi tiết (LOD): Dựa trên khoảng cách của camera, các lưới đơn giản hơn sẽ được sử dụng tự động.

  3. Hợp nhất các lưới nhỏ: Hợp nhất các đối tượng tĩnh để giảm số lượng lệnh gọi vẽ và mức hao tổn CPU.

  4. Loại bỏ phần bị che khuất và hình nón cụt: Tránh kết xuất các đối tượng nằm ngoài khung hiển thị hoặc bị che khuất bởi các phần tử khác.

Xoá các tệp đính kèm không cần thiết

Các tệp đính kèm của lượt kết xuất (ví dụ: màu sắc, chiều sâu, khuôn tô) tiêu tốn băng thông bộ nhớ và tài nguyên GPU, ngay cả khi chúng không được sử dụng. Việc xoá các tệp đính kèm không cần thiết hoặc dư thừa sẽ cải thiện hiệu suất và giảm mức tiêu thụ điện năng, đặc biệt là trên các nền tảng di động.

Cách tối ưu hoá

Sử dụng các công cụ lập hồ sơ và trình gỡ lỗi GPU, chẳng hạn như RenderDoc, Android GPU Inspector hoặc các trình phân tích hiệu suất khác để xác định các điểm tắc nghẽn hiệu suất liên quan đến hình học.

  1. Kiểm tra mức sử dụng thực tế: Có lệnh gọi vẽ hoặc chương trình đổ bóng nào đang ghi vào hoặc đọc từ tệp đính kèm không?
  2. Phân tích đầu ra khung hình: Sử dụng RenderDoc hoặc các tiện ích tương tự để xác định xem tệp đính kèm có đóng góp vào hình ảnh cuối cùng hay không.
  3. Cân nhắc các tệp đính kèm tạm thời hoặc giả: Bạn nên sử dụng các tệp đính kèm tạm thời hoặc thao tác lưu trữ "Không quan tâm" cho dữ liệu tạm thời không yêu cầu bộ nhớ liên tục.

Tối ưu hoá độ chính xác của chương trình đổ bóng

Việc sử dụng độ chính xác quá cao (ví dụ: highp thay vì mediump hoặc lowp) trong chương trình đổ bóng sẽ làm tăng khối lượng công việc của GPU, mức tiêu thụ điện năng và áp lực đăng ký, đặc biệt là trên GPU di động. Bằng cách sử dụng độ chính xác thấp nhất phù hợp cho các biến (ví dụ: vị trí, màu sắc, UV), bạn có thể cải thiện hiệu suất mà không ảnh hưởng đáng kể đến hình ảnh.

Bảng so sánh các chỉ số hiệu suất CPU và GPU khi sử dụng độ chính xác của chương trình đổ bóng mediump so với highp
Ảnh hưởng đến hiệu suất của độ chính xác của chương trình đổ bóng (nhấp để phóng to).

Cách tối ưu hoá

Sử dụng các công cụ phân tích tài nguyên và trình gỡ lỗi GPU như RenderDoc, Android GPU Inspector hoặc các trình phân tích hiệu suất khác để xác định các điểm tắc nghẽn về hiệu suất liên quan đến hình học.

  1. Xem xét mã chương trình đổ bóng: Đánh giá các biến chương trình đổ bóng và xác nhận rằng độ chính xác cao chỉ được dùng khi cần thiết, chẳng hạn như để tính toán độ sâu hoặc không gian màn hình. Sử dụng độ chính xác trung bình hoặc thấp cho màu sắc, toạ độ UV hoặc các giá trị không yêu cầu độ chính xác cao.

  2. Sử dụng trình gỡ lỗi GPU: Các tiện ích chẩn đoán, chẳng hạn như RenderDoc hoặc trình phân tích GPU di động (ví dụ: AGI, Mali/GPU Inspector), xác định mức sử dụng thanh ghi tăng lên hoặc tình trạng tắc nghẽn của chương trình đổ bóng liên quan đến các vấn đề về độ chính xác.

Trình phân tích tài nguyên sử dụng đa dạng Mali hiển thị phép nội suy 16 bit cùng với mã chương trình đổ bóng bằng mediump
Ví dụ về các công cụ lập hồ sơ và trình gỡ lỗi GPU (nhấp để phóng to).

Bật tính năng loại bỏ mặt sau

Việc kết xuất các hình tam giác quay mặt ra xa camera (mặt sau) thường không cần thiết đối với các đối tượng rắn.

Cách tối ưu hoá

Việc sử dụng VK_CULL_MODE_NONE có thể ảnh hưởng tiêu cực đến hiệu suất vì nó buộc GPU hiển thị cả mặt trước và mặt sau, điều này làm tăng khối lượng công việc kết xuất.

Nhật ký lệnh Vulkan hiển thị vkCmdSetCullMode được đặt thành VK_CULL_MODE_NONE
Gỡ lỗi nhật ký bằng tính năng loại bỏ mặt sau (nhấp để phóng to).

Giảm tình trạng vẽ nhiều lần trong các cảnh giao diện người dùng

Loại bỏ các lệnh gọi vẽ và lượt kết xuất không cần thiết, đặc biệt là trong các cảnh giao diện người dùng, để nâng cao hiệu suất kết xuất và giảm khối lượng công việc của GPU. Ví dụ: trong một cảnh giao diện người dùng mà toàn bộ thế giới hiển thị trước khi lớp phủ giao diện người dùng xuất hiện trên màn hình, việc kết xuất thế giới sẽ trở nên dư thừa.

Cách tối ưu hoá

Sử dụng một trình gỡ lỗi GPU, chẳng hạn như RenderDoc, để phân tích quy trình kết xuất và xác định các cơ hội tối ưu hoá sau đây:

  1. Xác minh việc không có tình trạng vẽ nhiều lần không cần thiết. Trong ngữ cảnh giao diện người dùng, khi toàn bộ màn hình có thể kết xuất, hãy xác nhận rằng các lượt kết xuất trước đó không bị vẽ chồng lên một cách không cần thiết.
  2. Bật tính năng kiểm thử độ sâu và loại bỏ để tối ưu hoá hiệu suất.
  3. Cân nhắc việc kết xuất theo thứ tự từ trước ra sau.
Trình duyệt sự kiện và trình xem kết cấu RenderDoc xác định một lượt kết xuất vẽ chồng không cần thiết
Ví dụ về cách loại bỏ các lệnh gọi vẽ và lượt kết xuất dư thừa (nhấp để phóng to).