Cải Tiến Giao Diện Hệ Thống Hiệu Ứng Đặc Biệt - nói dối e blog

Cải Tiến Giao Diện Hệ Thống Hiệu Ứng Đặc Biệt

Trong lần cập nhật trước, chúng tôi đã phân tích dữ liệu và nhận thấy hệ thống hiệu ứng trong engine (game) hiện tại chiếm tỷ lệ CPU rất cao. Mặc dù các phép tính hiệu ứng đã được chuyển sang luồng riêng biệt không ảnh hưởng đến khung hình, nhưng mức tiêu hao CPU vẫn dẫn đến việc pin nhanh cạn và gây nóng máy, từ đó làm giảm hiệu năng xử lý của thiết bị. Vì vậy, chúng tôi đã quyết định áp dụng phương án tối ưu hóa bằng cách hy sinh một phần độ chính xác: chỉ tính toán các hiệu ứng nằm trong khung hình, đồng thời loại bỏ việc xử lý các hạt hiệu ứng ở vùng ngoài tầm nhìn của camera.

Trong đợt cải tiến gần đây, chúng tôi đã tiến hành重构 (cải tạo lại) hệ thống này. Tuy nhiên, một trong những thách thức lớn nhất nằm ở chỗ: module hiệu ứng đặc biệt là thư viện bên thứ ba, không hoàn toàn phù hợp với thiết kế engine của chúng tôi. Trong ngắn hạn, chúng tôi không có kế hoạch viết lại hoặc cải biến sâu module này.

Điểm phức tạp nhất liên quan đến tính năng “Hitch” (treo móc) độc đáo của engine. Trong cảnh quan game, có thể có nhiều điểm Hitch cùng treo một cây con (subtree) giống hệt nhau. Điều này cho phép các đối tượng giống nhau được render nhiều lần với trạng thái hoàn toàn đồng nhất, chỉ khác biệt về vị trí do từng điểm Hitch xác định. Engine của chúng tôi đã áp dụng tối ưu hóa như rendering theo batch với kỹ thuật instance hóa cho các thành phần này.

Đối với các mô hình nội bộ engine hỗ trợ, việc batch rendering dựa trên Hitch rất thuận tiện. Dù chỉ tồn tại một cấu trúc dữ liệu duy nhất, nhưng có thể render nhiều lần mỗi khung hình. Tuy nhiên, khi các thành phần được treo móc Hitch chứa hiệu ứng đặc biệt, module hiệu ứng lại không hỗ trợ việc nhân bản và submit nhiều lần. Chúng tôi buộc phải tạo nhiều đối tượng hiệu ứng giống nhau trong module hiệu ứng, nhưng các đối tượng này lại có trạng thái độc lập. Việc giữ cho tất cả trạng thái luôn đồng nhất đã gây ra nhiều khó khăn trong triển khai.

Rất may là theo định nghĩa của engine, khi cần nhiều vật thể giống nhau cùng mang hiệu ứng đặc biệt, chúng hoàn toàn có thể được xem là đồng nhất. Chúng tôi không cần thiết lập mối liên hệ bền vững giữa các đối tượng hiệu ứng và vật thể hiển thị trên màn hình. Ví dụ: ở khung hình hiện tại có 4 đám khói trạng thái giống nhau, đến khung hình kế tiếp một đám bị loại khỏi tầm nhìn, một đám mới vào khung hình, ba đám còn lại di chuyển vị trí nhưng tổng số lượng không đổi. Chúng tôi không cần khôi phục trạng thái ba đám khói đã thay đổi vị trí từ khung hình trước. Engine chỉ cần xác định vị trí mới của bốn đám khói này.

Trạng thái nội tại của đối tượng hiệu ứng được cập nhật từng khung hình dựa trên giá trị delta. Module hiệu ứng không cung cấp phương thức clone đối tượng, nên để đảm bảo đồng nhất hoàn toàn, chúng tôi phải phát lại hiệu ứng từ đầu đến tiến độ hiện tại - một thao tác tốn nhiều tài nguyên. Sau khi cân nhắc, tôi quyết định hy sinh một phần độ chính xác: khi có hiệu ứng mới xuất hiện trong khung hình, nó có thể được phát lại từ đầu thay vì tiếp nối trạng thái trước đó. Tất nhiên, nếu không sử dụng Hitch để nhân bản hiệu ứng, trạng thái vẫn được giữ nguyên.

