Platforma synchronizacji wyraźnie opisuje zależności między różnymi operacjami asynchronicznymi w systemie graficznym Androida. Framework udostępnia interfejs API, który umożliwia komponentom wskazywanie, kiedy bufory są zwalniane. Platforma zapewnia również umożliwia przekazywanie podstawowych elementów synchronizacji między sterownikami w jądrze do przestrzeni użytkownika i między procesami w tej przestrzeni.
Aplikacja może na przykład ustawić w kolejce zadania do wykonania na karcie graficznej. GPU zaczyna rysować obraz. Chociaż obraz nie został jeszcze narysowany w pamięci, wskaźnik bufora jest przekazywany do kompozytora okna wraz z ograniczeniem, które wskazuje, kiedy zakończy się praca na karcie graficznej. Okno kompozytorski rozpoczyna przetwarzanie z wyprzedzeniem i przekazuje zadanie do kontrolera wyświetlania. W podobny sposób procesor trzeba wykonać wcześniej. Gdy GPU zakończy działanie, kontroler wyświetlacza natychmiast wyświetli obraz.
Platforma synchronizacji umożliwia też implementatorom korzystanie z zasobów synchronizacji w ich własnych komponentach sprzętowych. Na koniec framework zapewnia widoczność potoku graficznego, co ułatwia debugowanie.
Wyraźna synchronizacja
Wyraźna synchronizacja umożliwia producentom i odbiorcom buforów graficznych sygnalizowanie, kiedy skończyli korzystać z bufora. Wyraźna synchronizacja jest implementowana w przestrzeni jądra.
Zalety bezpośredniej synchronizacji:
- Mniejsza zmienność zachowania na różnych urządzeniach
- Lepsza obsługa debugowania
- Ulepszone dane testowe
Platforma synchronizacji ma 3 typy obiektów:
sync_timeline
sync_pt
sync_fence
sync_timeline
sync_timeline
to monotonicznie rosnąca oś czasu,
dostawcy powinni wdrożyć dla każdej instancji sterownika, np. kontekst GL,
lub np. kontroler wyświetlacza 2D. sync_timeline
zlicza zadania przesłane do jądra dla konkretnego sprzętu.
sync_timeline
zapewnia gwarancje dotyczące kolejności operacji i umożliwia implementacje związane z konkretnym sprzętem.
Podczas implementacji sync_timeline
przestrzegaj tych wskazówek:
- Aby uprościć, podaj przydatne nazwy wszystkich kierowców, osi czasu i płodów i debugowaniu.
- Aby ułatwić odczytywanie danych debugowania, stosuj w czasoprzestrzeniach operatory
timeline_value_str
ipt_value_str
. - W razie potrzeby zastosuj funkcję fill
driver_data
, aby nadać bibliotekom w przestrzeni użytkownika, takim jak biblioteka GL, dostęp do prywatnych danych z osi czasu.data_driver
pozwala dostawcom przekazywać informacje o niezmiennychsync_fence
isync_pts
, aby tworzyć na ich podstawie wiersze poleceń. - Nie zezwalaj userspace na jawne tworzenie lub sygnalizowanie ogrodzenia. Wyraźnie tworzenie sygnałów/płodów skutkuje atakiem typu DoS, wstrzymuje działanie potoku.
- Nie uzyskuj dostępu do usług
sync_timeline
,sync_pt
lubsync_fence
. Interfejs API udostępnia wszystkie wymagane funkcje.
sync_pt
sync_pt
to pojedyncza wartość lub punkt na
sync_timeline
Punkt
ma 3 stany: aktywny, sygnalizowany i błąd. Punkty zaczynają się w stanie aktywnym i przechodzą do stanu sygnalizowania lub błędu. Jeśli na przykład obraz
konsument nie potrzebuje już bufora, pojawia się sygnał sync_pt
dzięki czemu producent obrazów wie, że może znowu zapisać go w buforze.
granica_synchronizacji
sync_fence
to zbiór wartości sync_pt
często
mają różne elementy nadrzędne sync_timeline
(np. dla wyświetlacza
kontroler i GPU). sync_fence
, sync_pt
i
sync_timeline
to główne podstawowe elementy, których sterowniki i przestrzeń użytkownika
których używają do informowania o zależności. Gdy pojawi się sygnał,
polecenia wydane przed ogrodzeniem są zapewne ukończone, ponieważ
sterownik jądra lub blok sprzętowy wykonuje polecenia po kolei.
Ramka synchronizacji umożliwia wielu konsumentom i producentom sygnalizowanie, że skończyli korzystać z bufora, przekazując informacje o zależnościach za pomocą jednego parametru funkcji. Ogrodzenia są wspierane przez deskryptor pliku i przekazywane z
z jednego jądra systemu operacyjnego. Na przykład bariery mogą zawierać 2 wartości sync_pt
, które wskazują, kiedy 2 oddzielne elementy korzystające z obrazu skończyły odczyt bufora. Gdy ogrodzenie jest sygnalizowane, producenci obrazu wiedzą,
przez konsumentów.
Ogrodzenia, takie jak wartości sync_pt
, zaczynają być aktywne i zmieniają stan w zależności od tego,
na stan ich punktów. Jeśli wszystkie wartości sync_pt
zostaną zasygnalizowane,
sync_fence
otrzymuje sygnał. Jeśli jeden sync_pt
spadnie
w stan błędu, cały element sync_fence
zawiera stan błędu.
Po utworzeniu ogrodzenia członkostwo w sync_fence
jest nieodwracalne. Aby uzyskać więcej niż jeden punkt ogrodzenia, scalanie
polega na tym, że do trzeciego ogrodzenia dodaje się punkty z dwóch różnych płotów.
Jeśli jeden z tych punktów został zgłoszony w pierwszym ogrodzeniu, a drugi nie, trzeci ogród również nie będzie zgłoszony.
Aby wdrożyć jawną synchronizację, podaj te informacje:
- Podsystem w przestrzeni jądra, który wdraża mechanizm synchronizacji dla konkretnego sterownika sprzętowego. Kierowcy, którzy muszą mieć świadomość płotu,
wszystko, co uzyskuje dostęp do narzędzia Hardware Composer lub się z nim komunikuje.
Najważniejsze pliki to:
- Implementacja podstawowa:
kernel/common/include/linux/sync.h
kernel/common/drivers/base/sync.c
- Dokumentacja:
kernel/common/Documentation/sync.txt
- Biblioteka do komunikacji z przestrzenią jądra w
platform/system/core/libsync
- Implementacja podstawowa:
- Dostawca musi podać odpowiednie bariery synchronizacji jako parametry funkcji
validateDisplay()
ipresentDisplay()
w interfejsie HAL. - 2 rozszerzenia GL związane z fence (
EGL_ANDROID_native_fence_sync
iEGL_ANDROID_wait_sync
) oraz obsługa fence w sterowniku graficznym.
Studium przypadku: wdrażanie sterowników dla reklam displayowych
Aby używać interfejsu API obsługującego funkcję synchronizacji, opracuj sterownik wyświetlacza, który ma funkcję bufora wyświetlacza. Zanim istniała platforma synchronizacji, ta funkcja otrzymywała obiekty dma-buf
, umieszczała je na wyświetlaczu i blokowała, gdy bufor był widoczny. Na przykład:
/* * assumes buffer is ready to be displayed. returns when buffer is no longer on * screen. */ void display_buffer(struct dma_buf *buffer);
W przypadku platformy synchronizacji funkcja display_buffer
jest bardziej złożona. Po umieszczeniu bufora na wyświetlaczu zostaje on powiązany
ogrodzeniem wskazującym, kiedy bufor będzie gotowy. Możesz ustawić zadanie w kolejce i zainicjować jego wykonywanie, gdy zniknie blokada.
Dodanie zadania do kolejki i jego uruchomienie po usunięciu bariery nie powoduje blokowania niczego. Od razu zwracasz własną siatkę, co gwarantuje, że bufor nie będzie widoczny na ekranie. Gdy umieszczasz bufory w kolejce, jądro wymienia zależności z ramami synchronizacji:
/* * displays buffer when fence is signaled. returns immediately with a fence * that signals when buffer is no longer displayed. */ struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence *fence);
Integracja z synchronizacją
W tej sekcji opisano, jak zintegrować framework synchronizacji w przestrzeni jądra z częściami frameworka Androida w przestrzeni użytkownika i sterownikami, które muszą się ze sobą komunikować. Obiekty przestrzeni jądra są reprezentowane jako deskryptory plików w przestrzeń użytkownika.
Konwencje integracji
Stosuj konwencje interfejsu HAL Androida:
- Jeśli interfejs API udostępnia deskryptor pliku odnoszący się do elementu
sync_pt
, sterownik dostawcy lub kod HAL korzystający z interfejsu API musi zamykać deskryptor pliku. - Jeśli sterownik dostawcy lub HAL przekazuje do funkcji interfejsu API deskryptor pliku zawierający
sync_pt
, sterownik dostawcy lub HAL nie może zamykać tego deskryptora. - Aby nadal używać deskryptora pliku ogrodzenia, sterownik dostawcy lub HAL musi go powielić.
Nazwa obiektu ogrodzenia zmienia się za każdym razem, gdy przechodzi przez BufferQueue.
Obsługa ogrodzenia jądra umożliwia nadawanie ogrodzeniom nazw w postaci ciągów znaków, więc framework synchronizacji używa nazwy okna i indeksu bufora, które są umieszczane w kole, aby nazwać ogrodzenie, np. SurfaceView:0
. Ten
jest pomocne w debugowaniu w celu identyfikacji źródła zakleszczeń w postaci pojawiających się nazw
w danych wyjściowych /d/sync
i raportach o błędach.
Integracja ANativeWindow
Okno typu ANativeWindow jest świadome ogrodzenia. Parametry bariery: dequeueBuffer
, queueBuffer
i cancelBuffer
.
Integracja z OpenGL ES
Synchronizacja OpenGL ES opiera się na 2 rozszerzeniach EGL:
EGL_ANDROID_native_fence_sync
umożliwia tworzenie lub owijanie natywnych opisów pliku ogrodzenia Androida w obiektachEGLSyncKHR
.EGL_ANDROID_wait_sync
pozwala na blokowanie GPU, a nie procesora, co powoduje oczekiwanie GPU naEGLSyncKHR
. RozszerzenieEGL_ANDROID_wait_sync
jest takie samo jak rozszerzenieEGL_KHR_wait_sync
.
Aby korzystać z tych rozszerzeń niezależnie, zaimplementuj tag
EGL_ANDROID_native_fence_sync
rozszerzenie i powiązane
i obsługą jądra systemu operacyjnego. Następnie włącz: EGL_ANDROID_wait_sync
w sterowniku. Rozszerzenie EGL_ANDROID_native_fence_sync
składa się z osobnego natywnego obiektu ogrodzenia EGLSyncKHR
. W rezultacie rozszerzenia, które mają zastosowanie do istniejących typów obiektów EGLSyncKHR
, nie muszą mieć zastosowania do obiektów EGL_ANDROID_native_fence
, co zapobiega niepożądanym interakcjom.
Rozszerzenie EGL_ANDROID_native_fence_sync
używa odpowiedniego natywnego atrybutu pliku opisu ogrodzenia, który można ustawić tylko w momencie tworzenia. Nie można go zapytać bezpośrednio z istniejącego obiektu synchronizacji. Ten atrybut
można ustawić na jeden z dwóch trybów:
- Prawidłowy deskryptor pliku ogrodzenia zawiera w obiekcie
EGLSyncKHR
istniejący natywny deskryptor pliku ogrodzenia Androida. - -1 tworzy natywny plik deskryptora ogrodzenia na Androida na podstawie obiektu
EGLSyncKHR
.
Użyj wywołania funkcji DupNativeFenceFD()
, aby wyodrębnić parametr
Obiekt EGLSyncKHR
z deskryptora natywnego pliku Android fence.
Daje to ten sam wynik co zapytanie dotyczące atrybutu zbioru, ale jest zgodne z konwencją, że odbiorcy zamykają nawias (stąd duplikacja operacji). Na koniec zniszczenie obiektu EGLSyncKHR
powoduje zamknięcie atrybutu wewnętrznego ogrodzenia.
Integracja z Composerem
Narzędzie do konfiguracji sprzętu obsługuje 3 typy ogrodzeń synchronizacji:
- Zdobywanie progów jest przekazywane wraz z buforami wejściowymi do
połączenia
setLayerBuffer
isetClientTarget
. Reprezentują one oczekujące zapisanie do bufora i muszą być sygnalizowane, zanim SurfaceFlinger lub HWC spróbuje odczytać z powiązanego bufora, aby wykonać kompozycję. - Ogrodzenia zwalniające są pobierane po wywołaniu
presentDisplay
za pomocą wywołaniagetReleaseFences
. Reprezentują one oczekujący odczyt z poprzedniego bufora w tej samej warstwie. A sygnały dotyczące ogrodzenia sygnału zwalniającego, gdy HWC nie używa już poprzedniego bufora, ponieważ bieżący bufor zastąpił poprzedni bufor na wyświetlaczu. Blokady wersji są przekazywane z powrotem do aplikacji razem z poprzednimi buforami, zostaną zastąpione w bieżącej kompozycji. Aplikacja musi poczekać do rozluźnić granicę przed zapisaniem w buforze nowej zawartości, które zostały im zwrócone. - Obecne ogrodzenia są zwracane w ramach wywołania funkcji
presentDisplay
, po jednym na każdy kadr. Prezentuj ogrodzenia, pokazując, kiedy czy kompozycja tej klatki zakończy się lub na przemian, gdy nie jest już potrzebny wynik kompozycji poprzedniej klatki. W przypadku fizycznych wyświetlaczy funkcjapresentDisplay
zwraca obecne ogrodzenia, gdy bieżąca klatka jest wyświetlana na ekranie. Po zwróceniu obecnych ograniczeń można ponownie zapisać dane w buforze docelowym SurfaceFlinger (jeśli to konieczne). W przypadku wyświetlaczy wirtualnych zwracane są aktualne zasięgi, gdy można bezpiecznie odczytać dane z bufora wyjściowego.