Jak CPU i GPU oddziałują na wyświetlanie grafiki komputerowej?

58

Tutaj możesz zobaczyć zrzut ekranu małego programu C ++ o nazwie Triangle.exe z obracającym się trójkątem opartym na API OpenGL.

wprowadź opis zdjęcia tutaj

Wprawdzie bardzo prosty przykład, ale myślę, że ma on zastosowanie do innych operacji na kartach graficznych.

Byłem po prostu ciekawy i chciałem poznać cały proces od dwukrotnego kliknięcia pliku Triangle.exe w systemie Windows XP, dopóki nie zobaczę trójkąta obracającego się na monitorze. Co się dzieje, w jaki sposób procesor (który najpierw obsługuje plik .exe) i procesor graficzny (który ostatecznie wyświetla trójkąt na ekranie) współdziałają?

Wydaje mi się, że w wyświetlaniu tego obracającego się trójkąta zaangażowany jest przede wszystkim następujący sprzęt / oprogramowanie:

Sprzęt komputerowy

  • HDD
  • Pamięć systemowa (RAM)
  • procesor
  • Pamięć wideo
  • GPU
  • wyświetlacz LCD

Oprogramowanie

  • System operacyjny
  • Interfejs API DirectX / OpenGL
  • Sterownik Nvidia

Czy ktoś może wyjaśnić ten proces, może za pomocą jakiegoś schematu ilustracyjnego?

Nie powinno to być skomplikowane objaśnienie, które obejmuje każdy krok (przypuszczam, że wykracza to poza zakres), ale wyjaśnienie, które może zastosować pośrednik z branży IT.

Jestem pewien, że wiele osób, które nawet nazywają siebie specjalistami IT, nie może poprawnie opisać tego procesu.

JohnnyFromBF
źródło
Twój dylemat skończyłby się, gdybyś mógł po prostu rozważyć GPU jako rozszerzenie procesora!
KawaiKx

Odpowiedzi:

55

Postanowiłem napisać trochę o aspekcie programowania i o tym, jak komponenty ze sobą rozmawiają. Może rzuci trochę światła na niektóre obszary.

Prezentacja

Co trzeba zrobić, aby ten pojedynczy obraz, który opublikowałeś w swoim pytaniu, został narysowany na ekranie?

Istnieje wiele sposobów narysowania trójkąta na ekranie. Dla uproszczenia załóżmy, że nie użyto buforów wierzchołków. ( Bufor wierzchołków to obszar pamięci, w którym przechowujesz współrzędne.) Załóżmy, że program po prostu powiedział potokowi przetwarzania grafiki o każdym pojedynczym wierzchołku (wierzchołek jest tylko współrzędną w przestrzeni) z rzędu.

Ale zanim cokolwiek narysujemy, najpierw musimy uruchomić rusztowanie. Zobaczymy, dlaczego później:

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

Co to zrobiło?

Kiedy piszesz program, który chce korzystać z karty graficznej, zwykle wybierasz interfejs sterownika. Niektóre dobrze znane interfejsy do sterownika to:

  • OpenGL
  • Direct3D
  • CUDA

W tym przykładzie będziemy trzymać się OpenGL. Teraz interfejs do sterownika zapewnia wszystkie narzędzia potrzebne do tego, aby program komunikował się z kartą graficzną (lub sterownikiem, który następnie komunikuje się z kartą).

Ten interfejs ma pewne narzędzia . Narzędzia te mają kształt interfejsu API, który można wywołać z poziomu programu.

Ten interfejs API jest tym, co widzimy w powyższym przykładzie. Przyjrzyjmy się bliżej.

Rusztowanie

Zanim naprawdę będziesz mógł wykonać rzeczywisty rysunek, musisz wykonać konfigurację . Musisz zdefiniować swoją rzutnię (obszar, który będzie faktycznie renderowany), swoją perspektywę ( aparat do swojego świata), jakiego antyaliasingu będziesz używał (aby wygładzić krawędź trójkąta) ...

Ale na to nie spojrzymy. Rzućmy okiem na rzeczy, które będziesz musiał zrobić w każdej klatce . Lubić:

Czyszczenie ekranu

Potok grafiki nie wyczyści ekranu dla każdej klatki. Musisz to powiedzieć. Dlaczego? Dlatego:

wprowadź opis zdjęcia tutaj

