CPU - przepływ danych w pamięci GPU [zamknięty]

16

Jestem początkującym programistą grafiki i ostatnio zastanawiałem się - w jaki sposób dane modelu (siatki i materiały) przepływają z aplikacji (pamięć procesora) na kartę graficzną (pamięć GPU?)? Powiedzmy, że mam model statyczny (np. Budynek), który ładuję i konfiguruję raz i nie zmieniam się przez cały okres użytkowania aplikacji.

  • Czy dane są wysyłane do pamięci GPU tylko raz i pozostają tam na zawsze?
  • Kiedy model jest faktycznie renderowany w każdej ramce, czy procesory GPU muszą pobierać dane za każdym razem z pamięci GPU? Chodzi mi o to - gdybym miał 2 modele renderowane wiele razy każdy - czy miałoby to znaczenie, jeśli najpierw renderowałem pierwszy raz wiele razy, a następnie drugi raz wiele razy, czy też pierwszy raz renderowałem jeden raz, drugi raz jeden raz i przeplatałeś to w ten sposób? W tym sensie mogę nazwać to pytanie „wewnętrznym przepływem danych GPU”.
  • Oczywiście karty graficzne mają ograniczoną pamięć RAM - jeśli nie może pomieścić wszystkich danych modelu niezbędnych do renderowania 1 klatki, myślę, że pobiera (część) z pamięci RAM procesora każdej klatki, czy to prawda?

Wiem, że w Internecie jest wiele książek na ten temat, ale może masz jakieś ogólne ogólne wskazówki, jak zarządzać przepływem danych (kiedy i co wysłać, ile, kiedy i jak renderować)?

Edycja: Zapomniałem zrobić jedno rozróżnienie: wysyłanie danych do GPU i ustawianie / wiązanie buforów jako bieżących . Czy to ostatnie powoduje przepływ danych?

Edycja2: Po przeczytaniu postu Raxvana chciałbym wyróżnić kilka działań:

  • tworzenie bufora z inicjalizacją (jak powiedział, że mogę przechowywać dane w pamięci RAM procesora lub GPU)
  • aktualizacja danych bufora (co moim zdaniem jest proste, gdy dane są przechowywane w pamięci RAM procesora i wymagają pobierania z GPU do pamięci RAM procesora (a następnie z powrotem), gdy są przechowywane w pamięci RAM GPU)
  • powiązanie bufora jako aktywnego (czy to po prostu sposób, aby powiedzieć API, że chcę, aby ten bufor był renderowany w następnym wywołaniu losowania i sam nic nie robi ?)
  • Wywołanie losowania API (tutaj chciałbym usłyszeć od ciebie, co się tam naprawdę dzieje)
NPS
źródło
W żadnym wypadku nie jestem ekspertem, ale jeśli używasz nowoczesnego (tj. Nie natychmiastowego) OpenGL z VAO i VBO, dane są wysyłane do GPU i przechowywane w VRAM za każdym razem, gdy używasz jednej z rodziny poleceń glBuffer. Następnie przy każdym narysowaniu odpowiednie wierzchołki są pobierane z pamięci VRAM i renderowane. Jeśli jest to model, który się porusza, zwykle przechowujesz go statycznie i używasz macierzy, aby przechodzić z przestrzeni modelu do przestrzeni świata / kamery. Co do ostatniego punktu, nie mam pojęcia, co się stanie, jeśli zabraknie pamięci RAM. Domyślam się, że jeśli zabraknie pamięci VRAM, dane po prostu nie zostaną wysłane, prawdopodobnie z kodem błędu.
Polar
@Polar - niezupełnie. GL w rzeczywistości nie określa, w której pamięci jest przechowywany obiekt buforowy, a nawet może go swobodnie przenosić w czasie wykonywania na podstawie wzorca użytkowania. GL4.4 nieco to rozwiązuje, ale zauważa, że ​​w końcu najlepsze, co może zapewnić, to „jedna z tych głupich podpowiedzi”; patrz opengl.org/registry/specs/ARB/buffer_storage.txt, a zwłaszcza problemy 2 i 9.
Maximus Minimus,
1
@ JimmyShelter Ah, dzięki - byłoby miło, gdybyśmy mieli mniej „tych głupich podpowiedzi” i bardziej konkretną specyfikację.
Polar
@Polar - denerwujące jest to, że ARB_buffer_storage mógł uniknąć podania kolejnej wskazówki, ale projektanci stracili okazję. No cóż, być może 4.5 w końcu to naprawi.
Maximus Minimus
2
Nie edytuj pytań, aby „odpowiedzieć” na odpowiedzi. Zamiast tego opublikuj nowe pytanie.

