opengl: glFlush () so với glFinish ()


105

Tôi gặp khó khăn khi phân biệt sự khác biệt thực tế giữa gọi glFlush()glFinish().

Các tài liệu nói điều đó glFlush()glFinish()sẽ đẩy tất cả các hoạt động trong bộ đệm sang OpenGL để người ta có thể yên tâm rằng tất cả chúng sẽ được thực thi, sự khác biệt là nó glFlush()sẽ trả về ngay lập tức ở vị trí như glFinish()các khối cho đến khi tất cả các hoạt động hoàn tất.

Sau khi đọc các định nghĩa, tôi nhận ra rằng nếu tôi sử dụng glFlush()điều đó có thể tôi sẽ gặp phải vấn đề khi gửi nhiều hoạt động đến OpenGL hơn nó có thể thực thi. Vì vậy, chỉ để thử, tôi đã đổi chỗ của tôi glFinish()cho a glFlush()và lo và kìa, chương trình của tôi đã chạy (theo như tôi có thể nói), giống hệt nhau; tốc độ khung hình, sử dụng tài nguyên, mọi thứ đều giống nhau.

Vì vậy, tôi tự hỏi liệu có nhiều sự khác biệt giữa hai cuộc gọi hay mã của tôi khiến chúng chạy không khác nhau. Hoặc nơi một cái nên được sử dụng so với cái kia. Tôi cũng nhận ra rằng OpenGL sẽ có một số lệnh gọi như glIsDone()để kiểm tra xem tất cả các lệnh trong bộ đệm cho a glFlush()đã hoàn thành hay chưa (vì vậy một lệnh không gửi các hoạt động tới OpenGL nhanh hơn chúng có thể được thực thi), nhưng tôi không tìm thấy hàm nào như vậy .

Mã của tôi là vòng lặp trò chơi điển hình:

while (running) {
    process_stuff();
    render_stuff();
}

Câu trả lời:


93

Hãy nhớ rằng những lệnh này đã tồn tại từ những ngày đầu của OpenGL. glFlush đảm bảo rằng các lệnh OpenGL trước đó phải hoàn thành trong thời gian hữu hạn ( thông số kỹ thuật OpenGL 2.1 , trang 245). Nếu bạn vẽ trực tiếp vào bộ đệm phía trước, điều này sẽ đảm bảo rằng các trình điều khiển OpenGL bắt đầu vẽ mà không có quá nhiều độ trễ. Bạn có thể nghĩ đến một cảnh phức tạp xuất hiện đối tượng này đến đối tượng khác trên màn hình, khi bạn gọi glFlush sau mỗi đối tượng. Tuy nhiên, khi sử dụng đệm kép, glFlush thực tế không có tác dụng gì cả, vì các thay đổi sẽ không hiển thị cho đến khi bạn hoán đổi bộ đệm.

glFinish không trở lại cho đến khi tất cả các hiệu ứng từ các lệnh đã ban hành trước đó [...] được thực hiện đầy đủ . Điều này có nghĩa là việc thực thi chương trình của bạn sẽ đợi ở đây cho đến khi mọi pixel cuối cùng được vẽ và OpenGL không phải làm gì nữa. Nếu bạn kết xuất trực tiếp đến bộ đệm phía trước, glFinish là lệnh gọi cần thực hiện trước khi sử dụng lệnh gọi của hệ điều hành để chụp ảnh màn hình. Nó ít hữu ích hơn đối với bộ đệm kép, vì bạn không thấy những thay đổi mà bạn buộc phải hoàn thành.

Vì vậy, nếu bạn sử dụng bộ đệm kép, có thể bạn sẽ không cần glFlush và glFinish. SwapBuffers ngầm định hướng các lệnh gọi OpenGL đến đúng bộ đệm, không cần gọi glFlush trước . Và đừng bận tâm đến việc nhấn mạnh trình điều khiển OpenGL: glFlush sẽ không bị nghẹt thở với quá nhiều lệnh. Nó không được đảm bảo rằng cuộc gọi này trả về ngay lập tức (bất kể điều đó có nghĩa là gì), vì vậy có thể mất bất cứ lúc nào nó cần để xử lý các lệnh của bạn.


