Tại sao điều kiện này trong shader mảnh của tôi rất chậm?


19

Tôi đã thiết lập một số mã đo FPS trong WebGL (dựa trên câu trả lời SO này ) và đã phát hiện ra một số điều kỳ lạ với hiệu suất của trình đổ bóng mảnh của tôi. Mã chỉ hiển thị một hình tứ giác (hay đúng hơn là hai hình tam giác) trên khung vẽ 1024x1024, vì vậy tất cả các phép thuật xảy ra trong shader mảnh.

Hãy xem xét trình đổ bóng đơn giản này (GLSL; trình tạo bóng đỉnh chỉ là một đường dẫn):

// some definitions

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

    // Nothing to see here...

    gl_FragColor = vec4(value, value, value, 1.0);
}

Vì vậy, điều này chỉ làm cho một vải trắng. Nó trung bình khoảng 30 khung hình / giây trên máy của tôi.

Bây giờ, hãy tăng cường số lượng giòn và tính toán từng mảnh dựa trên một vài quãng tám của tiếng ồn phụ thuộc vào vị trí:

void main() {
    float seed = uSeed;
    float x = vPos.x;
    float y = vPos.y;

    float value = 1.0;

      float noise;
      for ( int j=0; j<10; ++j)
      {
        noise = 0.0;
        for ( int i=4; i>0; i-- )
        {
            float oct = pow(2.0,float(i));
            noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
        }
      }

      value = noise/2.0+0.5;

    gl_FragColor = vec4(value, value, value, 1.0);
}

Nếu bạn muốn chạy mã trên, tôi đã sử dụng triển khai nàysnoise .

Điều này làm giảm fps xuống một cái gì đó như 7. Điều đó có ý nghĩa.

Bây giờ là phần kỳ lạ ... chúng ta chỉ tính một trong 16 đoạn là nhiễu và để lại các phần trắng khác, bằng cách gói tính toán nhiễu trong điều kiện sau:

if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
    // same noise computation
}

Bạn mong muốn điều này sẽ nhanh hơn nhiều, nhưng nó vẫn chỉ có 7 khung hình / giây.

Đối với một thử nghiệm nữa, thay vào đó, hãy lọc các pixel có điều kiện sau:

if (x > 0.5 && y > 0.5) {
    // same noise computation
}

Điều này cho cùng một số pixel nhiễu chính xác như trước đây, nhưng hiện tại chúng tôi đã quay lại gần 30 khung hình / giây.

Chuyện gì đang xảy ra ở đây? Không phải hai cách để lọc ra 16 pixel sẽ cho cùng một số chu kỳ sao? Và tại sao tốc độ chậm lại chậm như hiển thị tất cả các pixel dưới dạng nhiễu?

Câu hỏi thưởng: Tôi có thể làm gì về điều này? Có cách nào để làm việc xung quanh việc thực hiện khủng khiếp nếu tôi thực sự làm muốn đốm vải của tôi chỉ với một vài mảnh vỡ đắt tiền?

(Để chắc chắn, tôi đã xác nhận rằng tính toán modulo thực tế hoàn toàn không ảnh hưởng đến tốc độ khung hình, bằng cách hiển thị mỗi pixel thứ 16 màu đen thay vì màu trắng.)

Câu trả lời:


22

Các pixel được nhóm thành các ô vuông nhỏ (lớn như thế nào phụ thuộc vào phần cứng) và được tính toán với nhau trong một đường ống SIMD duy nhất . (cấu trúc của loại mảng SIMD)

Đường ống này (có một số tên khác nhau tùy thuộc vào nhà cung cấp: warps, wavefronts) sẽ thực hiện các thao tác cho từng pixel / đoạn trong bước khóa. Điều này có nghĩa là nếu 1 pixel cần tính toán thì tất cả các pixel sẽ tính toán và những pixel không cần kết quả sẽ ném nó đi.

Nếu tất cả các mảnh theo cùng một đường đi qua một shader thì các nhánh khác sẽ không được thực thi.

Điều này có nghĩa là phương pháp tính toán đầu tiên của bạn mỗi pixel thứ 16 sẽ là phân nhánh trường hợp xấu nhất.

Nếu bạn muốn vẫn giảm kích thước hình ảnh của mình thì chỉ cần kết xuất thành một kết cấu nhỏ hơn và sau đó nâng cấp nó lên.


5
Kết xuất đến một kết cấu nhỏ hơn và upampling là một cách tốt để làm điều đó. Nhưng nếu vì một lý do nào đó, bạn thực sự cần phải ghi vào mỗi pixel thứ 16 của kết cấu lớn, sử dụng một shader tính toán với một lời gọi cho mỗi pixel thứ 16 cộng với tải / lưu trữ hình ảnh để phân tán ghi vào mục tiêu kết xuất có thể là một lựa chọn tốt.
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.