Làm thế nào để chúng tôi giải quyết các yêu cầu bộ nhớ video lớn trong một trò chơi 2D?


40

Làm thế nào để chúng tôi giải quyết các yêu cầu bộ nhớ video lớn trong một trò chơi 2D?


Chúng tôi đang phát triển một trò chơi 2D (Factorio) trong allegro C / C ++ và chúng tôi đang phải đối mặt với một vấn đề với việc tăng yêu cầu bộ nhớ video khi nội dung trò chơi tăng lên.

Chúng tôi hiện đang thu thập tất cả thông tin về các hình ảnh sẽ được sử dụng trước tiên, cắt tất cả các hình ảnh này càng nhiều càng tốt và sắp xếp chúng thành các tập lớn nhất càng chặt chẽ càng tốt. Các atlase này được lưu trữ trong bộ nhớ video, kích thước phụ thuộc vào các giới hạn hệ thống; hiện tại nó thường là 2 hình ảnh có kích thước lên tới 8192x8192, vì vậy chúng cần bộ nhớ video 256Mb đến 512Mb.

Hệ thống này hoạt động khá tốt đối với chúng tôi, vì với một số tối ưu hóa tùy chỉnh và chia luồng kết xuất và cập nhật, chúng tôi có thể vẽ hàng chục ngàn hình ảnh trên màn hình trong 60 khung hình / giây; chúng tôi có nhiều đối tượng trên màn hình và cho phép thu nhỏ lớn là một yêu cầu quan trọng. Như chúng tôi muốn bổ sung thêm, sẽ có một số rắc rối với các yêu cầu bộ nhớ video, vì vậy hệ thống này không thể giữ được.

Một trong những điều chúng tôi muốn thử là có một tập bản đồ với những hình ảnh phổ biến nhất và thứ hai là bộ đệm. Các hình ảnh sẽ được chuyển đến đó từ bitmap bộ nhớ, theo yêu cầu. Có hai vấn đề với cách tiếp cận này:

  1. Việc vẽ từ bitmap bộ nhớ sang bitmap video rất chậm, trong allegro.
  2. Không thể làm việc với bitmap video ngoài luồng chính, trong allegro, vì vậy nó thực tế không thể sử dụng được.

Dưới đây là một số yêu cầu bổ sung mà chúng tôi có:

  • Trò chơi phải có tính xác định, vì vậy các vấn đề về hiệu suất / thời gian tải không bao giờ có thể thay đổi trạng thái trò chơi.
  • Trò chơi là thời gian thực, và sẽ sớm được nhiều người chơi. Chúng ta cần tránh ngay cả những người nói lắp nhỏ nhất bằng mọi giá.
  • Hầu hết các trò chơi là một thế giới mở liên tục.

Thử nghiệm bao gồm vẽ 10 000 sprite trong một lô cho các kích thước từ 1x1 đến 300x300, nhiều lần cho mỗi cấu hình. Tôi đã thực hiện các thử nghiệm trên Nvidia Geforce GTX 760.

  • Bản đồ bitmap video sang bản vẽ bitmap video mất 0,1us mỗi sprite, khi bitmap nguồn không thay đổi giữa các bitmap riêng lẻ (biến thể atlas); kích thước không thành vấn đề
  • Bản đồ bitmap video sang bản vẽ bitmap video, trong khi bitmap nguồn được chuyển đổi giữa các bản vẽ (biến thể không có bản đồ), lấy 0,56us mỗi sprite; kích thước cũng không thành vấn đề.
  • Bộ nhớ bitmap để vẽ bitmap video thực sự đáng ngờ. Kích thước từ 1x1 đến 200x200 mất 0,3us mỗi bitmap, do đó không quá chậm. Đối với kích thước lớn hơn, thời gian bắt đầu tăng rất đột ngột, ở mức 9us cho năm 201x201 lên tới 3116us cho 291x291.

Sử dụng tập bản đồ giúp tăng hiệu suất lên gấp 5 lần. Nếu tôi có 10ms cho kết xuất, với tập bản đồ tôi bị giới hạn ở 100 000 họa tiết trên mỗi khung và không có nó, giới hạn là 20 000 họa tiết. Đây sẽ là vấn đề.

