Rozumiem, jak pisać programy OpenGL / DirectX i znam matematykę i koncepcje, które się za tym kryją, ale jestem ciekaw, jak działa komunikacja GPU-CPU na niskim poziomie.
Powiedzmy, że mam program OpenGL napisany w C, który wyświetla trójkąt i obraca kamerę o 45 stopni. Kiedy skompiluję ten program, zostanie on zamieniony na serię wywołań ioctl, a sterownik gpu wyśle następnie odpowiednie polecenia do gpu, gdzie cała logika obracania trójkąta i ustawiania odpowiednich pikseli w odpowiednim kolorze jest podłączona w? A może program zostanie skompilowany do postaci „programu GPU”, który jest ładowany do procesora GPU i oblicza rotację itp.? A może coś zupełnie innego?
Edycja : Kilka dni później znalazłem tę serię artykułów, która w zasadzie odpowiada na pytanie: http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part- 1 /
Odpowiedzi:
Odpowiedź na to pytanie jest prawie niemożliwa, ponieważ sam OpenGL jest tylko interfejsem API typu front-end i tak długo, jak implementacja jest zgodna ze specyfikacją, a wynik jest zgodny z tym, można to zrobić w dowolny sposób.
Pytanie mogło brzmieć: jak działa sterownik OpenGL na najniższym poziomie. Teraz znowu nie można odpowiedzieć na to ogólnie, ponieważ sterownik jest ściśle powiązany z jakimś sprzętem, który może znowu działać tak, jak zaprojektował go programista.
Zatem pytanie powinno brzmieć: „Jak to średnio wygląda za kulisami OpenGL i systemu graficznego?”. Spójrzmy na to od dołu do góry:
Na najniższym poziomie jest jakieś urządzenie graficzne. W dzisiejszych czasach są to GPU, które zapewniają zestaw rejestrów kontrolujących ich działanie (które dokładnie są zależne od urządzenia), mają trochę pamięci programowej dla shaderów, pamięć masową na dane wejściowe (wierzchołki, tekstury itp.) Oraz kanał I / O dla reszty systemu, przez który odbiera / wysyła strumienie danych i poleceń.
Sterownik karty graficznej śledzi stan procesorów GPU i wszystkie zasoby aplikacji korzystających z GPU. Odpowiada również za konwersję lub jakiekolwiek inne przetwarzanie danych wysyłanych przez aplikacje (konwertowanie tekstur do formatu pikseli obsługiwanego przez GPU, kompilowanie shaderów w kodzie maszynowym GPU). Ponadto zapewnia abstrakcyjny, zależny od sterownika interfejs do programów użytkowych.
Jest też zależna od sterownika biblioteka / sterownik klienta OpenGL. W systemie Windows ładuje się to przez proxy przez opengl32.dll, w systemach Unix znajduje się w dwóch miejscach:
W systemie MacOS X jest to „OpenGL Framework”.
To właśnie ta część tłumaczy wywołania OpenGL, jak to robisz, na wywołania funkcji specyficznych dla sterownika w części sterownika opisanej w (2).
Wreszcie aktualna biblioteka API OpenGL, opengl32.dll w systemie Windows i na Unix /usr/lib/libGL.so; to głównie przekazuje polecenia do właściwej implementacji OpenGL.
Jak przebiega faktyczna komunikacja, nie można uogólniać:
W Uniksie połączenie 3 <-> 4 może się odbywać albo przez Sockets (tak, może i przechodzi przez sieć, jeśli chcesz) lub przez Shared Memory. W systemie Windows biblioteka interfejsów i klient sterownika są ładowane do przestrzeni adresowej procesu, więc to nie tyle komunikacja, ile proste wywołania funkcji i przekazywanie zmiennych / wskaźników. W MacOS X jest to podobne do Windows, tyle że nie ma oddzielenia między interfejsem OpenGL a klientem sterownika (to jest powód, dla którego MacOS X tak wolno nadąża za nowymi wersjami OpenGL, zawsze wymaga pełnej aktualizacji systemu operacyjnego, aby dostarczyć nowy struktura).
Komunikacja między 3 <-> 2 może przechodzić przez ioctl, odczytywać / zapisywać lub poprzez mapowanie pewnej pamięci do przestrzeni adresowej procesu i konfigurowanie MMU tak, aby wyzwalała kod sterownika, gdy tylko zmiany w tej pamięci są wykonywane. Jest to dość podobne w każdym systemie operacyjnym, ponieważ zawsze musisz przekroczyć granicę jądra / obszaru użytkownika: Ostatecznie przechodzisz przez wywołanie systemowe.
Komunikacja między systemem a GPU odbywa się za pośrednictwem magistrali peryferyjnej i definiowanych przez nią metod dostępu, czyli PCI, AGP, PCI-E itp., Które działają przez Port-I / O, Memory Mapped I / O, DMA, IRQ.
źródło
Nie jesteś daleko. Twój program wywołuje instalowalny sterownik klienta (który tak naprawdę nie jest sterownikiem, jest to biblioteka współdzielona w przestrzeni użytkownika). Spowoduje to użycie ioctl lub podobnego mechanizmu do przekazywania danych do sterownika jądra.
W następnej części zależy to od sprzętu. Starsze karty graficzne miały tak zwany „potok o ustalonej funkcji”. Na karcie graficznej były dedykowane miejsca w pamięci dla macierzy i dedykowany sprzęt do wyszukiwania tekstur, mieszania itp. Sterownik wideo ładował odpowiednie dane i flagi dla każdej z tych jednostek, a następnie ustawiał DMA do przesyłania danych wierzchołków (pozycja , kolor, współrzędne tekstury itp.).
Nowszy sprzęt ma rdzenie procesorów („shadery”) wewnątrz karty graficznej, które różnią się od procesora tym, że każdy z nich działa znacznie wolniej, ale jest ich znacznie więcej pracujących równolegle. W przypadku tych kart graficznych sterownik przygotowuje pliki binarne programu do działania w modułach cieniujących GPU.
źródło
Twój program nie jest kompilowany dla żadnego konkretnego GPU; jest po prostu dynamicznie połączony z biblioteką, która zaimplementuje OpenGL. Rzeczywista implementacja może obejmować wysyłanie poleceń OpenGL do GPU, uruchamianie oprogramowania awaryjnego, kompilowanie programów do cieniowania i wysyłanie ich do GPU, a nawet używanie funkcji zastępczych shaderów do poleceń OpenGL. Krajobraz graficzny jest dość skomplikowany. Na szczęście łączenie izoluje cię od większości złożoności sterowników, pozostawiając wykonawcom sterowników swobodę korzystania z dowolnych technik, które uznają za stosowne.
źródło
Kompilatory / konsolidatory C / C ++ robią dokładnie jedną rzecz: konwertują pliki tekstowe na serię kodów operacyjnych specyficznych dla maszyny, które są uruchamiane na procesorze. OpenGL i Direct3D to po prostu interfejsy API C / C ++; nie mogą magicznie przekształcić twojego kompilatora / linkera C / C ++ w kompilator / linker dla GPU.
Każda linia kodu C / C ++, którą napiszesz, zostanie wykonana na procesorze. Wywołania OpenGL / Direct3D będą wywoływać biblioteki C / C ++, statyczne lub dynamiczne, w zależności od przypadku.
Jedynym miejscem, w którym „program gpu” mógłby wejść do gry, jest sytuacja, gdy twój kod jawnie tworzy shadery. Oznacza to, że jeśli wykonasz wywołania API do OpenGL / D3D, które spowodują kompilację i linkowanie shaderów. Aby to zrobić, (w czasie wykonywania, a nie w C / C ++ kompilacji) generujesz lub ładujesz ciągi znaków, które reprezentują shadery w jakimś języku shaderów. Następnie przepychasz je przez kompilator modułu cieniującego i odzyskujesz obiekt w tym interfejsie API, który reprezentuje ten moduł cieniujący. Następnie zastosujesz jeden lub więcej shaderów do określonego polecenia renderowania. Każdy z tych kroków odbywa się jawnie w kierunku Twojego kodu C / C ++, który, jak wspomniano wcześniej, działa na procesorze.
Wiele języków shaderów używa składni podobnej do C / C ++. Ale to nie czyni ich odpowiednikiem C / C ++.
źródło