1
Ý bạn là gì khi nói rằng glFlush "phải hoàn thành trong thời gian FINITE" và sau đó nói rằng glFlush sẽ không bị nghẹt thở vì "có thể mất BẤT CỨ thời gian nào nó cần để xử lý lệnh của bạn"? (Mũ của tôi) Những cái đó không loại trừ lẫn nhau sao?
Praxiteles

1
Miễn là nó kết thúc tại một số thời điểm nó đáp ứng các tiêu chí thời gian FINITE. Một số trình điều khiển hoàn toàn không chọn cuộc gọi này.
chrisvarnz

SwapBuffers là ngữ cảnh cụ thể và chỉ cần xóa ngữ cảnh chuỗi đang gọi. Nếu hiển thị nhiều ngữ cảnh, mỗi ngữ cảnh nên được xóa theo cách thủ công vì nó không đảm bảo xảy ra khi hoán đổi bộ đệm qua ngữ cảnh khác.
Andreas

22

Như các câu trả lời khác đã gợi ý, thực sự không có câu trả lời tốt theo thông số kỹ thuật. Mục đích chung glFlush()là sau khi gọi nó, CPU chủ sẽ không có công việc liên quan đến OpenGL để làm - các lệnh sẽ được đẩy đến phần cứng đồ họa. Mục đích chung của glFinish()nó là sau khi nó quay trở lại, không còn công việc nào còn lại và kết quả sẽ có sẵn ở tất cả các API không phải OpenGL thích hợp (ví dụ: đọc từ bộ đệm khung, ảnh chụp màn hình, v.v.). Điều đó có thực sự xảy ra hay không là phụ thuộc vào tài xế. Đặc điểm kỹ thuật cho phép rất nhiều vĩ độ như thế nào là hợp pháp.


18

Tôi luôn bối rối về hai lệnh đó, nhưng hình ảnh này đã làm cho tôi thấy rõ tất cả: Flush vs Finish Rõ ràng một số trình điều khiển GPU không gửi các lệnh đã ban hành đến phần cứng trừ khi một số lệnh nhất định đã được tích lũy. Trong ví dụ này, con số đó là 5 .
Hình ảnh hiển thị các lệnh OpenGL khác nhau (A, B, C, D, E ...) đã được đưa ra. Như chúng ta có thể thấy ở trên cùng, các lệnh vẫn chưa được đưa ra vì hàng đợi chưa đầy.

Ở giữa chúng ta thấy glFlush()ảnh hưởng của các lệnh được xếp hàng đợi như thế nào . Nó yêu cầu trình điều khiển gửi tất cả các lệnh đã xếp hàng đến phần cứng (ngay cả khi hàng đợi chưa đầy). Điều này không chặn chuỗi cuộc gọi. Nó chỉ báo hiệu cho trình điều khiển rằng chúng tôi có thể không gửi thêm bất kỳ lệnh nào. Do đó, việc chờ đợi để lấp đầy hàng đợi sẽ rất lãng phí thời gian.

Ở dưới cùng, chúng tôi thấy một ví dụ sử dụng glFinish(). Nó gần như làm điều tương tự glFlush(), ngoại trừ nó làm cho chuỗi gọi đợi cho đến khi tất cả các lệnh đã được xử lý bởi phần cứng.

Hình ảnh lấy từ sách "Lập trình đồ họa nâng cao sử dụng OpenGL".


Tôi thực sự biết những gì glFlushglFinishphải làm, và tôi không thể biết hình ảnh đó đang nói gì. Bên trái và bên phải có gì? Ngoài ra, hình ảnh đó đã được phát hành trong Miền Công cộng hoặc theo một số giấy phép cho phép bạn đăng nó lên Internet?
Nicol Bolas

Tôi nên giải thích một chút. Về giấy phép: Tôi thực sự không chắc lắm. Tôi tình cờ tìm thấy tệp PDF trên google, khi đang nghiên cứu.
Tara

7

Nếu bạn không thấy bất kỳ sự khác biệt nào về hiệu suất, điều đó có nghĩa là bạn đang làm sai. Như một số người khác đã đề cập, bạn cũng không cần gọi, nhưng nếu bạn gọi glFinish, thì bạn sẽ tự động mất đi tính song song mà GPU và CPU có thể đạt được. Hãy để tôi đi sâu hơn:

Trong thực tế, tất cả công việc bạn gửi cho trình điều khiển được thực hiện theo lô và có thể được gửi đến phần cứng theo cách sau đó (ví dụ: tại thời điểm SwapBuffer).

Vì vậy, nếu bạn đang gọi glFinish, về cơ bản bạn đang buộc trình điều khiển đẩy các lệnh đến GPU (nó được xử lý theo đợt cho đến thời điểm đó và không bao giờ yêu cầu GPU hoạt động) và dừng CPU cho đến khi các lệnh được đẩy hoàn toàn Thực thi. Vì vậy, trong toàn bộ thời gian GPU hoạt động, CPU thì không (ít nhất là trên luồng này). Và tất cả thời gian CPU thực hiện công việc của nó (chủ yếu là các lệnh theo lô), GPU không làm bất cứ điều gì. Vì vậy, yeah, glFinish sẽ ảnh hưởng đến hiệu suất của bạn. (Đây là ước tính gần đúng, vì các trình điều khiển có thể bắt đầu để GPU hoạt động trên một số lệnh nếu nhiều lệnh trong số chúng đã được xử lý sẵn. Tuy nhiên, nó không phải là điển hình, vì bộ đệm lệnh có xu hướng đủ lớn để chứa khá nhiều lệnh).

Bây giờ, tại sao bạn lại gọi là glFinish? Lần duy nhất tôi sử dụng nó là khi tôi gặp lỗi trình điều khiển. Thật vậy, nếu một trong những lệnh bạn gửi xuống phần cứng làm hỏng GPU, thì tùy chọn đơn giản nhất của bạn để xác định lệnh nào là thủ phạm là gọi glFinish sau mỗi lần Draw. Bằng cách đó, bạn có thể thu hẹp những gì chính xác gây ra sự cố

Một lưu ý nhỏ là các API như Direct3D hoàn toàn không hỗ trợ khái niệm Kết thúc.


5
Trên "Bây giờ, tại sao bạn lại gọi là glFinish". Nếu bạn đang tải tài nguyên (ví dụ: kết cấu) trên một luồng và sử dụng chúng để hiển thị trên luồng khác, với việc chia sẻ thông qua wglShareLists hoặc tương tự, bạn cần gọi glFinish trước khi báo hiệu cho luồng kết xuất rằng bạn có thể tự do hiển thị những gì đã được tải không đồng bộ.
Marco Mp

Như tôi đã nói bên dưới, điều này giống như một cách giải quyết lỗi trình điều khiển. Tôi không thể tìm thấy bất kỳ đề cập kỹ thuật nào về yêu cầu này. Trên windows, trình điều khiển phải khóa, trên mac bạn phải thực hiện CGLLockContext. Nhưng sử dụng glFinish có vẻ rất sai lầm. Để biết khi nào một kết cấu hợp lệ, bạn phải thực hiện khóa hoặc sự kiện của riêng mình trong mọi trường hợp.
starmole

1
opencl opengl interop docs khuyến nghị glFinish để đồng bộ hóa "Trước khi gọi clEnqueueAcquireGLObjects, ứng dụng phải đảm bảo rằng bất kỳ hoạt động GL đang chờ xử lý nào truy cập vào các đối tượng được chỉ định trong mem_objects đã hoàn thành. Điều này có thể được thực hiện một cách dễ dàng bằng cách phát hành và chờ hoàn thành lệnh glFinish trên tất cả GL bối cảnh với các tham chiếu đang chờ xử lý đến những đối tượng này "
Mark Essel

1
bạn không cần phải gọi glFinish, bạn có thể sử dụng các đối tượng đồng bộ.
chrisvarnz

Chỉ cần nói thêm, kể từ câu trả lời ban đầu: Một số triển khai GL bắt đầu sử dụng flush / finish để đồng bộ hóa giữa các ngữ cảnh được chia sẻ trên các luồng khác nhau: developer.apple.com/library/ios/documentation/3DDrawing/… - Đáng chú ý nhất là Apple IOS. Tôi nghĩ về cơ bản đó là một hành vi lạm dụng API khác. Nhưng đây là một cảnh báo cho câu trả lời ban đầu của tôi: Đối với một số triển khai, kết thúc / xả nước là cần thiết. Nhưng cách thức và lý do triển khai được xác định chứ không phải thông số kỹ thuật OpenGL.
starmole