Jeśli nie wyczyścisz ekranu, po prostu narysujesz go na każdej klatce. Dlatego dzwonimy glClearz GL_COLOR_BUFFER_BITzestawem. Drugi bit ( GL_DEPTH_BUFFER_BIT) mówi OpenGL, aby wyczyścił bufor głębokości . Bufor ten służy do określania, które piksele znajdują się przed (lub za) innymi pikselami.

Transformacja

wprowadź opis zdjęcia tutaj
Źródło obrazu

Transformacja to część, w której bierzemy wszystkie współrzędne wejściowe (wierzchołki naszego trójkąta) i stosujemy naszą macierz ModelView. Jest to macierz, która wyjaśnia, w jaki sposób nasz model (wierzchołki) są obracane, skalowane i tłumaczone (przenoszone).

Następnie stosujemy naszą macierz projekcji. Powoduje to przesuwanie wszystkich współrzędnych, aby były skierowane w stronę naszego aparatu.

Teraz przekształcamy się jeszcze raz, dzięki naszej matrycy Viewport. Robimy to, aby skalować nasz model do rozmiaru naszego monitora. Teraz mamy zestaw wierzchołków, które są gotowe do renderowania!

Wrócimy do transformacji nieco później.

Rysunek

Narysować trójkąt, możemy po prostu powiedzieć OpenGL, aby rozpocząć nową listę trójkątów wywołując glBeginprzy GL_TRIANGLESstałej.
Można również narysować inne formy. Jak trójkątny pasek lub trójkątny wachlarz . Są to przede wszystkim optymalizacje, ponieważ wymagają mniejszej komunikacji między CPU a GPU, aby narysować taką samą liczbę trójkątów.

Następnie możemy dostarczyć listę zestawów 3 wierzchołków, które powinny tworzyć każdy trójkąt. Każdy trójkąt używa 3 współrzędnych (jak w przestrzeni 3D). Dodatkowo podaję również kolor dla każdego wierzchołka, dzwoniąc glColor3f przed wywołaniem glVertex3f.

Cień między 3 wierzchołkami (3 narożniki trójkąta) jest obliczany automatycznie przez OpenGL . Będzie interpolować kolor na całej powierzchni wielokąta.

Interakcja

Teraz, kiedy klikniesz okno. Aplikacja musi tylko przechwycić komunikat okna sygnalizujący kliknięcie. Następnie możesz uruchomić dowolną akcję w swoim programie.

Staje się to o wiele trudniejsze, gdy chcesz rozpocząć interakcję ze sceną 3D.

Najpierw musisz wyraźnie wiedzieć, przy którym pikselu użytkownik kliknął okno. Następnie, biorąc pod uwagę twoją perspektywę , możesz obliczyć kierunek promienia od momentu kliknięcia myszą w twoją scenę. Następnie możesz obliczyć, czy jakikolwiek obiekt w scenie przecina się z tym promieniem . Teraz wiesz, czy użytkownik kliknął obiekt.

Jak więc się obraca?

Transformacja

Mam świadomość dwóch rodzajów transformacji, które są ogólnie stosowane:

  • Transformacja macierzowa
  • Transformacja kości

Różnica polega na tym, że kości wpływają na pojedyncze wierzchołki . Macierze zawsze wpływają na wszystkie narysowane wierzchołki w ten sam sposób. Spójrzmy na przykład.

Przykład

Wcześniej ładowaliśmy naszą matrycę tożsamości przed narysowaniem naszego trójkąta. Macierz tożsamości to taka, która po prostu nie zapewnia żadnej transformacji . Tak więc na cokolwiek rysuję, wpływa tylko moja perspektywa. Tak więc trójkąt w ogóle nie zostanie obrócony.

Jeśli chcę, aby obrócić go teraz, mogę też zrobić matematyki ja (CPU) i po prostu zadzwonić glVertex3fz innych współrzędnych (które są obrócone). Albo mógłbym pozwolić GPU wykonać całą pracę, dzwoniąc glRotatefprzed rysowaniem:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amountjest oczywiście tylko stałą wartością. Jeśli chcesz animować , musisz śledzić amounti zwiększać ją w każdej klatce.

Więc poczekaj, co się stało z całą rozmową matrycy wcześniej?

W tym prostym przykładzie nie musimy przejmować się matrycami. Po prostu dzwonimy glRotatefi dba o to wszystko za nas.

glRotatetworzy obrót anglestopni wokół wektora xyz. Bieżąca macierz (patrz glMatrixMode ) jest mnożona przez macierz obrotu, a produkt zastępuje bieżącą macierz, tak jakby glMultMatrix został wywołany z następującą macierzą jako argumentem:

x 2 ⁡ 1 - c + cx ⁢ y ⁡ 1 - c - z ⁢ sx ⁢ z ⁡ 1 - c + y ⁢ s 0 y ⁢ x ⁡ 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x ⁢ s 0 x ⁢ z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Dzięki za to!

Wniosek

Staje się oczywiste, że wiele mówi się o OpenGL. Ale to nic nam nie mówi . Gdzie jest komunikacja?

Jedyne, co OpenGL mówi nam w tym przykładzie, to kiedy to się skończy . Każda operacja zajmie określoną ilość czasu. Niektóre operacje trwają niewiarygodnie długo, inne są niezwykle szybkie.

Wysłanie wierzchołka do GPU będzie tak szybkie, że nawet nie wiedziałbym, jak to wyrazić. Przesłanie tysięcy wierzchołków z CPU do GPU, każdej pojedynczej klatki, najprawdopodobniej nie stanowi żadnego problemu.

Wyczyszczenie ekranu może potrwać milisekundę lub gorzej (pamiętaj, że zwykle masz tylko około 16 milisekund czasu na narysowanie każdej klatki), w zależności od tego, jak duża jest twoja rzutnia. Aby go wyczyścić, OpenGL musi narysować każdy piksel w kolorze, który chcesz wyczyścić, może to być miliony pikseli.

Poza tym możemy jedynie zapytać OpenGL o możliwości naszego adaptera graficznego (maksymalna rozdzielczość, maksymalne wygładzanie, maksymalna głębia kolorów, ...).

Ale możemy również wypełnić teksturę pikselami, z których każdy ma określony kolor. Każdy piksel ma zatem wartość, a tekstura jest gigantycznym „plikiem” wypełnionym danymi. Możemy załadować to na kartę graficzną (poprzez utworzenie bufora tekstur), a następnie załadować moduł cieniujący , powiedzieć temu modułowi cieniującemu, aby użył naszej tekstury jako danych wejściowych i wykonał bardzo ciężkie obliczenia na naszym „pliku”.

Następnie możemy „renderować” wynik naszych obliczeń (w postaci nowych kolorów) na nową teksturę.

W ten sposób możesz sprawić, by procesor graficzny działał dla Ciebie na inne sposoby. Zakładam, że CUDA działa podobnie do tego aspektu, ale nigdy nie miałem okazji z nim pracować.

Naprawdę tylko nieznacznie dotknęliśmy całego tematu. Programowanie grafiki 3D to piekielnie bestia.

wprowadź opis zdjęcia tutaj
Źródło obrazu

Der Hochstapler
źródło
38

Trudno zrozumieć dokładnie to, czego nie rozumiesz.

GPU ma szereg rejestrów mapowanych przez BIOS. Umożliwiają one CPU dostęp do pamięci GPU i instruują GPU, aby wykonał operacje. Procesor podłącza wartości do tych rejestrów, aby zmapować część pamięci GPU, aby procesor mógł uzyskać do niej dostęp. Następnie ładuje instrukcje do tej pamięci. Następnie zapisuje wartość do rejestru, który nakazuje GPU wykonanie instrukcji załadowanych przez CPU do jego pamięci.

Informacje składają się z oprogramowania, które GPU musi uruchomić. To oprogramowanie jest dołączane do sterownika, a następnie sterownik obsługuje podział odpowiedzialności między CPU a GPU (uruchamiając części swojego kodu na obu urządzeniach).

Sterownik następnie zarządza serią „okien” w pamięci GPU, z których procesor może czytać i zapisywać. Zasadniczo wzorzec dostępu obejmuje zapisywanie przez CPU instrukcji lub informacji w zmapowanej pamięci GPU, a następnie instruowanie GPU za pośrednictwem rejestru, aby wykonać te instrukcje lub przetworzyć te informacje. Informacje obejmują logikę cieniowania, tekstury i tak dalej.