Odpowiedzi:

12

Czy dane są wysyłane do pamięci GPU tylko raz i pozostają tam na zawsze?

Zwykle tak, ale sterownik ma swobodę robienia tego, co jest „optymalne”, dane mogą być przechowywane w VRAM lub RAM lub po prostu buforowane tutaj. Atricle wyjaśnia, co faktycznie dzieje się z przepływem VBO .

Na przykład, jeśli został oflagowany jako dynamiczny bufor OpenGL (np. VBO), jest bardziej prawdopodobne, że będzie przechowywany w pamięci RAM. GPU korzysta z bezpośredniego dostępu do pamięci (DMA), aby uzyskać bezpośredni dostęp do pamięci RAM bez interwencji CPU, jest to kontrolowane przez kontroler DMA na karcie graficznej i sterowniku graficznym i jest wykonywany w trybie jądra.

Kiedy model jest faktycznie renderowany w każdej ramce, czy procesory GPU muszą pobierać dane za każdym razem z pamięci GPU, nawet jeśli model renderuje wiele razy sekwencyjnie?

Podobnie jak procesory, procesory graficzne mogą zamawiać ponownie instrukcje GPU i operacje dostępu do pamięci (czytaj: wykonywanie poza kolejnością ), więc najprawdopodobniej GPU poradzi sobie ze wspomnianym scenariuszem, uzyskując dostęp do pamięci znajdującej się w jej pamięci podręcznej (zwykle ostatnio ), ale czasami nie można tego zrobić.

Oczywiście karty graficzne mają ograniczoną pamięć RAM - jeśli nie może pomieścić wszystkich danych modelu niezbędnych do renderowania 1 klatki, myślę, że pobiera (część) z pamięci RAM procesora każdej klatki, czy to prawda?

Nie chcesz, żeby tak się stało. Ale niezależnie od tego, czy tak się stanie, procesor graficzny zacznie przenosić pamięć między RAM i VRAM (odpowiedzialny za to procesor poleceń na GPU), co znacznie zwolni renderowanie, co spowoduje zatrzymanie procesora graficznego, ponieważ będzie musiał czekać na dane do skopiowania z / do V / RAM.

Przesyłanie danych do GPU i ustawianie / wiązanie buforów jako bieżących. Czy to ostatnie powoduje przepływ danych?

Procesory graficzne zawierają bufor poleceń , a wszystkie polecenia API są przekazywane do tego bufora, zauważ, że może się to zdarzyć jednocześnie z kopiowaniem danych do GPU. Bufor pierścieniowy polecenie jest kolejka komunikacji pomiędzy procesorem i graficznego , każdym poleceniem, które muszą być wykonane musi zostać złożone w kolejce, więc może być execulated GPU. Podobnie jak każda operacja wiążąca nowe bufory musi zostać przesłana do GPU, aby mógł uzyskać dostęp do niektórych lokalizacji pamięci.

Jest to jeden z powodów, dla których glBegin / glEnd było przestarzałe, przesyłanie nowych poleceń wymaga synchronizacji kolejek (przy użyciu płotów / barier pamięci).

wprowadź opis zdjęcia tutaj

Co do twoich innych punktów:

Tworzenie bufora z inicjalizacją