Tôi cũng đã cố gắng tìm cách kiểm tra nén bitmap và định dạng bitmap 1bpp cho bóng, nhưng tôi không thể tìm ra cách để làm điều này trong allegro.


1
Fan hâm mộ lớn của trò chơi của bạn, tôi ủng hộ chiến dịch Indiegogo. Tôi cứ say sưa vài tháng một lần. Làm tốt lắm Tôi đã xóa các câu hỏi "sử dụng công nghệ nào" ngoài chủ đề cho trang web. Các câu hỏi còn lại vẫn còn khá rộng, nếu bạn có bất cứ điều gì cụ thể hơn, bạn nên cố gắng thu hẹp phạm vi.
MichaelHouse

Cảm ơn về sự hỗ trợ. Vậy đâu là nơi để hỏi nên sử dụng công nghệ nào? Tôi không tìm kiếm câu trả lời với khuyến nghị cụ thể về động cơ, nhưng tôi không thể tìm thấy so sánh sâu hơn về động cơ 2d và kiểm tra từng cái một cách thủ công bao gồm các bài kiểm tra hiệu suất và khả năng sử dụng sẽ mất nhiều thời gian.
Marwin

Kiểm tra dưới cùng của trang này để biết một số nơi để đặt câu hỏi như "sử dụng công nghệ nào". Bạn có một câu hỏi hoàn toàn hợp lệ và hợp lý, đó không phải là loại câu hỏi chúng tôi giải quyết tại trang web này. Mặc dù bạn không tìm kiếm một công cụ cụ thể, nhưng đó thực sự là cách duy nhất để trả lời câu hỏi "Có công nghệ nào làm X không?". Ai đó chỉ có thể trả lời "có" và không đưa ra khuyến nghị cho một người cụ thể, nhưng điều đó sẽ không hữu ích. Chúc may mắn với điều đó!
MichaelHouse

2
Bạn đang nén kết cấu của bạn?
GuyRT

3
@Marwin, Hoạ tiết nén có thể hoạt động tốt hơn nhiều so với kết cấu không nén vì chúng làm giảm băng thông bộ nhớ cần thiết (điều này đặc biệt đúng trên nền tảng di động nơi băng thông thấp hơn nhiều). Bạn có thể tiết kiệm một lượng lớn bộ nhớ chỉ bằng cách nén họa tiết của mình. Thực sự, nhược điểm duy nhất là các hiện vật được giới thiệu chắc chắn.
GuyRT

Câu trả lời:


17

Chúng tôi có một trường hợp tương tự với RTS của chúng tôi (KaM Remake). Tất cả các đơn vị và nhà ở là sprite. Chúng tôi có 18 000 họa tiết cho các đơn vị và nhà ở và địa hình, cộng thêm ~ 6 000 cho màu đội (áp dụng làm mặt nạ). Kéo dài chúng ta cũng có khoảng ~ 30 000 ký tự được sử dụng trong các phông chữ.

