CPU - Luồng dữ liệu bộ nhớ GPU [đã đóng]


16

Tôi là một lập trình viên đồ họa người mới và tôi đã tự hỏi gần đây - làm thế nào để dữ liệu mô hình (lưới và vật liệu) truyền từ ứng dụng (bộ nhớ CPU) sang card đồ họa (bộ nhớ GPU?)? Giả sử tôi có một mô hình tĩnh (ví dụ: tòa nhà) mà tôi tải và thiết lập một lần và không thay đổi trong suốt tuổi thọ của ứng dụng.

  • Dữ liệu của nó có được gửi đến bộ nhớ GPU chỉ một lần và ngồi đó mãi mãi không?
  • Khi mô hình thực sự được hiển thị mỗi khung hình, bộ xử lý GPU có phải tìm nạp dữ liệu của nó mỗi lần từ bộ nhớ GPU không? Ý tôi là - nếu tôi có 2 mô hình được kết xuất nhiều lần mỗi lần - sẽ là vấn đề nếu lần đầu tiên tôi kết xuất lần đầu tiên nhiều lần và sau đó lần thứ hai nhiều lần hoặc nếu tôi kết xuất lần đầu tiên chỉ một lần, lần thứ hai chỉ một lần và cứ xen kẽ như thế? Tôi có thể gọi câu hỏi này là "luồng dữ liệu GPU nội bộ" theo nghĩa này.
  • Rõ ràng các card đồ họa có RAM hạn chế - khi nó không thể chứa tất cả dữ liệu mô hình cần thiết để hiển thị 1 khung hình, tôi đoán nó tiếp tục tìm nạp (một số) nó từ RAM CPU mỗi khung hình, điều đó có đúng không?

Tôi biết có rất nhiều sách và nội dung về điều này trên internet nhưng có lẽ bạn có một số hướng dẫn chung nhanh về cách quản lý luồng dữ liệu này (khi nào nên gửi cái gì và bao nhiêu, khi nào và làm thế nào để kết xuất)?

Chỉnh sửa: Tôi quên tạo một điểm khác biệt: có gửi dữ liệu tới GPU và có cài đặt / ràng buộc bộ đệm như hiện tại . Liệu cái sau gây ra bất kỳ luồng dữ liệu?

Edit2: Sau khi đọc bài đăng của Raxvan, tôi muốn phân biệt một vài hành động:

  • tạo bộ đệm với khởi tạo (như ông nói tôi có thể lưu trữ dữ liệu trong CPU ram hoặc GPU)
  • cập nhật dữ liệu bộ đệm (mà tôi tin là đơn giản khi dữ liệu được lưu trong ram CPU và yêu cầu tìm nạp từ GPU sang ram CPU (và sau đó quay lại) khi nó được giữ trong ram GPU)
  • ràng buộc bộ đệm là hoạt động (nó chỉ là một cách để nói với API rằng tôi muốn bộ đệm này được hiển thị trong lệnh gọi tiếp theo và nó không tự làm gì cả ?)
  • Cuộc gọi rút thăm API (ở đây tôi muốn nghe từ bạn những gì thực sự xảy ra ở đó)

Tôi không phải là chuyên gia, nhưng nếu bạn đang sử dụng OpenGL hiện đại (tức là không phải ngay lập tức) với VAO và VBO, thì dữ liệu sẽ được gửi đến GPU và được lưu trữ trong VRAM bất cứ khi nào bạn sử dụng một trong các dòng lệnh glBuffer. Sau đó, mỗi khi bạn vẽ nó, các đỉnh có liên quan sẽ được tìm nạp từ VRAM và được hiển thị. Nếu đó là một mô hình di chuyển, bạn có xu hướng lưu trữ tĩnh và sử dụng ma trận để di chuyển từ không gian mô hình sang không gian thế giới / máy ảnh. Về điểm cuối cùng, tôi không biết chuyện gì sẽ xảy ra nếu bạn hết RAM. Tôi đoán là nếu bạn hết VRAM, thì dữ liệu sẽ không được gửi, có thể có mã lỗi.
Cực

@Polar - không chính xác. GL không thực sự chỉ định bộ nhớ mà đối tượng bộ đệm được lưu trữ và thậm chí có thể tự do di chuyển nó trong thời gian chạy dựa trên mẫu sử dụng. GL4.4 giải quyết vấn đề này phần nào, nhưng lưu ý rằng cuối cùng, thứ tốt nhất nó có thể cung cấp là "một trong những điều gợi ý ngớ ngẩn"; xem opengl.org/registry/specs/ARB/buffer_st Storage.txt và đặc biệt là các vấn đề 2 và 9.
Maximus Minimus

