Tối ưu hóa các thay đổi trạng thái trong hàng đợi kết xuất


7

Tôi đang cấu trúc lại một số phần của công cụ trò chơi mà tôi đang làm việc. Công cụ này được thực hiện trong C # với XNA.

Phần tôi gặp khó khăn là shader / liệu và hàng đợi kết xuất. Trong phiên bản mới, tôi sẽ cung cấp khả năng chỉ ra các trạng thái hiển thị khác nhau trên mỗi lần vượt qua trong một kỹ thuật (trạng thái pha trộn, trạng thái rasterizer, ...). Trong công cụ của tôi, tất cả các kết xuất đều có một vật liệu và mỗi vật liệu được liên kết với một kỹ thuật (do đó với một hoặc nhiều đường chuyền).

Sau này, khi trò chơi sẽ chạy, tôi sẽ thêm các đối tượng có thể kết xuất vào hàng đợi kết xuất và sau đó sắp xếp nó dựa trên đường chuyền được sử dụng bởi mỗi vật liệu. Những gì tôi muốn đạt được là giảm các thay đổi trạng thái giữa các lệnh gọi bằng cách nhóm đầu tiên cùng một lượt và sau đó chuyển với các trạng thái gần giống nhau để đóng càng tốt để giảm thiểu thay đổi. Nhưng giải pháp này dường như có vấn đề khi sử dụng kỹ thuật với nhiều hơn một lần vượt qua với các trạng thái hoàn toàn khác nhau. Các đường chuyền trong cùng một kỹ thuật có thể được tách biệt hoàn toàn trong hàng đợi kết xuất và không được kết xuất lần lượt. Nếu tôi nói rằng đó là vì tôi luôn thấy kỹ thuật nhiều đường chuyền được kết xuất trong một vòng lặp và tất cả các đường chuyền được kết xuất ngay lập tức như thế:

foreach(EffectPass pass in technique.Passes)
{
    /* Update parameters */

    pass.apply();
    graphicsDevice.Draw(...);
}

Nhưng tôi không biết liệu tôi có thể trì hoãn vượt qua kết xuất từ ​​cùng một kỹ thuật thay vì sử dụng lần lượt từng cái như trong ví dụ trước không.

Dưới đây là một ví dụ về những gì có thể sẽ xảy ra với giải pháp tôi cố gắng xây dựng:

  • Shader1
    • Kỹ thuật
      • Pass1 (Bang A)
      • Pass2 (bang C)
  • Shader2
    • Kỹ thuật
      • Pass1 (Bang A)
  • Shader3
    • Kỹ thuật
      • Pass1 (Bang A)

Bây giờ tôi có ba mô hình để vẽ, Model1, Model2 và Model3, mỗi mô hình sử dụng một kỹ thuật khác nhau từ danh sách trước đó. Nếu tôi thêm ba mô hình đó vào hàng đợi kết xuất, tôi sẽ kết thúc với hàng đợi kết xuất sau:

-> Đặt trạng thái A -> Vẽ Model1 (Pass1) -> Vẽ Model2 (Pass1) -> Vẽ Model3 (Pass1) -> Đặt trạng thái C -> Vẽ Model1 (Pass2)

Chúng ta có thể thấy rằng Model1 sẽ được rút ra hai lần (hai lần cho Kỹ thuật A) nhưng cuộc gọi rút thăm được phân tách bằng cuộc gọi rút thăm khác.

Tôi không biết làm thế nào để giải quyết điều này. Điều gì sẽ là một giải pháp tốt cho vấn đề này? Làm cách nào tôi có thể thiết kế hàng đợi kết xuất của mình để giảm các thay đổi trạng thái nhiều nhất có thể giữa các lệnh gọi.

BIÊN TẬP:

Sau khi tìm kiếm thêm một chút, tôi đã tìm thấy bài viết hữu ích này: http://realtimecollisiondetection.net/blog/?p=86 . Nó dường như là một cách thanh lịch để sắp xếp đối tượng theo độ sâu và vật liệu. Và trong id vật liệu, tôi có thể có một id pass để sắp xếp cùng một pass sẽ cho phép giảm các thay đổi trạng thái.

Hiện tại trình kết xuất của tôi vẽ các đối tượng trực tiếp trong backbuffer mà không có đèn cũng không có bóng, mọi thứ đều phẳng. Vì vậy, tôi sẽ chỉ lặp qua hàng đợi kết xuất của mình và gọi các phương thức vẽ. Nhưng trong tương lai tôi có thể sẽ thực hiện kết xuất hoãn lại.

Tôi không biết nhiều về nó nhưng tôi biết rằng tôi phải kết xuất các đối tượng của mình trong các mục tiêu kết xuất khác nhau để thu thập thông tin khác nhau (Bộ đệm Z, Bộ đệm thông thường, Bộ đệm G, ...). Tôi giả sử rằng đối với bộ đệm z và bộ đệm thông thường, tôi sẽ sử dụng cùng một shader trên mọi đối tượng cho dù chúng sử dụng vật liệu nào. Nhưng đối với bộ đệm G, tôi sẽ sử dụng các vật liệu của từng đối tượng để điều đó có nghĩa là các shader khác nhau với số lần chuyền khác nhau. Nếu tôi sử dụng giải pháp trong liên kết trước, nếu tôi có một đối tượng có vật liệu cần thực hiện hai lần trở lên, hàng đợi kết xuất của tôi sẽ có ít nhất hai lần đối tượng (một cho mỗi lần vượt qua). Nhưng đối với bộ đệm Z hoặc bộ đệm thông thường không phải là vô dụng khi có cùng một đối tượng nhiều lần?