Vì vậy, có một số tối ưu hóa chống lại RGBA32 mà bạn đang sử dụng:

  • Trước tiên, chia nhóm sprite của bạn thành nhiều tập nhỏ hơn và sử dụng chúng theo yêu cầu như được nêu trong các câu trả lời khác. Điều đó cũng cho phép sử dụng các kỹ thuật tối ưu hóa khác nhau cho từng tập bản đồ . Tôi nghi ngờ bạn sẽ có một ít RAM lãng phí, vì khi đóng gói vào một kết cấu khổng lồ như vậy thường có các khu vực không được sử dụng ở phía dưới;

  • Hãy thử sử dụng kết cấu paletted . Nếu bạn sử dụng shader, bạn có thể "áp dụng" bảng màu trong mã shader;

  • Bạn có thể xem xét thêm một tùy chọn để sử dụng RGB5_A1 thay vì RGBA8 (ví dụ: nếu bóng của bảng kiểm tra là ổn cho trò chơi của bạn). Tránh Alpha 8 bit khi có thể và sử dụng RGB5_A1 hoặc các định dạng tương đương với độ chính xác nhỏ hơn (tương tự RGBA4), chúng chiếm một nửa không gian;

  • Hãy chắc chắn rằng bạn đang đóng gói chặt các sprite thành atlours (xem thuật toán Bin Đóng gói), xoay các sprite khi cần thiết và xem liệu bạn có thể chồng lên các góc trong suốt cho các sprite hình thoi;

  • Bạn có thể thử các định dạng nén phần cứng (DXT, S3TC, v.v.) - chúng có thể giảm đáng kể việc sử dụng RAM, nhưng kiểm tra các thành phần nén - trên một số hình ảnh có thể không nhận thấy sự khác biệt (bạn có thể sử dụng điều này một cách chọn lọc như được mô tả trong điểm đầu tiên), nhưng trên một số - rất phát âm. Các định dạng nén khác nhau gây ra các tạo tác khác nhau, vì vậy bạn có thể chọn một định dạng phù hợp nhất với phong cách nghệ thuật của mình.

  • Nhìn vào việc chia các sprite lớn (tất nhiên không phải bằng tay, nhưng trong trình đóng gói atlas kết cấu của bạn) thành sprite nền tĩnh và các sprite nhỏ hơn cho các phần hoạt hình.


2
+1 để sử dụng DXT, đó là một điều rất tốt để có. Nén tuyệt vời và được sử dụng trực tiếp bởi GPU nên chi phí tối thiểu.

1
Tôi đồng ý với dxt. Bạn cũng có thể truy vấn hỗ trợ DXT7 (phần cứng DX11 +), có cùng kích thước với DXT1 nhưng (rõ ràng) chất lượng cao hơn. Tuy nhiên, bạn sẽ cần phải có gấp đôi kết cấu (một DXT7 và một DXT1) hoặc nén / giải nén trong khi tải.
Chương trình

5

Trước hết bạn cần sử dụng nhiều hơn, các kết cấu nhỏ hơn. Càng ít kết cấu bạn càng có nhiều quản lý bộ nhớ cứng và cứng hơn. Tôi sẽ đề xuất kích thước tập bản đồ là 1024, trong trường hợp đó bạn sẽ có 128 kết cấu thay vì 2 hoặc 2048 trong trường hợp đó bạn sẽ có 32 kết cấu, bạn có thể tải và dỡ tải khi cần.

Hầu hết các trò chơi thực hiện việc quản lý tài nguyên này bằng cách có ranh giới cấp độ, trong khi màn hình tải được hiển thị tất cả các tài nguyên không còn cần thiết ở cấp độ tiếp theo sẽ được tải và tài nguyên cần được tải.

Một tùy chọn khác là tải theo yêu cầu, trở nên cần thiết nếu ranh giới cấp độ không mong muốn hoặc thậm chí một cấp độ quá lớn để phù hợp với bộ nhớ. Trong trường hợp này, trò chơi sẽ cố gắng dự đoán những gì người chơi sẽ thấy trong tương lai và tải nó ở chế độ nền. (Ví dụ: nội dung hiện cách người chơi 2 màn hình.) Đồng thời, những thứ không được sử dụng nữa trong thời gian dài hơn sẽ bị hủy tải.

Tuy nhiên, có một vấn đề xảy ra khi điều gì đó bất ngờ xảy ra mà trò chơi không thể thấy trước?

  • Hoảng loạn và hiển thị màn hình tải cho đến khi tất cả những thứ cần thiết được tải. Điều này có thể cảm thấy gây rối cho trải nghiệm.
  • Có các sprite độ phân giải thấp cho mọi thứ được tải sẵn, tiếp tục trò chơi và thay thế chúng ngay khi các sprite độ phân giải cao tải xong. Điều này có thể trông rẻ tiền cho người chơi.
  • Làm cho nó tác động đến trò chơi và trì hoãn sự kiện miễn là cần thiết. Ví dụ: không sinh ra kẻ thù cho đến khi đồ họa của nó được tải. Đừng mở rương kho báu đó trước khi tất cả đồ họa cho chiến lợi phẩm đó được tải, v.v.