1
@JimmyShelter Ah, cảm ơn - thật tuyệt nếu chúng ta có ít "những điều gợi ý ngớ ngẩn" đó và một đặc điểm kỹ thuật cụ thể hơn.
Cực

@Polar - Điều khó chịu là ARB_buffer_st Storage có thể tránh được kể cả một gợi ý khác, nhưng các nhà thiết kế đã bỏ lỡ cơ hội. Oh tốt, có lẽ 4,5 cuối cùng sẽ làm cho nó đúng.
Maximus Minimus

2
Vui lòng không chỉnh sửa câu hỏi của bạn để "trả lời" cho câu trả lời. Gửi một câu hỏi mới thay thế.

Câu trả lời:


12

Dữ liệu của nó có được gửi đến bộ nhớ GPU chỉ một lần và ngồi đó mãi mãi không?

Thông thường là có, nhưng trình điều khiển có thể tự do làm những gì "tối ưu", dữ liệu có thể được lưu trữ tại VRAM hoặc RAM hoặc có thể được lưu trong bộ nhớ cache ở đây là một giải thích cho những gì thực sự xảy ra với luồng VBO .

Ví dụ: nếu nó được gắn cờ là bộ đệm openGL động (ví dụ VBO), nhiều khả năng nó sẽ được lưu trữ trong RAM. GPU sử dụng truy cập bộ nhớ trực tiếp (DMA) để truy cập ram trực tiếp mà không cần sự can thiệp của CPU, điều này được điều khiển bởi bộ điều khiển DMA trong card đồ họa và trình điều khiển đồ họa và được thực thi trong chế độ kernel.

Khi mô hình thực sự được hiển thị mỗi khung hình, bộ xử lý GPU phải lấy dữ liệu của nó mỗi lần từ bộ nhớ GPU, ngay cả khi một mô hình được hiển thị nhiều lần liên tiếp?

Cũng giống như CPU, GPU được phép sắp xếp lại các hướng dẫn GPU và các hoạt động truy cập bộ nhớ , (đọc: thực hiện theo thứ tự ) nên rất có thể GPU sẽ xử lý tình huống bạn đã đề cập bằng cách truy cập bộ nhớ trong bộ nhớ cache của nó (thường được truy cập gần đây ), nhưng đôi khi nó không thể làm điều này.

Rõ ràng các card đồ họa có RAM hạn chế - khi nó không thể chứa tất cả dữ liệu mô hình cần thiết để hiển thị 1 khung hình, tôi đoán nó tiếp tục tìm nạp (một số) nó từ RAM CPU mỗi khung hình, điều đó có đúng không?

Bạn không muốn điều này xảy ra. Nhưng bất kể điều đó xảy ra, GPU sẽ bắt đầu di chuyển bộ nhớ giữa RAM và VRAM (bộ xử lý lệnh trên GPU chịu trách nhiệm cho việc này), điều này sẽ khiến việc hiển thị chậm hơn nhiều, điều này sẽ khiến GPU bị đình trệ, bởi vì nó sẽ phải chờ dữ liệu được sao chép từ / đến V / RAM.

Có gửi dữ liệu tới GPU và có cài đặt / ràng buộc bộ đệm như hiện tại. Liệu cái sau gây ra bất kỳ luồng dữ liệu?

GPU chứa bộ đệm lệnh và tất cả các lệnh API được gửi đến bộ đệm này, lưu ý rằng điều này có thể xảy ra đồng thời với dữ liệu được sao chép vào GPU. Bộ đệm vòng lệnh là một hàng đợi giao tiếp giữa CPU và GPU , bất kỳ lệnh nào cần được thực thi cần phải được gửi đến hàng đợi để GPU có thể được thực thi. Giống như bất kỳ hoạt động nào ràng buộc bộ đệm mới cần phải được gửi đến gpu để nó có thể truy cập một số vị trí bộ nhớ.

Đó là một trong những lý do khiến glBegin / glEnd không được chấp nhận, việc gửi các lệnh mới cần đồng bộ hóa hàng đợi (sử dụng hàng rào / rào cản bộ nhớ).

nhập mô tả hình ảnh ở đây

Đối với các điểm khác của bạn:

Tạo bộ đệm với khởi tạo