Những gì tôi thấy là tôi sẽ cần phải có ít nhất hai hàng đợi kết xuất khác nhau: một cho bộ đệm G trong đó một đối tượng có thể nhiều lần tùy thuộc vào số lượng vật liệu yêu cầu và một hàng đợi kết xuất khác cho bộ đệm Z hoặc bộ đệm khác trong đó mỗi đối tượng chỉ một lần. Tôi có đúng không

Câu trả lời:


4

Thứ tự vẽ thường được ngụ ý bởi các hướng dẫn, nơi bạn làm một cái gì đó như thế này:

for each object:
    for each pass:
        apply pass state
        draw object

thực sự là ngược với cách nó có ý nghĩa để làm điều đó trong bối cảnh "trò chơi thực sự". Thay vào đó, bạn sẽ có nhiều khả năng làm một cái gì đó như:

for each pass:
    apply pass state
    for each object (grouped by shader/material/mesh/etc):
        draw object

Nhiều đối tượng vượt qua gần như không bao giờ là những gì bạn thực sự muốn. Thay vào đó, bạn muốn tích lũy tất cả các đối tượng sử dụng một đường chuyền nhất định, và sau đó vẽ tất cả các đối tượng đó lại với nhau.

Ví dụ: một số đường chuyền phổ biến bạn gặp phải là:

  • Độ sâu trước vượt qua
  • Bản đồ bóng
  • Trì hoãn bóng chuyền
  • Decal qua
  • Đèn chiếu sáng
  • Chuyển tiếp bóng râm
  • Vượt qua
  • Vượt qua hậu xử lý
  • Giao diện người dùng

Không phải mọi shader sẽ bao gồm tất cả một trong những đường chuyền này - ví dụ, trong trình kết xuất bị trì hoãn, một shader "đối tượng mờ" điển hình chỉ có thể sử dụng ba đường chuyền đầu tiên, một bóng đổ ánh sáng sẽ chỉ sử dụng đường chuyền chiếu sáng, v.v.

Trong những trường hợp như thế này, tự nhiên bạn muốn vẽ tất cả các đối tượng ở độ sâu trước khi vượt qua cùng nhau, vì vậy bạn sẽ tạo một danh sách kết xuất bao gồm tất cả các đối tượng đó, sau đó áp dụng trạng thái vượt qua một lần và vẽ chúng lại với nhau. Sau đó, bạn áp dụng trạng thái vượt qua bản đồ bóng và vẽ tất cả các đối tượng đi trong bản đồ bóng, v.v.

Chỉ có một số trường hợp đặc biệt hiếm khi bạn thực sự muốn vượt qua từng đối tượng - ví dụ: tán xạ dưới bề mặt bằng khuếch tán không gian kết cấu, trong đó bạn vẽ ánh sáng của đối tượng vào mục tiêu kết xuất ngoài màn hình, làm mờ nó, sau đó vẽ đối tượng trên màn hình bằng cách làm mờ ánh sáng (3 lượt cho mỗi đối tượng). Loại điều này không có quy mô tốt - nó sẽ rất tốn kém khi bạn thêm các đối tượng - vì vậy các lập trình viên động cơ cố gắng tránh nó càng nhiều càng tốt.


Cảm ơn phản hồi của bạn, nó đã giúp tôi suy nghĩ về vấn đề của mình. Trong khi đó tôi đã tìm thấy một bài viết thú vị về một phương pháp để sắp xếp hàng đợi kết xuất. Tôi nghĩ về việc thực hiện nó nhưng tôi có nhiều câu hỏi hơn, vì vậy tôi đã chỉnh sửa câu hỏi của mình với nhiều chi tiết hơn và liên kết đến bài viết.
Julien Pires

@Takumi Cái mà tôi gọi là "pass" ở đây giống với cái được gọi là "layer" trong bài viết đó. Sẽ là khá bất thường khi một tài liệu có nhiều hơn một lần hoãn. Các đối tượng khác nhau sẽ có các vật liệu khác nhau, nhưng tất cả chúng sẽ được rút ra một lần trong đường chuyền hoãn lại. Và như tôi đã đề cập trong câu trả lời của mình, nhiều tài liệu sẽ được thực hiện trong một số lần nhưng không phải là tài liệu khác. Bạn vẫn có thể giữ mọi thứ trong một hàng đợi kết xuất chính miễn là nó được sắp xếp theo lượt.
Nathan Reed

1
@cubrman Nếu sử dụng khung hiệu ứng, IIRC bạn cần gọi Áp dụng mỗi khi bạn thay đổi giá trị tham số.
Nathan Reed

1
@cubrman Tôi sẽ không nói như vậy. Cập nhật các tham số shader là một trong những thay đổi trạng thái nhẹ nhất, vì vậy tôi sẽ không lo lắng về nó quá nhiều. Bạn vẫn có thể giảm thiểu các thay đổi trạng thái đắt tiền (chuyển shader, chuyển mục tiêu kết xuất) bằng cách nhóm các đối tượng của bạn bằng shader. Và bạn có thể tránh chuyển đổi kết cấu, bộ đệm đỉnh, v.v. (đắt tiền trung bình) bằng cách nhóm các đối tượng của bạn hơn nữa bằng vật liệu và lưới, trong nhóm được tạo bởi shader.
Nathan Reed

1
@cubrman Vâng, thay đổi kỹ thuật hoặc chuyển trong tệp hiệu ứng sẽ thay đổi trình đổ bóng. Bất cứ khi nào bạn chuyển sang một chương trình đổ bóng đỉnh hoặc pixel riêng lẻ khác nhau, đó là thay đổi shader.
Nathan Reed
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.