Làm thế nào để cải thiện hiệu suất của shader này?


8

Tôi có một cảnh với 150000 trường hợp. Tôi sử dụng glsl và opengl 4.0. Shader A chậm hơn 2 lần so với shader BIe với shader AI nhận 20fps, với shader BI trung bình 40fps. Tôi có thể làm gì để cải thiện shader A?

Shader A:

#version 400

struct Light {
   vec3 position;
   vec3 intensities; //a.k.a the color of the light
   float ambientCoefficient;
   float attenuation;
};

uniform bool useLight;
uniform mat4 modelMatrix;
uniform bool useTex;
uniform sampler2D tex;
uniform Light light;
uniform vec4 diffuseColor;

in vec2 fragTexCoord;
in vec3 fragNormal;
in vec3 fragVert;

out vec4 finalColor;

void main() {
    vec3 normal = normalize(transpose(inverse(mat3(modelMatrix))) * fragNormal);
    vec3 surfacePos = vec3(modelMatrix * vec4(fragVert, 1));

    vec4 surfaceColor = vec4(0,0,0,1);

    if(useTex) {
        surfaceColor = texture(tex, fragTexCoord);
    }
    else {
        //surfaceColor = diffuseColor;
        surfaceColor = vec4(0,1,0,1);
    }

    if(useLight) {
        vec3 surfaceToLight = normalize(light.position - surfacePos);

        //ambient
        vec3 ambient = light.ambientCoefficient * surfaceColor.rgb * light.intensities;

        //diffuse
        float diffuseCoefficient = max(0.0, dot(normal, surfaceToLight));
        vec3 diffuse = diffuseCoefficient * surfaceColor.rgb * light.intensities;

        //attenuation
        float distanceToLight = length(light.position - surfacePos);
        float attenuation = 1.0 / (1.0 + light.attenuation * pow(distanceToLight, 2));

        //linear color (color before gamma correction)
        vec3 linearColor = ambient + attenuation*(diffuse);

        //final color (after gamma correction)
        vec3 gamma = vec3(1.0/2.2);
        finalColor = vec4(pow(linearColor, gamma), surfaceColor.a);
    }
    else {
        finalColor = surfaceColor;
    }
}

Shader B:

#version 400

struct Light {
   vec3 position;
   vec3 intensities; //a.k.a the color of the light
   float ambientCoefficient;
   float attenuation;
};

uniform bool useLight;
uniform mat4 modelMatrix;
uniform bool useTex;
uniform sampler2D tex;
uniform Light light;
uniform vec4 diffuseColor;

in vec2 fragTexCoord;
in vec3 fragNormal;
in vec3 fragVert;

out vec4 finalColor;

void main() {
    finalColor = vec4(0,0,0.7,1);
}

1
Đây có thể là một câu hỏi thú vị cho CodeReview.SE
LukeG

1
@LukeG Tôi đồng ý, tuy nhiên tôi sẽ không ngạc nhiên chút nào nếu nó có thêm lực kéo ở đây, OpenGL có lẽ là một ngách nhỏ ở đó so với bánh mì và bơ ở đây. Analog sẽ xin lời khuyên về một kịch bản shell trên Unix SE.
Jared Smith

@LukeG - đó cũng là trường hợp không có gì đặc biệt sai với mã này khi được xem xét một cách cô lập. Người ta cũng phải xem xét nền tảng mà nó đang chạy, GPU và các đặc tính hiệu suất của nền tảng đó để có được bức tranh đầy đủ hơn.
Maximus Minimus

Tôi có thể thiếu một cái gì đó ở đây, nếu vậy xin hãy tha thứ cho tôi. Nhưng bạn chỉ hỏi tại sao mã với ít hoạt động hơn đáng kể nhanh hơn mã kia?
Doddy

Câu trả lời:


15

Đầu tiên, bạn nên tính toán trước càng nhiều dữ liệu càng tốt và tránh tính toán các giá trị giống nhau cho mỗi pixel.

Bạn có một mảnh như vậy:

transpose(inverse(mat3(modelMatrix))

Điều này đảo ngược ma trận, hoạt động không tầm thường và mặc dù thực tế là dữ liệu đầu vào giống nhau cho từng pixel (vì vậy kết quả sẽ giống nhau), nó sẽ tính toán lại cho mỗi pixel. Tính toán nó một lần, trước khi kết xuất và chuyển kết quả dưới dạng ma trận khác, giống như bạn làm với modelMatrix.

Sau đó, bạn cũng bình thường hóa (light.position - surfacePos)vectơ nhưng bạn cũng đang tính toán lengthnó, vì vậy nó dẫn đến hai sqrtthao tác thay vì một.

Ngoài ra, tùy thuộc vào phần cứng của bạn, bạn có thể thấy rằng sử dụng if'strong trình đổ bóng pixel có thể làm giảm hiệu suất của bạn. Nếu đó là trường hợp bạn có thể chuẩn bị một vài phiên bản khác nhau của trình đổ bóng và xử lý các trường hợp của bạn tùy thuộc vào useLightuseTexthuộc tính.

BIÊN TẬP:

Bạn cũng có thể cố gắng hạ thấp phiên bản OpenGL được xác định trong trình đổ bóng, để trở thành phiên bản thấp nhất hỗ trợ các tính năng của bạn. Về lý thuyết, nó không nên làm gì nhiều nhưng tùy thuộc vào trình điều khiển và nhà cung cấp CTNH, thực tế có thể khác ... (nghĩa là nếu GPU của bạn hỗ trợ OGL 4.0, điều đó thường có nghĩa là nó nhanh trong OGL 3.0 nhưng rất chậm trong 4.0, nhưng bạn cần kiểm tra nó về trường hợp cụ thể).


Nếu vec3 bình thường chỉ phụ thuộc vào các biến phân đoạn, GPU sẽ không biết điều đó và tránh tính toán dư thừa?
dùng68854

@ user68854 - GPU thường không hoạt động như vậy: chúng thường chỉ cày xới công việc. Trình biên dịch shader của bạn có thể xác định được điều này, nhưng nó có thể không, và nó có hay không không được xác định bởi thông số GL; nói cách khác, bạn không nên dựa vào nó.
Maximus Minimus

@ user68854 Tôi không nghĩ vậy. Mặc dù trình biên dịch shader của bạn có thể tối ưu hóa một số mã cho bạn nhưng nó không thể thực hiện điều này giữa các lần thực hiện khác nhau của trình đổ bóng của bạn, nhưng đơn giản là nó không có 'Scratchpad' để đưa các dữ liệu phổ biến đó vào. Vì vậy, nó có thể tối ưu hóa trường hợp sqrt đôi của bạn, nhưng ngay cả khi nhận ra rằng kết quả của bạn inverselà không đổi, không có chỗ nào trên GPU để lưu trữ dữ liệu này. Nó chỉ không hoạt động theo cách này (AFAIK).
kolenda

@kolenda Có thể rất thú vị khi hỏi trên computergraphics.stackexchange.com nếu việc tối ưu hóa như vậy là có thể.
porglezomp

1
Điều này làm tôi bối rối: transpose(inverse(mat3(modelMatrix))Không phải chuyển vị của ma trận xoay 3x3 đã là nghịch đảo sao?
Sidar

3

Nhìn vào dòng này nói riêng:

vec3 normal = normalize(transpose(inverse(mat3(modelMatrix))) * fragNormal);

làm việc ngược lại với một ma trận rất là đánh thuế, và nên được tính toán trước bởi cpu thay vì buộc gpu phải lãng phí thời gian để làm rối tung nó.


3

Không có nhiều thứ bạn có thể làm; thực tế đơn giản là shader A hoạt động nhiều hơn shader B, vì vậy nó sẽ luôn chạy chậm hơn.

Tuy nhiên, chúng ta có thể thu hẹp khoảng cách. Tôi không thể cung cấp cho bạn số liệu xác định bao nhiêu, tất cả phụ thuộc vào đặc điểm hiệu suất của phần còn lại của chương trình của bạn, vì vậy hãy coi đây là những thông lệ tốt chung.

transpose(inverse(mat3(modelMatrix)))

Đó là rất nhiều nghịch đảo và chuyển vị trên mỗi khung. Thay vào đó, hãy thực hiện trên CPU, chỉ một lần (hoặc ít nhất là khi modelMatrix thay đổi) và gửi ma trận nghịch đảo / chuyển đổi dưới dạng đồng phục bổ sung. Nếu các hoạt động của ALU là một nút cổ chai đối với bạn, thì điều này sẽ giúp bạn tăng mạnh nhất.

if(useTex) {

Sự phân nhánh không phải là cái chết như trước đây, nhưng bạn vẫn có thể tránh nó ở đây (và lưu một khe đồng nhất) bằng cách tạo kết cấu 1x1 (có màu thích hợp) và thay vào đó.

if(useLight) {

Phân nhánh nhiều hơn. Trong trường hợp này, không có sự thay thế rõ ràng nào (chẳng hạn như kết cấu 1x1) vì vậy tôi khuyến khích bạn tách điều kiện này thành một shader thứ ba và điểm chuẩn cả hai (tức là 2 shader có một nhánh so với 3 shader mà không có). Tùy thuộc vào tần suất bạn cần thay đổi shader, bạn có thể thấy hoặc không thấy sự khác biệt về hiệu suất khi so sánh với việc phân nhánh.


Kết cấu 1x1 có vẻ như giải pháp thử nghiệm. Tốt hơn để làm cho một shader khác. Dù sao tại sao phân nhánh là tốn kém? Nhảy với điều kiện trên lõi GPU có đắt không?
dùng68854

@ user68854 - GPU không hoạt động giống như CPU. Các GPU cũ hơn, đặc biệt, không có hỗ trợ riêng cho việc phân nhánh hoàn toàn mà thay vào đó thực hiện cả hai mặt của nhánh sau đó sử dụng một hướng dẫn bước để chọn đúng. Ngày nay, việc phân nhánh rẻ hơn (nhưng vẫn không miễn phí) vì vậy thật đáng để thử nếu thay đổi shader có rẻ hơn hay không. Sử dụng kết cấu 1x1 là một thủ thuật nổi tiếng, vui lòng xem stackoverflow.com/questions/22703166/iêu chẳng hạn.
Maximus Minimus

Sự phân nhánh có thể tốn kém vì tối ưu hóa đường ống. Khi GPU của bạn thực hiện một số hoạt động, thực tế nó đang chuẩn bị cho một vài cái tiếp theo. Nếu nó gặp phải ifnó có thể chỉ chuẩn bị một nhánh - nếu bạn chọn nhánh thứ 2 thì công việc này bị lãng phí và cần phải được khởi động lại. Điều tương tự cũng xảy ra trên CPU. Thủ thuật với kết cấu 1x1 có thể giúp ích hay không - tất cả phụ thuộc vào trình tạo bóng cụ thể, nhà cung cấp GPU, kiến ​​trúc, trình điều khiển, v.v. vì vậy bạn chỉ cần tự mình kiểm tra.
kolenda
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.