Tận dụng đa luồng giữa vòng lặp trò chơi và openGL


10

Nói về ngữ cảnh của một trò chơi dựa trên trình kết xuất openGL:

Giả sử có hai luồng:

  1. Cập nhật logic trò chơi và vật lý, vv cho các đối tượng trong trò chơi

  2. Thực hiện các lệnh gọi openGL cho từng đối tượng trò chơi dựa trên dữ liệu trong các đối tượng trò chơi (luồng 1 đó tiếp tục cập nhật)

Trừ khi bạn có hai bản sao của mỗi đối tượng trò chơi ở trạng thái hiện tại của trò chơi, bạn sẽ phải tạm dừng Chủ đề 1 trong khi Chủ đề 2 thực hiện các cuộc gọi rút thăm nếu không các đối tượng trò chơi sẽ được cập nhật ở giữa lệnh gọi cho đối tượng đó. là không mong muốn!

Nhưng việc dừng chủ đề 1 để thực hiện các cuộc gọi rút thăm từ chủ đề 2 sẽ giết chết toàn bộ mục đích của đa luồng / đồng thời

Có cách tiếp cận nào tốt hơn cho việc này ngoài việc sử dụng hàng trăm hoặc hàng nghìn hoặc đồng bộ hóa các đối tượng / hàng rào để kiến ​​trúc đa lõi có thể được khai thác để thực hiện?

Tôi biết tôi vẫn có thể sử dụng đa luồng để tải kết cấu và biên dịch shader cho các đối tượng chưa phải là một phần của trạng thái trò chơi hiện tại nhưng làm cách nào để làm cho các đối tượng hoạt động / hiển thị mà không gây xung đột với vẽ và cập nhật?

Điều gì xảy ra nếu tôi sử dụng khóa đồng bộ hóa riêng biệt trong từng đối tượng trò chơi? Bằng cách này, bất kỳ luồng nào sẽ chỉ chặn trên một đối tượng (trường hợp lý tưởng) chứ không phải cho toàn bộ chu trình cập nhật / vẽ! Nhưng làm thế nào tốn kém là mất khóa trên mỗi đối tượng (trò chơi có thể có một ngàn đối tượng)?


1. Giả sử sẽ chỉ có hai luồng không có tỷ lệ tốt, 4 lõi là rất phổ biến và sẽ chỉ tăng. 2. Câu trả lời chiến lược thực hành tốt nhất: áp dụng Phân chia trách nhiệm truy vấn lệnh và Mô hình diễn viên / Hệ thống thực thể thành phần. 3. Câu trả lời thực tế: chỉ cần hack nó theo cách C ++ truyền thống với các khóa và công cụ.
chối

Một kho dữ liệu chung, một khóa.
Justin

@justin đúng như tôi đã nói với khóa đồng bộ Ưu điểm duy nhất của đa luồng sẽ bị giết chết một cách tàn nhẫn vào ban ngày, luồng cập nhật sẽ phải đợi cho đến khi vẽ chủ đề thực hiện cuộc gọi cho tất cả các đối tượng thì luồng vẽ sẽ đợi cho đến khi vòng cập nhật hoàn tất công cụ cập nhật ! đó là tồi tệ hơn so với cách tiếp cận đơn luồng
Allahjane

1
Có vẻ như cách tiếp cận đa luồng trong các trò chơi với api đồ họa không đồng bộ (ví dụ openGL) vẫn là chủ đề đang hoạt động và không có giải pháp chuẩn hoặc gần như hoàn hảo này
Allahjane

1
Kho dữ liệu phổ biến cho công cụ của bạn và trình kết xuất của bạn không nên là đối tượng trò chơi của bạn. Nó phải là một số đại diện mà trình kết xuất có thể xử lý càng nhanh càng tốt.
Justin

Câu trả lời:


13

Cách tiếp cận bạn đã mô tả, sử dụng khóa, sẽ rất không hiệu quả và rất có thể chậm hơn so với sử dụng một luồng. Cách tiếp cận khác của việc giữ các bản sao dữ liệu trong mỗi luồng có thể sẽ hoạt động tốt "thông minh về tốc độ", nhưng với chi phí bộ nhớ bị cấm và độ phức tạp của mã để giữ các bản sao đồng bộ.

Có một số cách tiếp cận khác nhau, một giải pháp phổ biến cho kết xuất đa luồng là sử dụng bộ đệm đôi của các lệnh. Điều này bao gồm chạy back-end của trình kết xuất trong một luồng riêng biệt, trong đó tất cả các lệnh gọi và giao tiếp với API kết xuất được thực hiện. Chuỗi giao diện người dùng chạy logic trò chơi giao tiếp với trình kết xuất phía sau thông qua bộ đệm lệnh (bộ đệm đôi). Với thiết lập này, bạn chỉ có một điểm đồng bộ hóa khi hoàn thành khung. Trong khi front-end đang lấp đầy một bộ đệm bằng các lệnh render, thì back-end đang tiêu thụ cái khác. Nếu cả hai chủ đề được cân bằng tốt, không ai nên chết đói. Tuy nhiên, cách tiếp cận này là tối ưu, vì nó đưa ra độ trễ trong các khung được hiển thị, cộng với, trình điều khiển OpenGL có thể đã thực hiện điều này trong quy trình riêng của mình, do đó, hiệu suất sẽ phải được đo lường cẩn thận. Nó cũng chỉ sử dụng hai lõi, tốt nhất. Cách tiếp cận này đã được sử dụng trong một số trò chơi thành công, chẳng hạn như Doom 3Quake 3

Các cách tiếp cận có khả năng mở rộng hơn giúp sử dụng CPU đa lõi tốt hơn là các cách tiếp cận dựa trên các tác vụ độc lập , trong đó bạn thực hiện một yêu cầu không đồng bộ được phục vụ trong luồng thứ cấp, trong khi luồng xử lý yêu cầu tiếp tục với một số công việc khác. Nhiệm vụ lý tưởng là không có sự phụ thuộc với các luồng khác, để tránh các khóa (cũng tránh dữ liệu được chia sẻ / toàn cầu như bệnh dịch hạch!). Các kiến ​​trúc dựa trên nhiệm vụ có thể sử dụng nhiều hơn trong các phần cục bộ của trò chơi, chẳng hạn như hoạt hình điện toán, tìm đường dẫn AI, tạo thủ tục, tải động các đạo cụ cảnh, v.v. Trò chơi có đầy đủ các sự kiện, hầu hết các loại sự kiện đều không đồng bộ, vì vậy nó là dễ dàng để làm cho chúng chạy trong các chủ đề riêng biệt.

Cuối cùng, tôi khuyên bạn nên đọc:


chỉ tò mò thôi Làm thế nào để bạn biết Doom 3 đã sử dụng phương pháp này? Tôi nghĩ rằng các nhà phát triển không bao giờ để ra kỹ thuật!
Allahjane

4
@ ALLahjane, Doom 3 là Nguồn mở . Trong liên kết tôi đã cung cấp, bạn sẽ tìm thấy đánh giá về kiến ​​trúc tổng thể của trò chơi. Và bạn đã nhầm, vâng, rất hiếm khi tìm thấy các trò chơi Nguồn mở hoàn toàn, nhưng các nhà phát triển thường đưa ra các kỹ thuật và thủ thuật của họ trong blog, bài báo và sự kiện như GDC .
glampert

1
Hừm. Đây là một bài đọc tuyệt vời! cảm ơn vì đã khiến tôi nhận ra điều này! Bạn nhận được đánh dấu :)
Allahjane
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.