Bạn có thể phân bổ một bộ đệm mà không cần khởi tạo và giữ nó để sử dụng sau. Hoặc bạn có thể phân bổ cho nó một bộ đệm và sao chép dữ liệu cùng một lúc (nói về cấp độ API).

cập nhật dữ liệu đệm

Bạn có thể sử dụng glMapBuffer để cập nhật bộ nhớ ở phía GPU. liệu bộ nhớ sẽ được sao chép từ / sang RAM không thực sự là tiêu chuẩn và sẽ thay đổi rất nhiều tùy thuộc vào Nhà cung cấp, loại GPU và trình điều khiển.

Cuộc gọi rút thăm API (ở đây tôi muốn nghe từ bạn những gì thực sự xảy ra ở đó).

Điểm thứ hai của tôi trong câu hỏi chính bao gồm điều này.

ràng buộc bộ đệm là hoạt động (nó chỉ là một cách để nói với API rằng tôi muốn bộ đệm này> được hiển thị trong lệnh gọi tiếp theo và nó không tự làm gì cả?)

Hãy nghĩ về ràng buộc như sử dụng thiscon trỏ trong bất kỳ ngôn ngữ hướng đối tượng nào, mặc dù không hoàn toàn giống nhau, bất kỳ lệnh gọi API hệ quả nào cũng sẽ liên quan đến bộ đệm liên kết đó.


3

Nói chung, ranh giới và sự tham gia của cpu so với gpu là cụ thể trên nền tảng, nhưng hầu hết chúng đều theo mô hình này: cpu có một số ram, gpu cũng và bạn có thể di chuyển bộ nhớ xung quanh (trong một số trường hợp ram được chia sẻ nhưng đối với Vì đơn giản, chúng ta hãy gắn bó với các ram riêng biệt).

Điểm đầu tiên : Dữ liệu bạn khởi tạo bạn có thể chọn giữ nó trong ram CPU hoặc trên ram GPU, và đó là những lợi thế cho cả hai. Khi bạn kết xuất một thứ gì đó, GPU phải thực hiện một công việc nặng nhọc để rõ ràng là dữ liệu đã có trên bộ nhớ GPU sẽ cung cấp hiệu suất tốt hơn. Đối với CPU, trước tiên, nó phải gửi dữ liệu tới GPU (có thể chọn giữ nó trong một thời gian) và sau đó thực hiện kết xuất.

Điểm thứ hai : Có nhiều mẹo trong kết xuất nhưng cách chính được thực hiện là với đa giác. Trên khung gpu sẽ hiển thị từng đối tượng được tạo từ các đa giác và sau khi hoàn thành, GPU sẽ gửi hình ảnh đến màn hình. Không có khái niệm như các đối tượng, chỉ có các đa giác và cách bạn đặt chúng lại với nhau sẽ tạo ra một hình ảnh. công việc của GPU là chiếu các đa giác đó từ 3d lên 2d và áp dụng hiệu ứng (nếu muốn). Đa giác chỉ đi theo hướng CPU-> GPU-> SCREEN hoặc GPU-> SCREEN trực tiếp (nếu đa giác đã có trong ram GPU)

Điểm thứ ba : Ví dụ, khi bạn kết xuất hình ảnh động, tốt hơn là giữ dữ liệu gần với cpu vì anh ta thực hiện công việc nặng nhọc, sẽ không tối ưu để giữ dữ liệu trong GPU, di chuyển dữ liệu sang CPU và quay lại mọi khung hình. Có rất nhiều ví dụ khác như thế này để đếm nhưng nói chung tất cả dữ liệu sẽ ở gần với bất cứ ai đang thực hiện các tính toán. Thông thường, bạn sẽ muốn di chuyển càng nhiều dữ liệu càng tốt sang ram GPU để đạt được hiệu suất.

Việc gửi dữ liệu thực tế đến gpu được thực hiện bởi API bạn sử dụng (directx / opengl hoặc khác) và khái niệm ràng buộc và những thứ như thế này chỉ là trừu tượng để API hiểu những gì bạn muốn làm.