Możesz przydzielić bufor bez inicjalizacji i zachować go do późniejszego wykorzystania. Możesz też przydzielić mu bufor i jednocześnie skopiować dane (mówiąc o poziomie interfejsu API).

aktualizacja danych bufora

Możesz użyć glMapBuffer do aktualizacji pamięci po stronie GPU. to, czy pamięć zostanie skopiowana z / do pamięci RAM, nie jest tak naprawdę standardem i będzie się znacznie różnić w zależności od dostawcy, typu GPU i sterownika.

Wywołanie losowania API (tutaj chciałbym usłyszeć od ciebie, co się tam naprawdę dzieje).

Moja druga uwaga w głównym pytaniu dotyczy tego.

powiązanie bufora jako aktywnego (czy to tylko sposób, aby powiedzieć interfejsowi API, że chcę, aby ten bufor był> renderowany w następnym wywołaniu losowania i sam nic nie robi?)

Pomyśl o wiązaniu jako o użyciu thiswskaźnika w dowolnym języku obiektowym, chociaż nie jest to dokładnie to samo, wszelkie późniejsze wywołania API będą względne względem tego bufora wiązania.

concept3d
źródło
3

Zasadniczo granica i zaangażowanie procesora w stosunku do procesora są specyficzne dla platformy, ale większość z nich jest zgodna z tym modelem: procesor ma trochę pamięci RAM, GPU również i możesz przenosić pamięć (w niektórych przypadkach pamięć RAM jest wspólna, ale dla dla uproszczenia trzymajmy się oddzielnych pamięci RAM).

pierwszy punkt : Dane, które inicjujesz, możesz wybrać, aby zachować je w pamięci RAM procesora lub pamięci RAM karty graficznej, i są to zalety obu. Kiedy renderujesz coś, procesor graficzny musi wykonać ciężkie podnoszenie, więc oczywiste jest, że dane, które są już w pamięci GPU, zapewnią lepszą wydajność. w przypadku procesora musi najpierw wysłać dane do GPU (który może zatrzymać je na chwilę), a następnie wykonać renderowanie.

Drugi punkt : Istnieje wiele sztuczek w renderowaniu, ale głównym sposobem jest użycie wielokątów. Na ramce GPU będzie renderować obiekty wykonane z wielokątów jeden po drugim, a po zakończeniu procesor graficzny wyśle ​​obraz na ekran. Nie ma pojęcia takiego jak obiekty, są tylko wielokąty, a sposób ich złożenia stworzy obraz. zadaniem GPU jest rzutowanie tych wielokątów z 3d na 2d i zastosowanie efektu (jeśli jest to pożądane). Wielokąty przechodzą tylko w sposób bezpośredni CPU-> GPU-> SCREEN lub GPU-> SCREEN (jeśli wielokąty są już w ramce GPU)

trzeci punkt : na przykład podczas renderowania animacji lepiej jest trzymać dane blisko procesora, ponieważ tam wykonuje on ciężkie podnoszenie, nie byłoby optymalne utrzymywanie danych w GPU, przenoszenie ich do procesora i cofanie każdej klatki. Istnieje wiele innych przykładów takich jak ten, które należy liczyć, ale ogólnie wszystkie dane pozostaną blisko tego, kto wykonuje obliczenia. Zwykle chcesz przenieść jak najwięcej danych do pamięci RAM procesora graficznego, aby zwiększyć wydajność.

Rzeczywiste wysyłanie danych do GPU odbywa się za pomocą interfejsu API, którego używasz (directx / opengl lub inny), a koncepcja wiązania i tego typu rzeczy to tylko abstrakcje, dzięki czemu API rozumie, co chcesz zrobić.