David Schwartz
źródło
1
Dziękuję za wyjaśnienie. Zasadniczo nie zrozumiałem, w jaki sposób zestaw instrukcji procesora komunikuje się z zestawem instrukcji GPU, ale oczywiście to właśnie sterownik wykonuje tę część. To miałem na myśli z warstwami abstrakcji.
JohnnyFromBF,
2
Nie jest zaangażowany żaden zestaw instrukcji procesora. Sterownik i środowisko wykonawcze kompilują CUDA, OpenGL, Direct3D itp. Do rodzimych programów / jąder GPU, które następnie są również przesyłane do pamięci urządzenia. Bufor poleceń odnosi się następnie do takich jak każdy inny zasób.
Axel Gneiting,
2
Nie jestem pewien, do jakich programów się odnosisz (które działają na GPU i są dołączone do sterowników). GPU jest w dużej mierze sprzętem o stałej funkcji, a jedynymi programami, które uruchomi, są shadery, które są dostarczane przez aplikację, a nie sterownik. Sterownik kompiluje tylko te programy, a następnie ładuje je do pamięci GPU.
Ben Richards
1
@ sidran32: Na przykład w architekturze Kepler nVidii jądra, strumienie i zdarzenia są tworzone przez oprogramowanie działające na GPU, a nie (zwykle) CPU. Oprogramowanie po stronie GPU również zarządza RDMA. Całe to oprogramowanie jest ładowane przez sterownik do pamięci GPU i działa jako „mini-OS” na GPU, który obsługuje stronę GPU pary współpracującej CPU / GPU.
David Schwartz
@DavidSchwartz Zapomniałem o zadaniach obliczeniowych na GPU. Jednak mimo to zachowują się podobnie do shaderów. Nie nazwałbym tego jednak „mini-OS”, ponieważ nie ma tej samej funkcjonalności, co zwykle kojarzone z systemami operacyjnymi. To wciąż bardzo wyspecjalizowane oprogramowanie, ponieważ procesor graficzny nie jest zaprojektowany jako procesor (nie bez powodu).
Ben Richards
13

Byłem po prostu ciekawy i chciałem poznać cały proces od dwukrotnego kliknięcia pliku Triangle.exe w systemie Windows XP, dopóki nie zobaczę trójkąta obracającego się na monitorze. Co się dzieje, w jaki sposób procesor (który najpierw obsługuje plik .exe) i procesor graficzny (który ostatecznie wyświetla trójkąt na ekranie) współdziałają?

Załóżmy, że faktycznie wiesz, jak plik wykonywalny działa w systemie operacyjnym i jak ten plik wykonywalny jest wysyłany z twojego procesora graficznego do monitora, ale nie wiesz, co się dzieje pomiędzy nimi. Spójrzmy więc na aspekt sprzętowy i dalej rozwiążmy odpowiedź na aspekt programisty ...

Jaki jest interfejs między CPU a GPU?

Za pomocą sterownika procesor może rozmawiać przez funkcje płyty głównej, takie jak PCI, z kartą graficzną i wysyłać do niej polecenia, aby wykonać niektóre instrukcje GPU, uzyskać dostęp / zaktualizować pamięć GPU , załadować kod do wykonania na GPU i więcej ...

Ale tak naprawdę nie można rozmawiać bezpośrednio ze sprzętem lub sterownikiem z kodu; będzie to musiało odbywać się za pośrednictwem interfejsów API takich jak OpenGL, Direct3D, CUDA, HLSL, Cg. Podczas gdy pierwsze uruchamiają instrukcje GPU i / lub aktualizują pamięć GPU, drugie faktycznie wykonują kod na GPU, ponieważ są one językami fizyki / shaderów.

Po co uruchamiać kod na GPU, a nie na CPU?

Chociaż procesor dobrze radzi sobie z uruchamianiem naszych codziennych stacji roboczych i programów serwerowych, nie zastanawiałem się zbyt wiele nad tymi błyszczącymi grafikami, które można zobaczyć w dzisiejszych grach. W tamtych czasach istniały programy renderujące, które załatwiły niektóre rzeczy 2D i 3D, ale były bardzo ograniczające. Oto, gdzie gra GPU.

Procesor graficzny jest zoptymalizowany pod kątem jednego z najważniejszych obliczeń graficznych - manipulacji matrycą . Podczas gdy procesor musi obliczać każde mnożenie w manipulacji macierzą jeden po drugim (później, takie rzeczy jak 3DNow! I SSE nadrobiły zaległości), GPU może wykonać wszystkie te mnożenia na raz! Równoległość.

Ale równoległe obliczenia nie są jedynym powodem, innym powodem jest to, że GPU jest znacznie bliżej pamięci wideo, co sprawia, że ​​jest znacznie szybsza niż konieczność robienia podróży w obie strony przez procesor itp.

Jak te instrukcje / pamięć / kod GPU pokazują grafikę?

Brakuje jednego kawałka, aby wszystko działało, potrzebujemy czegoś, do czego możemy napisać, a następnie możemy przeczytać i wysłać na ekran. Możemy to zrobić, tworząc bufor ramki . Bez względu na to, jaką operację wykonasz, ostatecznie zaktualizujesz piksele w buforze ramki; która oprócz lokalizacji zawiera także informacje o kolorze i głębokości.