Tôi đã thêm một số yêu cầu mà tôi đã bỏ qua. Màn hình tải, hoặc bất kỳ loại tải nào là không thể. Tất cả mọi thứ phải được thực hiện ở chế độ nền hoặc xen giữa các tích tắc riêng lẻ (ít hơn 15ms cho mỗi lần) trong khi hầu hết thời gian thường được sử dụng để chuẩn bị kết xuất và cập nhật trò chơi. Dù sao, việc chia thành các phần nhỏ hơn có thể thêm một số linh hoạt trong việc chuyển đổi, chắc chắn sẽ nhanh hơn. Câu hỏi đặt ra là nó ảnh hưởng đến hiệu năng như thế nào khi kết xuất, khi chuyển bitmap nguồn trong khi vẽ làm chậm kết xuất. Tôi sẽ phải thực hiện đo lường chính xác để nói bao nhiêu.
Marwin

@Marwin Tác động hiệu suất, vâng, nhưng vì bạn đang xử lý 2D, nên vẫn còn rất xa để nó trở thành một vấn đề. Nếu kết xuất hiện mất 1ms cho mỗi khung hình và thông qua việc sử dụng các kết cấu nhỏ hơn thì đột nhiên mất 2ms, điều đó vẫn đủ nhanh để đạt được 60 FPS nhất quán. (16ms)
API-Beast

@Marwin Nhiều người chơi là khó khăn, luôn luôn, sẽ luôn luôn như vậy. Bạn rất có thể sẽ phải thỏa hiệp ở đó. Bạn sẽ gặp vấn đề, đơn giản là vì bạn phải truyền dữ liệu qua internet, các gói sẽ bị mất, ping có thể đột ngột tăng vọt, v.v. Nói lắp là không thể tránh khỏi, vì vậy điều quan trọng hơn là làm cho mô hình mạng có khả năng chống nhiễu. Biết khi nào nên chờ đợi & làm thế nào để chờ đợi người chơi khác.
API-Beast

Xin chào, nói lắp là gần như có thể tránh được trong nhiều người chơi, chúng tôi đang làm việc trên khu vực đó ngay bây giờ và tôi tin rằng chúng tôi có một kế hoạch tốt. Tôi thậm chí có thể đăng và trả lời câu hỏi của riêng mình mô tả những gì chúng tôi đã nghiên cứu chi tiết sau này :) Nó có thể gây ngạc nhiên, nhưng thời gian kết xuất thực sự là một vấn đề. Chúng tôi đã thực hiện rất nhiều tối ưu hóa để làm cho kết xuất nhanh hơn. Kết xuất chính bây giờ được thực hiện trong luồng riêng biệt và các điều chỉnh nhỏ khác. Đừng quên rằng, khi phóng to tối đa, người chơi có thể dễ dàng nhìn thấy hàng chục ngàn họa tiết cùng một lúc. Và chúng tôi thậm chí muốn cho phép các mức thu phóng cao hơn sau này.
Marwin

@Marwin Hừm, các đối tượng 10k thường không phải là vấn đề đối với PC hoặc máy tính xách tay hiện đại nếu bạn sử dụng theo đợt thích hợp, bạn đã lập hồ sơ mã kết xuất của mình chưa?
API-Beast

2

Ồ, đó là một số lượng lớn các họa tiết hoạt hình, được tạo ra từ các mô hình 3D mà tôi đoán?

Bạn thực sự không nên làm trò chơi này ở dạng 2D thô. Khi bạn có phối cảnh cố định, một điều thú vị xảy ra, bạn có thể trộn liền mạch các họa tiết và hình nền được kết xuất sẵn với các mô hình 3D được kết xuất trực tiếp, được một số trò chơi sử dụng rất nhiều. Nếu bạn muốn những hình ảnh động đẹp như vậy có vẻ như là cách tự nhiên nhất để làm điều đó. Nhận một công cụ 3D, định cấu hình nó để sử dụng phối cảnh đẳng cự và hiển thị các đối tượng mà bạn tiếp tục sử dụng các họa tiết dưới dạng các bề mặt phẳng đơn giản với hình ảnh trên đó. Và bạn có thể sử dụng nén kết cấu với công cụ 3D, một mình đó là một bước tiến lớn.

