Sử dụng hai shader thay vì một với các câu lệnh IF


9

Tôi đã làm việc về việc chuyển nguồn op 1.1 tương đối lớn sang ES 2.0.

Trong OpenGL ES 2.0 (có nghĩa là, mọi thứ đều sử dụng shader), tôi muốn vẽ một ấm trà ba lần.

  1. Cái đầu tiên, với màu đồng nhất (ala glColor4f cũ).

  2. Cái thứ hai, với màu trên mỗi đỉnh (ấm trà cũng có mảng màu của đỉnh)

  3. Cái thứ ba, với kết cấu trên mỗi đỉnh

  4. Và có thể là một thứ tư, với cả kết cấu và màu sắc trên mỗi đỉnh. Và sau đó có thể là một thứ 5, với quy tắc là tốt ..

Có hai sự lựa chọn mà tôi có khi thực hiện, theo như tôi biết. Đầu tiên là tạo một shader hỗ trợ tất cả các mục trên, với một bộ đồng phục được thiết lập để thay đổi hành vi (ví dụ: sử dụng đồng phục màu số ít hoặc đồng phục màu trên mỗi đỉnh).

Sự lựa chọn thứ hai là tạo ra một shader khác nhau cho mỗi tình huống. Với một số tiền xử lý đổ bóng tùy chỉnh, việc này không phức tạp lắm, nhưng điều đáng quan tâm là chi phí hiệu năng trong việc chuyển đổi các bóng đổ giữa các đối tượng vẽ. Tôi đã đọc rằng nó không phải là nhỏ.

Ý tôi là, cách tốt nhất để nói về vấn đề này là xây dựng cả hai và đo lường, nhưng thật tốt khi nghe bất kỳ đầu vào nào.

Câu trả lời:


10

Chi phí thực hiện của việc phân nhánh cũng không phải là nhỏ. Trong trường hợp của bạn, tất cả các đỉnh và các mảnh được vẽ sẽ đi theo cùng một đường đi qua các shader của bạn, vì vậy trên phần cứng máy tính để bàn hiện đại, nó sẽ không tệ như nó có thể, nhưng bạn đang sử dụng ES2, ngụ ý rằng bạn không sử dụng hiện đại phần cứng máy tính để bàn.

Trường hợp xấu nhất với sự phân nhánh sẽ diễn ra như thế này:

  • cả hai mặt của chi nhánh được đánh giá.
  • một lệnh "trộn" hoặc "bước" sẽ được trình biên dịch đổ bóng tạo ra và chèn vào mã của bạn để quyết định sử dụng bên nào.

Và tất cả các hướng dẫn bổ sung này sẽ được chạy cho từng đỉnh hoặc đoạn mà bạn vẽ. Đó có khả năng là hàng triệu hướng dẫn bổ sung được cân nhắc với chi phí thay đổi shader.

" Hướng dẫn lập trình OpenGL ES cho iOS " của Apple (có thể được coi là đại diện cho phần cứng mục tiêu của bạn) có ý nghĩa này để nói về việc phân nhánh:

Tránh phân nhánh

Các chi nhánh không được khuyến khích trong các shader, vì chúng có thể làm giảm khả năng thực hiện các hoạt động song song trên các bộ xử lý đồ họa 3D. Nếu các shader của bạn phải sử dụng các nhánh, hãy làm theo các khuyến nghị sau:

  • Hiệu suất tốt nhất: Chi nhánh trên một hằng số được biết khi trình đổ bóng được biên dịch.
  • Chấp nhận được: Chi nhánh trên một biến thống nhất.
  • Có khả năng chậm: Phân nhánh trên một giá trị được tính bên trong shader.

Thay vì tạo một shader lớn với nhiều núm và cần gạt, hãy tạo các shader nhỏ hơn chuyên dùng cho các tác vụ kết xuất cụ thể. Có một sự đánh đổi giữa việc giảm số lượng nhánh trong các shader của bạn và tăng số lượng shader bạn tạo ra. Kiểm tra các tùy chọn khác nhau và chọn giải pháp nhanh nhất.

