OpenGL Compute Shader tạo các chỉ số tam giác: Làm thế nào để có được số phần tử chính xác cho glDrawElementsInirect?


7

Tôi có một chuỗi các shader tính toán tạo ra một lưới được lập chỉ mục. Người cuối cùng viết các chỉ số được tạo như thế này:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndexCount);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
}

Sau khi lưới đã được tạo, nó được vẽ bằng glDrawElementsInirect . Bộ chỉ số gián tiếp trong mã ở trên là bộ đếm nguyên tử_uint ở vị trí 0 bên trong GL_DRAW_INDIRECT_BUFFER (xem cấu trúc có tên DrawElementsInirectCommand ). Bộ đếm này bây giờ rõ ràng là quá nhỏ với hệ số ba, vì nó chỉ được tăng một lần cho mỗi tam giác. Hiện tại, tôi nhân nó với 3 sau đó ngay trước khi phát lệnh gọi.

(Tại thời điểm này, việc này được thực hiện bằng cách ánh xạ bộ đệm và nhân lên CPU, điều này tất nhiên là vô nghĩa, nhưng cho thấy toàn bộ mọi thứ về cơ bản đều hoạt động. Mọi thứ đều được vẽ chính xác. Tôi có thể thực hiện nó bằng một lệnh gọi của trình đổ bóng tính toán 1x1x1x1x1x1 , nhưng điều đó dường như chỉ hơi ngớ ngẩn.)

Làm thế nào để tôi thoát khỏi bước nhân thêm này?

Vì đây dường như là một vấn đề rõ ràng mỗi khi các lưới có số lượng chỉ số biến đổi được tạo ra, tôi đoán phải có một giải pháp dễ dàng nào đó mà tôi đang xem?


1
Bạn có thể thêm 3 nguyên tử vào bộ đếm thay vì tăng nó không?
Nathan Reed

1
Không, bộ đếm nguyên tử chỉ có thể được truy vấn, tăng (1) hoặc giảm (1). Xem Điều trong wiki opengl. Chúng không giống như nguyên tử Thêm vv trên Hình ảnh. Bạn chỉ có 8 (hoặc hơn) trong số họ và họ được cho là nhanh hơn rất nhiều khi được truy cập cực kỳ thường xuyên (như khi tạo (và do đó đếm) hàng ngàn ... thứ.)
cupe

Vâng, tôi đoán bạn sẽ phải biến nó từ một "bộ đếm nguyên tử" thành một biến trong SSBO. Sẽ rất thú vị để xem nếu điều đó thực sự chậm hơn (có thể không, tùy thuộc vào CTNH). Ngoài ra, điều duy nhất tôi có thể nghĩ là làm như bạn đã nói và chạy một shader tính toán để nhân giá trị lên 3.
Nathan Reed

Xin chào cupe, có thể xem mã ở đâu đó không? Ps: bạn có đang kiểm tra nhiều mục không?
bầu

Câu trả lời:


7

Nếu bạn có quyền truy cập vào glsl 4.3+ (hoặc glsl ES 3.1), bạn có thể sử dụng nguyên tửAdd

Tùy chọn tiếp theo là sử dụng a barrier()sau khi tất cả các đỉnh được tạo trong tính toán và sau đó nhân giá trị trong bộ đếm:

main(){

    // generate vertices

    barrier();
    if(gl_localInvocationID == vec3(0)){
        indirectCount = atomicCounter(indirectIndexCount)*3;
    }
}

Điều này mô phỏng chạy một shader tính toán 1x1x1 khác để nhân chỉ số.

Nếu không, bạn có thể sử dụng một nguyên tử thứ hai để giữ đỉnh:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndex);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
}

Cả hai indirectIndexindirectIndexCountđược khởi tạo thành 0 và indirectIndexCountlà những gì được truyền qua glDrawElementsIndirect.


Tôi thích ý tưởng thứ hai bởi vì nó rõ ràng rằng tôi cần hai thứ khác nhau: Một cách để thêm các hình tam giác một cách nguyên tử và một cách để có được chỉ số cuối cùng. Không phải là cùng một cơ chế. Và vâng, tôi cũng sẽ thử nguyên tử. Như Nathan đã đề xuất trong một bình luận ở trên, sẽ rất thú vị để xem nếu nó thực sự chậm hơn. Tôi sẽ báo cáo lại với những phát hiện.
cupe

1
Có thể xác nhận rằng AtomicAdd thực sự không chậm hơn đối với trường hợp sử dụng này trên nvidia kepler (~ 1,5k chỉ số: 0,1ms - có thể cần kích thước lớn hơn cho điểm chuẩn có ý nghĩa). Liên kết bộ đệm lệnh gián tiếp dưới dạng SSBO và ghi vào uint ở vị trí 0 hoạt động tốt. Đôi khi, người ta chỉ nên thử các giải pháp dễ dàng trước tiên ...
cupe
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.