Podajmy przykład, w którym chciałeś narysować gdzieś duszka krwi (obraz); po pierwsze, sama tekstura drzewa jest ładowana do pamięci GPU, co ułatwia jej przerysowanie na życzenie. Następnie, aby rzeczywiście narysować duszka gdzieś, możemy przetłumaczyć duszka za pomocą wierzchołków (umieszczając go we właściwej pozycji), rasteryzując (zamieniając go z obiektu 3D w piksele) i aktualizując bufor ramki. Aby uzyskać lepszy pomysł, oto schemat przepływu rurociągów OpenGL z Wikipedii:

Jest to główny zamysł całej grafiki, więcej badań to zadanie domowe dla czytelnika.

Tamara Wijsman
źródło
7

Dla uproszczenia możemy to opisać w ten sposób. Niektóre adresy pamięci są zarezerwowane (przez BIOS i / lub system operacyjny) nie dla pamięci RAM, ale dla karty graficznej. Wszelkie dane zapisane przy tych wartościach (wskaźnikach) trafiają na kartę. Teoretycznie każdy program może pisać bezpośrednio na karcie wideo, znając zakres adresów, i tak właśnie robiono w dawnych czasach. W praktyce z nowoczesnymi systemami operacyjnymi jest to zarządzane przez sterownik wideo i / lub bibliotekę graficzną na górze (DirectX, OpenGL itp.).

AZ.
źródło
1
-1 pyta, w jaki sposób wywołanie API DirectX z CPU może komunikować się z GPU, a twoja odpowiedź brzmi: „jest zarządzany przez sterownik i / lub DirectX” ? Nie wyjaśnia to również, w jaki sposób można uruchomić niestandardowy kod (ala CUDA).
BlueRaja - Danny Pflughoeft
3
naucz się czytać. Powiedziałem pisząc na konkretne adresy pamięci, które są zarezerwowane dla GPU zamiast RAM. I to wyjaśnia, jak możesz uruchomić wszystko. Zakres pamięci jest zarejestrowany dla karty. Wszystko, co piszesz w tym zakresie, trafia do GPU z przetwarzaniem wierzchołków, CUDA, cokolwiek.
AZ.
5

Procesory graficzne są zwykle sterowane przez bufory DMA. Oznacza to, że sterownik kompiluje polecenia otrzymane od programu kosmicznego użytkownika w strumień instrukcji (stan przełączania, narysuj to w ten sposób, konteksty przełączania itp.), Które są następnie kopiowane do pamięci urządzenia. Następnie nakazuje GPU wykonanie bufora poleceń za pomocą rejestru PCI lub podobnych metod.

Tak więc przy każdym wywołaniu remisu itp. Dzieje się tak, że sterownik przestrzeni użytkownika skompiluje polecenie, które następnie wywołuje sterownik przestrzeni jądra poprzez przerwanie i że ostatecznie przesyła bufor poleceń do pamięci urządzenia i instruuje GPU, aby zaczął renderować.

Na konsolach możesz nawet dobrze się bawić, zwłaszcza na PS3.

Axel Gneiting
źródło
0

Myślę, że CPU wysyła dane wideo do GPU przez magistralę, a następnie GPU wyświetla je. Dzięki temu szybszy procesor graficzny może obsłużyć więcej danych z procesora. W ten sposób część przetwarzania cpuoffload na GPU. Dlatego zyskujesz większą prędkość w grach.

To trochę jak pamięć RAM, w której procesor przechowuje rzeczy, dzięki czemu można je szybko ładować i przetwarzać. Oba sprawiają, że gry są szybsze.

Lub karta dźwiękowa lub karta sieciowa działają na tej samej zasadzie, tj. Pobierają dane i odciążają procesor.

użytkownik583641
źródło
To powiela inną odpowiedź i nie dodaje żadnej nowej treści. Nie publikuj odpowiedzi, chyba że masz coś nowego do dodania.
DavidPostill
0

Myślę, że op nie jest pewien, co dokładnie procesor mówi karcie graficznej i dlaczego polecenia związane z grafiką (takie jak polecenia opengl lub direct3d) nie są wysyłane bezpośrednio do GPU.

CPU po prostu mówi GPU, co ma renderować. Wszystkie instrukcje najpierw przechodzą przez procesor, w którym są konfigurowane / inicjowane, aby procesor graficzny faktycznie renderował.

Dave
źródło