7

glFlush thực sự bắt nguồn từ mô hình máy chủ khách hàng. Bạn gửi tất cả các lệnh gl thông qua một đường ống đến một máy chủ gl. Đường ống đó có thể đệm. Cũng giống như bất kỳ tệp hoặc mạng nào, i / o có thể đệm. glFlush chỉ nói "gửi bộ đệm ngay bây giờ, ngay cả khi nó chưa đầy!". Trên hệ thống cục bộ, điều này hầu như không cần thiết vì một API OpenGL cục bộ không có khả năng tự đệm và chỉ đưa ra lệnh trực tiếp. Ngoài ra, tất cả các lệnh gây ra kết xuất thực tế sẽ thực hiện xả ngầm.

Mặt khác, glFinish được tạo ra để đo hiệu suất. Loại PING tới máy chủ GL. Nó làm tròn một lệnh và đợi cho đến khi máy chủ phản hồi "Tôi đang rảnh".

Ngày nay, những người lái xe địa phương hiện đại có những ý tưởng khá sáng tạo, mặc dù vậy việc nhàn rỗi có nghĩa là gì. Nó là "tất cả các pixel được vẽ" hay "hàng đợi lệnh của tôi có khoảng trống"? Cũng bởi vì nhiều chương trình cũ rắc glFlush và glFinish trong suốt mã của họ mà không có lý do vì voodoo mã hóa nhiều trình điều khiển hiện đại chỉ bỏ qua chúng như một "tối ưu hóa". Không thể đổ lỗi cho họ về điều đó, thực sự.

Vì vậy, tóm lại: Coi cả glFinish và glFlush là không có tác dụng gì trong thực tế trừ khi bạn đang mã hóa cho một máy chủ SGI OpenGL từ xa cổ xưa.


4
Sai lầm. Như trong nhận xét tôi đã để lại trong câu trả lời ở trên: Nếu bạn đang tải tài nguyên (ví dụ: kết cấu) trên một luồng và sử dụng chúng để hiển thị trên luồng khác, với việc chia sẻ thông qua wglShareLists hoặc tương tự, bạn cần gọi glFinish trước khi báo hiệu cho luồng kết xuất rằng nó hoàn toàn miễn phí để hiển thị những gì đã được tải không đồng bộ. Và nó không phải là không như bạn đã nói.
Marco Mp

Có thật không? Bạn có thể sao lưu điều này khi cần gọi glFinish trước khi sử dụng một đối tượng được chia sẻ với một số liên kết đặc tả không? Tôi hoàn toàn có thể thấy một trình điều khiển lỗi cần điều này. Và kiểu đó trở lại những gì tôi đã nói. Mọi người sử dụng glFinish để xử lý các trình điều khiển lỗi. Bởi vì các trình điều khiển hoạt động bình thường bỏ qua nó cho hiệu suất. Trường hợp sử dụng thông số kỹ thuật ban đầu của cấu hình (và hiển thị có bộ đệm đơn) không hoạt động nữa.
starmole

2
Kinh nghiệm cá nhân về việc phải xử lý các kết cấu bị hỏng, cộng với điều này: higherorderfun.com/blog/2011/05/26/… Nếu bạn nghĩ về nó, tuy nhiên, nó có ý nghĩa, vì nó không khác lắm với việc có mutexes hoặc đồng bộ khác. api giữa các luồng - trước khi một ngữ cảnh có thể sử dụng một tài nguyên được chia sẻ, người kia phải hoàn thành việc tải tài nguyên đó, do đó cần đảm bảo bộ đệm đã được làm trống. Tôi suy nghĩ nhiều hơn về góc hợp cụ thể của các thông số kỹ thuật chứ không phải lỗi, nhưng tôi đã không có bằng chứng cho tuyên bố này :)
Marco Mp

Câu trả lời tuyệt vời, cảm ơn. Đặc biệt là "nhiều chương trình cũ rắc glFlush và glFinish trong suốt mã của họ mà không có lý do như voodoo".
akauppi