Edytuj do edycji:

  • buffer creation with initialisation: to jest jak różnica między int a = new int[10]a a[0] = 0,a[1] = 1.... etc kiedy tworzysz bufor, robisz miejsce dla danych, a kiedy inicjujesz dane, umieszczasz tam rzeczy, które chcesz.

  • buffer data updatejeśli jest na ramce procesora vertex * vertices, masz możliwość grania z nim, jeśli go nie ma, musisz przenieść go z GPU vertex * vertices = map(buffer_id);(mapa to mitologiczna funkcja, która powinna przenosić dane z GPU na RAM procesora, ma też swoje przeciwieństwo buffer_id = create_buffer(vertices);

  • binding the buffer as activeto tylko koncepcja, którą nazywają bindingrenderowaniem, jest złożonym procesem i przypomina wywołanie funkcji z 10000 parametrami. Wiązanie jest tylko terminem używanym do określenia, który bufor idzie gdzie. Za tym terminem nie kryje się żadna magia, nie konwertuje, nie przenosi ani nie przenosi buforów, po prostu informuje kierowcę, że przy następnym wywołaniu losowania używa tego bufora.

  • API draw callPo wszystkich buforach do wiązania i ustawiania jest to miejsce, w którym guma styka się z drogą. Wywołanie losowania zajmie wszystkie dane (lub identyfikatory wskazujące dane), które podałeś, wysłał je do GPU (w razie potrzeby) i nakazał GPU rozpoczęcie zgniatania liczb. Nie jest to do końca prawdą na wszystkich platformach, istnieje wiele różnic, ale dla uproszczenia losowanie nakazuje GPU .... losowanie.

Raxvan
źródło
2

Najbardziej poprawna odpowiedź brzmi: zależy to od tego, jak ją zaprogramujesz, ale warto się martwić. Chociaż procesory graficzne stały się niezwykle szybkie, przepustowość do i z RAM GPU nie jest i będzie twoim najbardziej frustrującym wąskim gardłem.

Czy dane są wysyłane do pamięci GPU tylko raz i pozostają tam na zawsze?

Mam nadzieję, że tak. Aby uzyskać szybkość renderowania, chcesz, aby jak najwięcej danych znajdowało się na GPU, zamiast przesyłać je co każdą klatkę. VBO służą właśnie temu celowi. Istnieją zarówno statyczne, jak i dynamiczne VBO, te pierwsze są najlepsze dla modeli statycznych, a drugie są najlepsze dla modeli, których wierzchołki zmienią każdą klatkę (powiedzmy układ cząstek). Jednak nawet jeśli chodzi o dynamiczne VBO, nie chcesz ponownie wysyłać wszystkich wierzchołków każdej klatki; tylko te, które się zmieniają.

W przypadku twojego budynku, dane wierzchołków po prostu by tam siedziały, a jedyną zmianą są twoje macierze (model / świat, rzut i widok).

W przypadku układu cząstek stworzyłem dynamiczne VBO wystarczająco duże, aby pomieścić maksymalną liczbę cząstek, jaka kiedykolwiek istniałaby dla tego układu. Do każdej ramki wysyłam dane dla cząstek emitowanych w tej ramce, wraz z kilkoma mundurami i to wszystko. Kiedy rysuję, mogę określić punkt początkowy i końcowy w tym VBO, więc nie muszę usuwać danych cząstek. Mogę tylko powiedzieć, że nie rysuj.

Kiedy model jest faktycznie renderowany w każdej ramce, czy procesory GPU muszą pobierać dane za każdym razem z pamięci GPU? Chodzi mi o to - gdybym miał 2 modele renderowane wiele razy każdy - czy miałoby to znaczenie, jeśli najpierw renderowałem pierwszy raz wiele razy, a następnie drugi raz wiele razy, czy też pierwszy raz renderowałem jeden raz, drugi raz jeden raz i przeplatałeś to w ten sposób?

Wysyłanie wielu losowań zamiast tylko jednego jest znacznie większym ograniczeniem. Sprawdź renderowanie instancji; może ci bardzo pomóc i uczynić odpowiedź na to pytanie bezużyteczną. Miałem pewne problemy ze sterownikami, których jeszcze nie opracowałem, ale jeśli możesz go uruchomić, problem został rozwiązany.

Oczywiście karty graficzne mają ograniczoną pamięć RAM - jeśli nie może pomieścić wszystkich danych modelu niezbędnych do renderowania 1 klatki, myślę, że pobiera (część) z pamięci RAM procesora każdej klatki, czy to prawda?

Nie chcesz zabraknąć RAM GPU. Jeśli to zrobisz, zmień rzeczy, abyś tego nie zrobił. W bardzo hipotetycznym scenariuszu, w którym się skończysz, prawdopodobnie jakoś się rozbije, ale nigdy tego nie widziałem, więc szczerze mówiąc nie wiem.

Zapomniałem zrobić jedno rozróżnienie: wysyłanie danych do GPU i ustawianie / wiązanie buforów jako bieżących. Czy to ostatnie powoduje przepływ danych?

Nie ma znaczącego przepływu danych, nie. Jest to trochę kosztowne, ale dotyczy to każdego wiersza kodu, który piszesz. Dowiedz się, ile to kosztuje, i po co jest profilowanie.

tworzenie bufora z inicjalizacją

Odpowiedź Raxvana brzmi dobrze, ale nie jest całkiem dokładna. W OpenGL utworzenie bufora nie rezerwuje miejsca. Jeśli chcesz zarezerwować miejsce bez przekazywania żadnych danych, możesz wywołać glBufferData i po prostu przekazać null. (Zobacz sekcję notatek tutaj .)

aktualizacja danych bufora

Zgaduję, że masz na myśli glBufferData lub inne podobne funkcje, prawda? To tam następuje prawdziwy transfer danych. (Chyba, że ​​zdasz zero, jak właśnie powiedziałem w ostatnim akapicie).

powiązanie bufora jako aktywnego (czy to po prostu sposób, aby powiedzieć API, że chcę, aby ten bufor był renderowany w następnym wywołaniu losowania i sam nic nie robi?)

Tak, ale może zrobić coś więcej. Na przykład, jeśli powiążesz VAO (obiekt tablicy wierzchołków), a następnie powiążesz VBO, to VBO zostanie powiązane z VAO. Później, jeśli ponownie powiążesz ten VAO i wywołasz glDrawArrays, będzie wiedział, co VBO narysować.

Pamiętaj, że chociaż w wielu samouczkach utworzysz VAO dla każdego VBO, powiedziano mi, że nie jest to ich zamierzone zastosowanie. Podobno powinieneś stworzyć jedną VAO i używać jej z każdym VBO, który ma te same atrybuty. Jeszcze tego nie próbowałem, więc nie mogę powiedzieć na pewno, czy jest to lepsze czy gorsze.

Wywołanie losowania API

To, co się tutaj dzieje, jest dość proste (z naszej perspektywy). Załóżmy, że wiążesz VAO, a następnie wywołujesz glDrawArrays. Podajesz punkt początkowy i liczbę, a on uruchamia moduł cieniujący wierzchołki dla każdego wierzchołka w tym zakresie, który z kolei przekazuje swoje wyniki wzdłuż linii. Cały ten proces to jednak kolejny esej.

Icy Defiance
źródło
„wtedy problem rozwiązany” Tak, instancja bardzo by pomogła, ale bez niej nadal musiałbym wykonać polecenie losowania dla każdego obiektu. To samo dotyczy w obu przypadkach. Zastanawiam się więc, czy kolejność ma znaczenie.
NPS
@NPS - To ma znaczenie niektórych . Jeśli zostaną zamówione, abyś nie musiał ciągle zmieniać wiązań, tak, to prawdopodobnie będzie to niewielka ilość szybciej. Ale jeśli będziesz musiał zrobić wszystko, aby je uporządkować, prawdopodobnie będzie to o wiele, dużo bardziej kosztowne. Istnieje zbyt wiele zmiennych zależnych od implementacji, aby powiedzieć znacznie więcej.
Icy Defiance