Hệ thống sau cải tiến sử dụng một hệ thống độc lập quản lý các hiệu ứng tồn tại trong cảnh. Mỗi hiệu ứng có thể có nhiều instance bên trong. Trong mỗi khung hình rendering, khi nút cảnh là hiệu ứng đặc biệt, giống như mô hình, nó cần submit ma trận hiện tại đến module tương ứng: mô hình đến module rendering mô hình, hiệu ứng đến hệ thống hiệu ứng. Nhiệm vụ của hệ thống hiệu ứng mỗi khung hình là thu thập số lần submit của từng hiệu ứng. Các hiệu ứng ngoài tầm nhìn sẽ bị loại khỏi rendering (và thậm chí không cần cập nhật trạng thái - đây là tối ưu hóa đặc biệt của game chúng tôi). Khi cùng một hiệu ứng bị Hitch tham chiếu nhiều lần, nó sẽ được submit với nhiều ma trận cảnh khác nhau. Lúc này, hệ thống hiệu ứng sẽ kích hoạt cơ chế cache nội bộ, tái sử dụng trạng thái hiệu ứng từ khung hình trước. Nếu số lượng hiệu ứng khung này nhiều hơn khung trước, hệ thống sẽ phát (clone) thêm hiệu ứng mới; nếu ít hơn, có thể xóa bớt hoặc tạm ẩn các đối tượng thừa.

Trong quá trình cải tiến, chúng tôi đã xem xét lại giao diện phiên bản trước: muốn phát hiệu ứng thì tạo Entity, khi hiệu ứng biến mất thì hủy nó. Việc hủy có thể chủ động hoặc tự động. Sau thời gian sử dụng, tôi nhận ra ngữ nghĩa giao diện này chưa phù hợp. Entity chúng tôi tạo ra nên là “máy phát hiệu ứng” chứ không phải hiệu ứng được phát ra. Máy phát không nên bị tạo/xóa thường xuyên. Khi không hoạt động, nó chỉ chiếm không gian mà không tiêu hao CPU. Chúng tôi chỉ nên kích hoạt máy phát khi cần thiết.

Dựa trên tư tưởng này, trong quá trình phát triển với editor, các prefab nên được thiết kế sẵn máy phát hiệu ứng. Một số khung hình cụ thể trong animation của prefab sẽ kích hoạt hiệu ứng: ví dụ, ống khói hoạt động sẽ phát khói - một hiệu ứng hạt. Ở phiên bản cũ, keyframe animation sẽ tạo Entity hiệu ứng khói; phiên bản mới sẽ thiết kế sẵn máy phát hiệu ứng khói trong prefab, và keyframe animation sẽ điều khiển máy phát này khi chạy.

Thông thường, chúng tôi không cần kiểm soát chi tiết hiệu ứng đã phát ở runtime. Tuy nhiên trong môi trường editor cần kiểm soát tỉ mỉ: tạm dừng, tăng tốc, giảm tốc, phát đến khung hình cụ thể… Máy phát trả về Handle của hiệu ứng đã phát, cung cấp giao diện bổ sung để điều khiển. Đây là cách triển khai từ thư viện hiệu ứng底层. Tuy nhiên với tầng ứng dụng, việc phân biệt máy phát và hiệu ứng dễ gây nhầm lẫn. Giải pháp hiện tại là tích hợp trực tiếp các giao diện điều khiển lên máy phát: khi muốn điều khiển hiệu ứng, chỉ cần thao tác trên máy phát, các lệnh sẽ được chuyển tiếp đến tất cả hiệu ứng do máy phát này tạo ra. Nếu máy phát tạo nhiều hiệu ứng tồn tại đồng thời, mọi thao tác sẽ áp dụng lên tất cả hiệu ứng đang tồn tại.

0%