Chỉnh sửa để chỉnh sửa:

  • buffer creation with initialisation: nó giống như sự khác biệt giữa int a = new int[10]a[0] = 0,a[1] = 1.... etc khi bạn tạo bộ đệm, bạn tạo khoảng trống cho dữ liệu và khi bạn khởi tạo dữ liệu bạn đặt nội dung bạn muốn vào đó.

  • buffer data updateNếu nó có trên ram cpu thì bạn có vertex * verticesthể chơi với nó, nếu không có, bạn sẽ phải chuyển nó từ GPU vertex * vertices = map(buffer_id);(bản đồ là một chức năng thần thoại nên chuyển dữ liệu từ GPU sang ram CPU, nó cũng có mặt đối lập buffer_id = create_buffer(vertices);

  • binding the buffer as activeđó chỉ là một khái niệm mà họ gọi bindingkết xuất là một quá trình phức tạp và nó giống như gọi một hàm với 10000 tham số. Binding chỉ là một thuật ngữ họ sử dụng để nói bộ đệm đi đâu. Không có phép thuật thực sự đằng sau thuật ngữ này, nó không chuyển đổi hoặc di chuyển hoặc phân bổ lại bộ đệm, chỉ nói với người lái xe rằng trong cuộc gọi rút thăm tiếp theo sử dụng bộ đệm này.

  • API draw callSau khi tất cả các ràng buộc và thiết lập bộ đệm lên, đây là nơi cao su gặp đường. Cuộc gọi bốc thăm sẽ lấy tất cả dữ liệu (hoặc id trỏ đến dữ liệu) mà bạn đã chỉ định, gửi nó đến GPU (nếu cần) và báo cho GPU bắt đầu bẻ số. Điều này không hoàn toàn đúng trên tất cả các nền tảng có nhiều điểm khác biệt, nhưng để đơn giản, việc rút thăm sẽ cho GPU biết .... vẽ.


2

Câu trả lời đúng nhất là, nó phụ thuộc vào cách bạn lập trình nó, nhưng đây là một điều tốt để lo lắng. Mặc dù GPU đã trở nên cực kỳ nhanh, băng thông đến và từ RAM GPU thì không, và sẽ là nút cổ chai khó chịu nhất của bạn.

Dữ liệu của nó có được gửi đến bộ nhớ GPU chỉ một lần và ngồi đó mãi mãi không?

Có hy vọng. Đối với tốc độ kết xuất, bạn muốn có càng nhiều dữ liệu trên GPU càng tốt, thay vì gửi lại mỗi khung hình. VBO phục vụ mục đích chính xác này. Có cả VBO tĩnh và động, trước đây là tốt nhất cho các mô hình tĩnh và sau là tốt nhất cho các mô hình có các đỉnh sẽ thay đổi mọi khung hình (giả sử, một hệ thống hạt). Tuy nhiên, ngay cả khi nói đến VBO động, bạn không muốn gửi lại tất cả các đỉnh mỗi khung; chỉ là những người đang thay đổi.

Trong trường hợp tòa nhà của bạn, dữ liệu đỉnh sẽ chỉ nằm ở đó và điều duy nhất thay đổi là ma trận của bạn (mô hình / thế giới, hình chiếu và chế độ xem).

Trong trường hợp hệ thống hạt, tôi đã tạo ra một VBO động đủ lớn để lưu trữ số lượng hạt tối đa sẽ tồn tại cho hệ thống đó. Mỗi khung tôi gửi dữ liệu cho các hạt phát ra khung đó, cùng với một vài đồng phục, và đó là tất cả. Khi tôi vẽ, tôi có thể chỉ định điểm bắt đầu và điểm kết thúc trong VBO đó, vì vậy tôi không phải xóa dữ liệu hạt. Tôi chỉ có thể nói đừng vẽ chúng.

Khi mô hình thực sự được hiển thị mỗi khung hình, bộ xử lý GPU có phải tìm nạp dữ liệu của nó mỗi lần từ bộ nhớ GPU không? Ý tôi là - nếu tôi có 2 mô hình được kết xuất nhiều lần mỗi lần - sẽ là vấn đề nếu lần đầu tiên tôi kết xuất lần đầu tiên nhiều lần và sau đó lần thứ hai nhiều lần hoặc nếu tôi kết xuất lần đầu tiên chỉ một lần, lần thứ hai chỉ một lần và cứ xen kẽ như thế?

Hành động gửi nhiều cuộc gọi rút thăm thay vì chỉ một cuộc gọi là một giới hạn lớn hơn nhiều. Kiểm tra kết xuất đồ họa; nó có thể giúp bạn rất nhiều và làm cho câu trả lời cho câu hỏi này trở nên vô dụng. Tôi đã có một số vấn đề về trình điều khiển với nó mà tôi chưa giải quyết được, nhưng nếu bạn có thể làm cho nó hoạt động, thì vấn đề đã được giải quyết.

Rõ ràng các card đồ họa có RAM hạn chế - khi nó không thể chứa tất cả dữ liệu mô hình cần thiết để hiển thị 1 khung hình, tôi đoán nó tiếp tục tìm nạp (một số) nó từ RAM CPU mỗi khung hình, điều đó có đúng không?

Bạn không muốn hết RAM GPU. Nếu bạn làm, sau đó thay đổi mọi thứ để bạn không. Trong kịch bản rất giả thuyết là bạn hết, có thể nó sẽ sụp đổ bằng cách nào đó, nhưng tôi chưa bao giờ thấy nó xảy ra nên tôi thực sự không biết.

Tôi đã quên phân biệt: có gửi dữ liệu tới GPU và cài đặt / ràng buộc bộ đệm như hiện tại. Liệu cái sau gây ra bất kỳ luồng dữ liệu?

Không có bất kỳ luồng dữ liệu quan trọng, không. Có một số chi phí cho nó, nhưng điều đó đúng với mọi dòng mã bạn viết. Tìm hiểu xem chi phí của bạn là bao nhiêu, một lần nữa, hồ sơ là để làm gì.

tạo bộ đệm với khởi tạo

Câu trả lời của Raxvan nghe có vẻ hay, nhưng nó không hoàn toàn chính xác. Trong OpenGL, việc tạo bộ đệm không dành bất kỳ khoảng trống nào. Nếu bạn muốn dự trữ dung lượng mà không truyền bất kỳ dữ liệu nào, bạn có thể gọi glBufferData và chỉ cần truyền null. (Xem phần ghi chú ở đây .)

cập nhật dữ liệu đệm

Tôi đoán bạn có nghĩa là glBufferData, hoặc các chức năng khác như vậy, phải không? Đây là nơi chuyển dữ liệu thực sự xảy ra. (Trừ khi bạn vượt qua null, như tôi vừa nói ở đoạn cuối.)

ràng buộc bộ đệm là hoạt động (nó chỉ là một cách để nói với API rằng tôi muốn bộ đệm này được hiển thị trong lệnh gọi tiếp theo và nó không tự làm gì cả?)

Vâng, nhưng nó có thể làm nhiều hơn thế. Ví dụ: nếu bạn liên kết VAO (đối tượng mảng đỉnh), sau đó liên kết VBO, VBO đó sẽ bị ràng buộc với VAO. Sau đó, nếu bạn liên kết lại VAO đó và gọi glDrawArrays, nó sẽ biết VBO sẽ vẽ gì.

Lưu ý rằng mặc dù nhiều hướng dẫn sẽ giúp bạn tạo VAO cho mỗi VBO, nhưng tôi được cho biết đây không phải là mục đích sử dụng của họ. Giả sử bạn nên tạo một VAO và sử dụng nó với mọi VBO có cùng thuộc tính. Tôi chưa thử điều này, vì vậy tôi không thể chắc chắn liệu nó tốt hơn hay xấu đi.

Cuộc gọi vẽ API

Những gì xảy ra ở đây là khá đơn giản (từ quan điểm của chúng tôi). Giả sử bạn liên kết VAO, sau đó gọi glDrawArrays. Bạn chỉ định một điểm bắt đầu và một số đếm, và nó chạy trình tạo bóng đỉnh của bạn cho mọi đỉnh trong phạm vi đó, lần lượt chuyển các đầu ra của nó xuống dòng. Toàn bộ quá trình đó là một bài luận khác của riêng mình, mặc dù.


"Sau đó, vấn đề đã được giải quyết" Có, việc kích hoạt sẽ giúp ích rất nhiều nhưng nếu không có nó, tôi vẫn sẽ phải thực hiện một cuộc gọi rút thăm cho từng đối tượng. Amout giống nhau trong cả hai trường hợp. Vì vậy, tôi tự hỏi nếu thứ tự quan trọng.
NPS

@NPS - Nó quan trọng một số . Nếu họ được yêu cầu để bạn không phải tiếp tục chuyển đổi các ràng buộc của mình, vâng, đó có thể sẽ là một lượng rất nhỏ nhanh hơn. Nhưng nếu bạn phải đi ra ngoài để sắp xếp chúng, điều đó có thể sẽ tốn kém hơn rất nhiều. Có quá nhiều biến số phụ thuộc vào việc thực hiện của bạn để nói nhiều hơn thế.
Icy Defiance
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.