Tôi không nghĩ việc tải và dỡ sẽ giúp ích rất nhiều cho bạn vì bạn có thể có khá nhiều thứ trên màn hình cùng một lúc.


2

Trước tiên hãy tìm định dạng kết cấu hiệu quả nhất mà bạn vẫn có thể hài lòng với hình ảnh của trò chơi cho dù đây là RGBA4444 hay nén DXT, v.v. Nếu bạn không hài lòng với các tạo tác được tạo thành hình ảnh nén DXT alpha, nó có khả thi không để làm cho hình ảnh không trong suốt bằng cách sử dụng nén DXT1 cho màu kết hợp với kết cấu mặt nạ thang độ xám 4 hoặc 8 bit cho alpha? Tôi tưởng tượng bạn sẽ ở lại RGBA8888 cho GUI.

Tôi ủng hộ việc phá vỡ mọi thứ thành các kết cấu nhỏ hơn bằng cách sử dụng bất kỳ định dạng nào bạn quyết định. Xác định các mục luôn ở trên màn hình và do đó luôn được tải, đây có thể là bản đồ địa hình và GUI. Sau đó tôi sẽ chia nhỏ các mục còn lại thường được kết xuất với nhau càng nhiều càng tốt. Tôi không tưởng tượng bạn sẽ mất quá nhiều hiệu suất thậm chí lên tới 50 - 100 cuộc gọi rút tiền trên PC nhưng hãy sửa tôi nếu tôi sai.

Bước tiếp theo sẽ là tạo các phiên bản mipmap của các kết cấu này như ai đó đã chỉ ra ở trên. Tôi sẽ không lưu trữ chúng trong một tập tin duy nhất mà riêng biệt. Vì vậy, bạn sẽ kết thúc với các phiên bản 1024x1024, 512x512, 256x256 vv của mỗi tệp và tôi sẽ làm điều này cho đến khi tôi đạt được mức độ chi tiết thấp nhất mà tôi muốn được hiển thị.

Bây giờ bạn có các kết cấu riêng biệt, bạn có thể xây dựng một hệ thống mức độ chi tiết (LOD) để tải kết cấu cho mức thu phóng hiện tại và hủy tải kết cấu nếu không được sử dụng. Không sử dụng kết cấu nếu mục được hiển thị không xuất hiện trên màn hình hoặc không được yêu cầu bởi mức thu phóng hiện tại. Cố gắng tải các kết cấu vào RAM video trong một luồng riêng biệt cho các luồng cập nhật / kết xuất. Bạn có thể hiển thị kết cấu LOD thấp nhất cho đến khi yêu cầu được tải. Điều này đôi khi có thể dẫn đến một chuyển đổi có thể nhìn thấy giữa một chi tiết thấp / kết cấu chi tiết cao nhưng tôi tưởng tượng điều này sẽ chỉ xảy ra khi bạn thực hiện thu phóng cực nhanh và trong khi di chuyển trên bản đồ. Bạn có thể làm cho hệ thống trở nên thông minh bằng cách cố gắng tải trước nơi bạn nghĩ rằng người đó sẽ di chuyển hoặc phóng to và tải càng nhiều càng tốt trong các ràng buộc bộ nhớ hiện tại.

Đó là loại điều tôi sẽ kiểm tra xem nó có giúp ích gì không. Tôi tưởng tượng để có được mức thu phóng cực cao, chắc chắn bạn sẽ cần một hệ thống LOD.


1

Tôi tin rằng cách tiếp cận tốt nhất là chia kết cấu trong nhiều tệp và tải chúng theo yêu cầu. Có lẽ vấn đề của bạn là bạn đang cố tải các kết cấu lớn hơn mà bạn sẽ cần cho một cảnh 3D hoàn chỉnh và bạn đang sử dụng Allegro cho điều đó.