Ngay cả khi bạn hài lòng rằng bạn đang ở vị trí "Có thể chấp nhận" ở đây, bạn vẫn cần xem xét rằng với 4 hoặc 5 trường hợp để chọn giữa, bạn sẽ tăng số lượng lệnh trong các shader của mình. Bạn nên lưu ý về các giới hạn số lượng hướng dẫn trên phần cứng mục tiêu của bạn và đảm bảo rằng bạn không vượt lên trên chúng, trích dẫn lại từ liên kết Apple ở trên:

Việc triển khai OpenGL ES không bắt buộc phải thực hiện dự phòng phần mềm khi vượt quá các giới hạn này; thay vào đó, shader đơn giản là không biên dịch hoặc liên kết.

Không có gì trong số này là để nói rằng phân nhánh không phải là giải pháp tốt nhất cho nhu cầu của bạn. Bạn đã xác định chính xác thực tế rằng bạn nên lập hồ sơ cả hai cách tiếp cận, vì vậy đó là khuyến nghị cuối cùng. Nhưng chỉ cần lưu ý rằng khi các shader trở nên phức tạp hơn, một giải pháp dựa trên nhánh có thể mang lại chi phí cao hơn nhiều so với một vài thay đổi của shader.


3

Chi phí cho các shader ràng buộc có thể không phải là nhỏ, nhưng nó sẽ không phải là nút cổ chai của bạn trừ khi bạn kết xuất hàng ngàn mục mà không gộp tất cả các đối tượng sử dụng cùng các shader.

Mặc dù tôi không chắc điều này có áp dụng cho thiết bị di động hay không, nhưng GPU không bị chậm một cách khủng khiếp với các nhánh nếu điều kiện nằm giữa hằng số và đồng phục. Cả hai đều hợp lệ, cả hai đã được sử dụng trong quá khứ và sẽ tiếp tục được sử dụng trong tương lai, chọn bất kỳ cái nào bạn nghĩ sẽ sạch hơn trong trường hợp của bạn.

Ngoài ra, có một vài cách khác để thực hiện điều này: "Trình tạo bóng Uber" và một mẹo nhỏ với cách các chương trình trình tạo bóng OpenGL được liên kết.

"Uber-shader" về cơ bản là lựa chọn đầu tiên, trừ khi phân nhánh, nhưng bạn sẽ có nhiều shader. Thay vì sử dụng ifcác báo cáo, bạn sử dụng tiền xử lý - #define, #ifdef, #else, #endif, và biên dịch các phiên bản khác nhau, trong đó có thích hợp #defines cho những gì bạn cần.

vec4 color;
#ifdef PER_VERTEX_COLOR
color = in_color;
#else
color = obj_color;
#endif

Bạn cũng có thể chia shader thành các chức năng riêng biệt. Có một shader xác định nguyên mẫu cho tất cả các hàm và gọi chúng, liên kết một loạt các shader bổ sung bao gồm các cài đặt thích hợp. Tôi đã sử dụng thủ thuật này để lập bản đồ bóng, để giúp dễ dàng trao đổi cách lọc được thực hiện trên tất cả các đối tượng mà không phải sửa đổi tất cả các trình đổ bóng.

//ins, outs, uniforms

float getShadowCoefficient();

void main()
{
    //shading stuff goes here

    gl_FragColor = color * getShadowCoefficient();
}

Sau đó, tôi có thể có nhiều tệp shader khác xác định getShadowCoefficient(), đồng phục cần thiết và không có gì khác. Ví dụ shadow_none.glsl: chứa:

float getShadowCoefficient()
{
    return 1;
}

shadow_simple.glslchứa (đơn giản hóa từ trình đổ bóng của tôi thực hiện các CSM):

in vec4 eye_position;

uniform sampler2DShadow shad_tex;
uniform mat4 shad_mat;

float getShadowCoefficient()
{
    vec4 shad_coord = shad_mat * eye_position;
    return texture(shad_tex, shad_coord).x;
}

Và bạn có thể chỉ cần chọn xem bạn có muốn tô bóng hay không bằng cách liên kết một shadow_*shader khác . Giải pháp này rất có thể có nhiều chi phí hơn, nhưng tôi muốn nghĩ rằng trình biên dịch GLSL đủ tốt để tối ưu hóa mọi chi phí phụ so với các cách khác để làm điều này. Tôi chưa thực hiện bất kỳ thử nghiệm nào về vấn đề này, nhưng đó là cách tôi muốn làm.

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.