Vòng lặp trò chơi đa luồng


7

Tôi đang cố gắng thực hiện một vòng lặp trò chơi đa luồng. Tôi đã làm điều đó nhưng phải sử dụng một vài khóa cho điều đó, làm hỏng hiệu suất. Sau khi nghiên cứu một chút tôi đã nảy ra ý tưởng này:

Thay vì chia các hệ thống con động cơ thành các luồng khác nhau (ví dụ: vật lý, hoạt hình), tất cả các hệ thống con đều chạy trên tất cả các luồng. Vì vậy, khi chúng tôi có bốn CPU, bốn luồng được tạo, với mỗi luồng có một vòng lặp cho tất cả các hệ thống con. Vì vậy, vòng lặp trò chơi lõi đơn được sao chép trên cả bốn luồng. Các vòng lặp trò chơi này được điều khiển bởi một vòng lặp khác, sẽ gửi tin nhắn (hoặc 'công việc', 'nhiệm vụ') đến một trong các chuỗi này (tùy thuộc vào cách sử dụng của chúng) theo đầu vào của người dùng hoặc tập lệnh. Điều này có thể được thực hiện với bộ đệm lệnh đệm đôi.

Chỉ vòng lặp kết xuất là một mình trong một luồng cho hiệu suất kết xuất tối đa. Bây giờ tôi đang nghĩ về cách tốt nhất để giao tiếp với vòng lặp kết xuất. Ý tưởng tốt nhất tôi có thể đưa ra là một lần nữa sử dụng bộ đệm lệnh và trao đổi nó khi vòng lặp kết xuất hoàn tất. Theo cách đó, vòng lặp kết xuất không phải đợi bất kỳ vòng lặp nào và có thể tiếp tục kết xuất. Nếu vòng lặp trò chơi chưa kết thúc khi vòng lặp kết xuất hoán đổi bộ đệm, tất cả các lệnh sau đó sẽ được thực hiện trong khung tiếp theo của vòng lặp kết xuất. Để đảm bảo rằng tất cả các đối tượng sẽ được vẽ ngay cả khi vòng lặp trò chơi chưa kết thúc, vòng lặp kết xuất giữ tất cả các đối tượng sẽ được vẽ và vẽ chúng cho đến khi nhận được lệnh dừng vẽ chúng.

Thí dụ

Mục tiêu của tôi là làm cho động cơ có thể mở rộng theo số cpu và làm cho nó sử dụng tất cả các lõi. Đây có phải là một cách để làm điều đó? Cách tiếp cận tốt nhất cho việc này là gì và động cơ hiện đại xử lý việc này như thế nào?


Một cách tiếp cận tương tự cũng được sử dụng bởi Source Engine của Valve. Bạn có thể tìm thấy một số nghiên cứu hữu ích với một số google-fu.
akaltar

Câu trả lời:


8

Thực hành tốt nhất:

  • Một vòng lặp trung tâm trong luồng chính / kết xuất cũng xử lý âm thanh, bộ đệm mạng, v.v. - về cơ bản, điều này tập trung vào giao tiếp với HĐH và các luồng khác.
  • Tất cả các tác vụ chuyên sâu của bộ xử lý (ví dụ: xây dựng lưới, AI, vật lý) có thể được gửi đặc biệt, trong các đơn vị công việc có kích thước cắn, đến các luồng công nhân hiện có. Các chủ đề này được giữ sống và tái sử dụng nhiều lần cho các nhiệm vụ khác nhau. Một mô hình thực hiện điển hình là Thread Pooling .

Bạn có thể thấy các ví dụ về điều này trong các nền tảng hiện đại như Unity (WorkerThread), Xamarin (ThreadPool) và HTML5 (WebWorker) và thậm chí trong công nghệ cũ hơn như Java 6 (các cách tiếp cận khác nhau).

Nếu bạn thấy bạn đang chờ đợi rất nhiều trên mutexes, có lẽ bạn cần phải cơ cấu lại mọi thứ. Cố gắng song song các tác vụ của bạn sao cho chỉ một luồng truy cập vào một phần bộ nhớ nhất định tại một thời điểm nhất định. Ví dụ: nếu một luồng công nhân liên tục làm việc với các tác vụ chiếu sáng động có kích thước cắn, trong khi một luồng khác đang hoạt động trên AI, thì bạn không thể có xung đột lợi ích. Ngoài ra, có 2 luồng công nhân trên AI, nhưng hãy để chúng tiêu thụ các đơn vị công việc không ảnh hưởng đến kết quả của nhau.

Và có, kết xuất nên được liên tục, bất kể. Trong vòng lặp chính, trước khi kết xuất, sao chép kết quả của bất kỳ tác vụ luồng công nhân đã hoàn thành nào vào mô hình dữ liệu chính của bạn nếu chúng hoàn thành ( xem bên dưới ). Nhưng đừng cố đọc cùng một dữ liệu mô hình từ trình kết xuất, mà bạn hiện đang đọc / ghi trong các luồng công nhân. Đó rõ ràng là một công thức cho thảm họa.

Tôi đặc biệt không khuyến khích việc sử dụng nhiều vòng lặp mà bạn sẽ cần phải đồng bộ hóa định kỳ - thực tế nó chỉ giống như một công thức giảm đau.

Biết khi nào một đơn vị công việc hoàn thành & đồng bộ hóa với vòng lặp chính

Cơ chế chính xác để thông báo khi một công việc trên một luồng khác đã hoàn thành tùy thuộc vào cấu hình của bạn: nền tảng, ngôn ngữ, môi trường luồng. Ngay cả trong C ++, có nhiều môi trường luồng khác nhau như pthreads, luồng OpenMP, v.v. Bạn sẽ được thông báo bằng một số loại gọi lại, hoặc bạn sẽ phải thăm dò một số điều kiện từ vòng lặp chính để kiểm tra xem mỗi đơn vị công việc đã hoàn thành chưa. Tìm kiếm cơ chế được sử dụng trong môi trường luồng của bạn. Tôi đề nghị tìm kiếm và chạy một số ví dụ đơn giản đầu tiên, triệt để grok khái niệm luồng. Nếu bạn muốn đi tiêu chuẩn, hãy đi pthreads, nhưng hiểu rằng đó là một thư viện luồng rất thấp. PS Nếu bạn đang sử dụng pthreads, cuốn sách Thiết kế công cụ trò chơi đa luồng, phiên bản 1, xử lý việc này ở trang 84 trở đi ... bạn sẽ có thể tìm thấy bản xem trước trên sách google.


Nhưng làm thế nào để tôi đồng bộ hóa các chủ đề với vòng lặp chính?
Liess Jemai

1
@LiessJemai ngầm với các nhiệm vụ được gửi và hoàn thành
ratchet freak

@LiessJemai ratchet freak là chính xác; giải thích đầy đủ hơn ở trên.
Kỹ sư

Là câu trả lời của tôi dưới đây là một cách để đồng bộ hóa các chủ đề này?
Liess Jemai
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.