Nó thực sự không có hoạt động? Không phải glFinish và / hoặc glFlush có giá trị cao để xử lý hình ảnh cường độ cao hoặc xử lý toán học dựa trên GPU hay không? Ví dụ: chúng tôi không đọc kết quả tính toán GPU từ bộ đệm pixel đến CPU cho đến sau khi gọi glFinish để đảm bảo tất cả các phép tính pixel đã được ghi. Chúng ta đang thiếu một cái gì đó?
Praxiteles

5

Hãy xem ở đây . Tóm lại, nó nói:

glFinish () có tác dụng tương tự như glFlush (), với việc bổ sung glFinish () sẽ chặn cho đến khi tất cả các lệnh được gửi được thực thi.

Một bài báo khác mô tả những điểm khác biệt khác:

  • Chức năng hoán đổi (được sử dụng trong các ứng dụng đệm kép) tự động xóa các lệnh, do đó không cần gọi glFlush
  • glFinish buộc OpenGL thực hiện các lệnh nổi bật, đó là một ý tưởng tồi (ví dụ: với VSync)

Tóm lại, điều này có nghĩa là bạn thậm chí không cần các chức năng này khi sử dụng bộ đệm kép, ngoại trừ trường hợp triển khai bộ đệm hoán đổi của bạn không tự động xóa các lệnh.


Đúng vậy, nhưng tài liệu nói rằng cả hai đều có tác dụng giống nhau , chẳng hạn như chỉ có một số khác biệt liên quan đến hệ thống cửa sổ. Nhưng dù sao thì tôi cũng đã chỉnh sửa câu trả lời của mình;)
AndiDog

Tốt gọi ra về sắc thái. Nó làm rõ liệu có cần thiết phải gọi glFlush () và THEN glFinish () - (một câu hỏi mà chúng tôi đã có sau khi đọc mọi thứ ở trên.)
Praxiteles

4

Dường như không có cách nào để truy vấn trạng thái của bộ đệm. Có phần mở rộng này của Apple có thể phục vụ cùng một mục đích, nhưng nó có vẻ không đa nền tảng (chưa thử.) Nhìn sơ qua, có vẻ như trước khi flushbạn đẩy lệnh hàng rào vào; sau đó bạn có thể truy vấn trạng thái của hàng rào đó khi nó di chuyển qua bộ đệm.

Tôi tự hỏi liệu bạn có thể sử dụng flushtrước khi đệm các lệnh hay không, nhưng trước khi bắt đầu hiển thị khung tiếp theo mà bạn gọi finish. Điều này sẽ cho phép bạn bắt đầu xử lý khung hình tiếp theo khi GPU hoạt động, nhưng nếu nó không được thực hiện vào thời điểm bạn quay lại, finishsẽ chặn để đảm bảo mọi thứ đều ở trạng thái mới.

Tôi đã không thử điều này, nhưng tôi sẽ sớm.

Tôi đã thử nó trên một ứng dụng cũ sử dụng CPU và GPU khá đồng đều. (Ban đầu nó được sử dụng finish.)

Khi tôi thay đổi nó thành flushcuối và finishlúc bắt đầu, không có vấn đề gì ngay lập tức. (Mọi thứ đều ổn!) Khả năng phản hồi của chương trình tăng lên, có thể là do CPU không bị đình trệ khi chờ GPU. Chắc chắn là một phương pháp tốt hơn.

Để so sánh, tôi đã loại bỏ finished từ đầu khung, rời khỏi flushvà nó hoạt động giống nhau.

Vì vậy, tôi sẽ nói sử dụng flushfinish, bởi vì khi bộ đệm trống khi gọi tới finish, không có lần truy cập hiệu suất nào. Và tôi đoán nếu bộ đệm đã đầy bạn nên muốn finish.


0

Câu hỏi đặt ra là: bạn có muốn mã của mình tiếp tục chạy trong khi các lệnh OpenGL đang được thực thi hay chỉ để chạy sau khi các lệnh OpenGL của bạn đã được thực thi.

Điều này có thể quan trọng trong các trường hợp, chẳng hạn như sự chậm trễ của mạng, chỉ có một số đầu ra bảng điều khiển nhất định sau khi hình ảnh đã được vẽ hoặc tương tự.

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.