Để thu nhỏ lớn mà bạn muốn có thể áp dụng, bạn phải sử dụng mipmap. Mipmaps là phiên bản có độ phân giải thấp hơn về kết cấu của bạn, được sử dụng khi các đối tượng cách máy ảnh đủ xa. Điều này có nghĩa là bạn có thể lưu 8192x8192 của mình dưới dạng 4096x4096 và sau đó là 2048x2048, v.v. và bạn chuyển sang độ phân giải thấp hơn khi bạn nhìn thấy sprite nhỏ hơn trên màn hình. Bạn có thể lưu chúng dưới dạng kết cấu riêng biệt hoặc thay đổi kích thước chúng khi tải (nhưng tạo mipmap trong thời gian chạy sẽ tăng thời gian tải cho trò chơi của bạn).

Một hệ thống quản lý phù hợp sẽ tải các tệp yêu cầu theo yêu cầu và giải phóng tài nguyên khi không có ai sử dụng chúng, cộng với những thứ khác. Quản lý tài nguyên là một chủ đề quan trọng trong phát triển trò chơi và bạn đang giảm việc quản lý của mình thành một ánh xạ tọa độ đơn giản thành một kết cấu duy nhất, gần như không có sự quản lý nào cả.


1
Bằng cách chia thành các tập tin, bạn có nghĩa là các tập tin trên ổ cứng? Tôi giả sử rằng tôi có thể lưu trữ tất cả các hình ảnh trên RAM cho người mới bắt đầu và thậm chí việc sao chép từ bitmap bộ nhớ sang video-bitmap hiện quá chậm, do đó việc tải từ ổ cứng chắc chắn thậm chí còn chậm hơn. Có những cái nhìn thoáng qua sẽ không giúp tôi, vì tôi vẫn sẽ có độ phân giải lớn nhất trong vram.
Marwin

Vâng, bạn không phải tải mọi thứ, bạn chỉ phải tải những gì bạn sử dụng. Bất cứ khi nào bạn muốn thay đổi pixel trên kết cấu được tải trong VRAM, hệ thống phải di chuyển KẾT QUẢ vào RAM, chỉ để bạn sửa đổi một pixel, chuyển nó trở lại VRAM. Nếu bạn có mọi thứ trong một kết cấu duy nhất, điều này liên quan đến việc chuyển 256 MB sang RAM rồi quay lại VRAM, khóa toàn bộ máy tính. Có nó tách trong các tập tin và kết cấu khác nhau là cách làm chính xác.
Pablo Ariel

Việc sửa đổi kết cấu kích hoạt sao chép vào bộ nhớ và quay lại ram chỉ áp dụng cho các ảnh bitmap liên tục, bộ đệm có thể sẽ không được đặt thành liên tục, nhược điểm duy nhất là cần phải làm mới khi mất / tìm thấy màn hình. Nhưng trong allegro, ngay cả bản sao đơn giản của hình ảnh 640X480 từ vram sang bitmap bộ nhớ (lưu bản xem trước trò chơi) cũng mất khá nhiều thời gian.
Marwin

1
Tôi cần phải có mọi thứ trong một kết cấu lớn để tối ưu hóa bản vẽ, mà không có nó, hiệu ứng chuyển ngữ cảnh giữa các họa tiết riêng lẻ làm chậm quá trình kết xuất quá ít nhất là trong allegro. Đừng hiểu sai ý tôi, nhưng bạn là một đội trưởng rõ ràng ở đây, vì bạn đang mơ hồ đề nghị tôi làm điều gì đó tôi yêu cầu trong câu hỏi này.
Marwin

1
Có các kết cấu được ánh xạ mip này trong các tệp khác nhau sẽ buộc tôi tải lại tất cả các tập bản đồ khi người chơi phóng to. Vì động cơ chỉ có vài đơn vị ms cho nó, tôi không thấy cách nào để làm điều đó.
Marwin

0

Tôi khuyên bạn nên tạo nhiều tệp atlas có thể được nén bằng zlib và truyền ra khỏi nén cho mỗi tập bản đồ, và bằng cách có nhiều tệp atlas hơn và các tệp có kích thước nhỏ hơn, bạn có thể hạn chế lượng dữ liệu hình ảnh hoạt động trong bộ nhớ video. Ngoài ra, hãy thực hiện cơ chế bộ đệm ba để bạn chuẩn bị trước mỗi khung vẽ sớm hơn và có cơ hội hoàn thành nhanh hơn để các ngăn không xuất hiện trên màn hình.

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.