Popularny post MR1979 Styczeń 8, 2022 Autor tematu Popularny post Udostępnij Styczeń 8, 2022 Aktualnie przenoszę moją bibliotekę do grafiki 2D którą kiedyś pisałem na płytkę STM32F429 Discovery. Mam już zaimplementowane następujące funkcje: - piksel, linia, prostokąt, okrąg (oraz wersje wypełnione) - tekst z antyaliasingiem - wyświetlanie bitmapy z bufora w pamięci Wszędzie gdzie możliwe staram się wykorzystywać akcelerację sprzętowa z użyciem DMA2D Wyświetlanie można skonfigurować jako: - pojedyncza warstwa z dwoma buforami ramki - dwie warstwy z dwoma buforami ramki każda Warstwy miksowane są sprzętowo przez kontroler LTDC w momencie przesyłania obrazu do wyświetlacza. Ogólna zasada działania: - W dowolnej chwili dla każdej warstwy jedna ramka się wyświetla a druga jest dostępna do edycji - Gdy edycja ramki jest zakończona, odpowiednią funkcją oznaczamy ją jako "gotową" - Silnik 2D przełącza ramki oznaczone jako gotowe w momencie gdy kontroler LTDC skończy wyświetlać klatkę (aby uniknąć tzw. "tearing effect") Aktualnie największym problemem z jakim się borykam jest to, że gdy procesor nie zdąży wygenerować kolejnej klatki w czasie 1/30 sekundy to jest ona oznaczana jako gotowa za późno i spóźnia się z wyświetleniem na najbliższym cyklu odświeżania ekranu. W ten sposób pomimo odświeżania ekranu 30Hz, rzeczywista ilość klatek na sekundę od razu spada spada do 15. Dodałem też sterowanie podświetlaniem z użyciem PWM Ekran jest skonfigurowany na częstotliwość odświeżania 30Hz (zegar 13,5MHz). To oznacza że: - przy jednej warstwie wymagana przepustowość danych do LTDC wynosi: 800 x 480 x 4(bo RGBA) x 30 = 46MB/s - przy dwóch warstwach potrzebna przepustowość wynosi 92MB/s SDRAM jest taktowany z częstotliwością 108MHz przy 16bitowej magistrali teoretycznie daje to około 200MB/s. Pozostaje więc pewien zapas na generowanie klatek oraz pozostałe funkcje. Poniżej filmik z kolejnych testów: Pozdr, Marek 3 Cytuj Link do komentarza Share on other sites More sharing options...
Popularny post MR1979 Styczeń 14, 2022 Autor tematu Popularny post Udostępnij Styczeń 14, 2022 Postępy z placu boju 🙂 Zmiany w bibliotece graficznej: - Możliwość użycie trzech buforów ramki na warstwę - dzięki temu renderowanie i wyświetlanie grafiki odbywa się niezależnie => wyższy FPS - Wiele optymalizacji kodu, wykorzystanie DMA2D w trybie przerwań Konfiguracja sprzętu: - Zmiana CAS SDRAM z 3 na 2, oraz przeliczenie pozostałych parametrów kontrolera FMC zgodnie z notą katalogową pamięci (wcześniej było copy/paste z konfiguracji płytki discovery STM32F429) - Dostrojenie parametrów horizontal/vertical frontporch w LTCS aby uzyskać odświeżanie dokładnie 30 klatek/s (wcześniej było niecałe 29) przy zegarze 13,5MHz Minikonsolka widziana jest jako Mass Storage Device, więc można wgrywać / kasować pliki na karcie SD bezpośrednio z komputera Napisałem bibliotekę do wgrywania i katalogowania zasobów z karty SD. Zasoby wgrywane są bezpośrednio do pamięci SDRAM w ramach przydzielonej przestrzeni na zasoby. Katalogowanie polega na tym że tworzona jest tablica w której znajdują się informacje w jakim miejscu SDRAM dany zasób się zaczyna i jaką ma wielkość. W kolejnych bibliotekach będę się odwoływał do tego katalogu. Poniżej zdjęcie konsolki wyświetlającej przykładowy tileset do gry platformowej: Pozdrawiam, Marek 3 Cytuj Link do komentarza Share on other sites More sharing options...
Gieneq Styczeń 17, 2022 Udostępnij Styczeń 17, 2022 @MR1979 projekt robi wrażenie 🙂 podziwiam, że wiesz jak napisać do tego program. Dnia 14.01.2022 o 17:58, MR1979 napisał: Możliwość użycie trzech buforów ramki na warstwę - dzięki temu renderowanie i wyświetlanie grafiki odbywa się niezależnie Mógłbyś rozwinąć ten temat? Pamiętam jak pisałem coś w SDL albo Allegro i na pojedynczym buforze było bardzo źle, bo nie można wysłać do wyświetlenia gdy jeszcze nie skończyłem rysowania. Dla 2 buforów było dobrze - na jeden rysowałem, a drugi był w tym czasie rysowany. Ta sama sztuczka jest użyta w kursie STM32. A dlaczego tu masz 3 bufory? Cytuj Link do komentarza Share on other sites More sharing options...
Popularny post MR1979 Styczeń 17, 2022 Autor tematu Popularny post Udostępnij Styczeń 17, 2022 (edytowany) 3 godziny temu, Gieneq napisał: Mógłbyś rozwinąć ten temat? Pamiętam jak pisałem coś w SDL albo Allegro i na pojedynczym buforze było bardzo źle, bo nie można wysłać do wyświetlenia gdy jeszcze nie skończyłem rysowania. Dla 2 buforów było dobrze - na jeden rysowałem, a drugi był w tym czasie rysowany. Ta sama sztuczka jest użyta w kursie STM32. A dlaczego tu masz 3 bufory? Od razu przepraszam za anglojęzyczne nazwy, ale większość materiałów jest po angielsku, i nawet nie wiem jak poprawnie przetłumaczyć te nazwy na polski. Załóżmy że mamy projekt który wyświetla dane na ekran/monitor z częstotliwością odświeżania 30Hz. Czyli mamy nową klatkę co 1/30s (33ms). Załóżmy też że sam obraz jest przesyłany (linijka po linijce) do monitora przez 23ms, a przez pozostałe 10ms jest tzw blanking interval - czyli czas w którym transmisja z pamięci do wyświetlacza się nie odbywa. = JEDEN BUFOR = W takiej sytuacji wyświetlasz obraz i renderujesz kolejną klatkę w tym samym obszarze pamięci. Nie możemy tego robić w dowolnym momencie bo dojdzie do sytuacji gdy na ekranie pojawi się połowa poprzedniej klatki i połowa nowej klatki (tzw. tearing effect). Aby uniknąć teraring effect, bufor ramki należy aktualizować podczas blanking interval. Czyli tak na prawdę na każdą klatkę mamy tylko 10ms na wyrenderowanie kolejnej klatki. Przez pozostały czas procesor musi czekać. Zwykły procesor może zająć się innymi rzeczami, ale już dedykowany procesor graficzny marnuje swój potencjał. Podsumowując na każdą sekundę procesor ma 300ms na renderowanie grafiki. Pozostałe 700ms w najgorszym przypadku się marnuje. Jeszcze gorzej jest gdy nie zmieścimy się z renderingiem w tych 10ms. Wtedy tearing effect jest nieunikniony, albo w ogóle nie będziemy w stanie wyświetlić obrazu. = DWA BUFORY = W takiej sytuacji wyświetlasz obraz z jednego bufora, natomiast w tym czasie renderujesz nową klatkę w drugim buforze. Gdy skończysz rendering. Oznaczasz swoją klatkę jako ukończoną i w momencie blanking interval następuje przełączenie buforów (prosta operacja na wskaźnikach). I tak w kółko. Tearing effect nas nie dotyczy. Idealnie, prawda?? Otóż nie do końca. Sytuacja 1 - czas renderowania klatki trwa 30ms. Renderujemy klatkę -> oznaczamy jako skończoną ->po 3ms następuje blanking interval i przełączamy bufory -> możemy zacząć renderować kolejną klatkę w drugim buforze. W takiej sytuacji w każdej sekundzie procesor może zająć się renderowaniem przez 900ms, a pozostały czas 100ms się marnuje. FPS wynosi 30 klatek na sekundę. Sytuacja 2 - czas renderowania klatki trwa 10ms. Renderujemy klatkę -> oznaczamy jako skończoną -> po 23ms następuje blanking interval i przełączamy bufory -> zaczynamy renderować kolejną klatkę. W takiej sytuacji na każdą sekundę przypada 300ms renderowania. Pozostałe 700ms się marnuje. FPS wynosi 30 klatek na sekundę. Sytuacja 3 - czas renderowania klatki trwa 35ms. Renderujemy klatkę -> oznaczamy jako skończoną -> spóźniamy się na blanking interval o 2ms, więc musimy czekać na kolejny aż 31ms. Przełączamy bufory -> zaczynamy kolejną klatkę W takiej sytuacji drastycznie spada nam wydajność całego systemu. W każdej sekundzie możemy poświęcić tylko 525ms na rendering, a pozostałe 475ms się marnuje. Pomimo że spóźniliśmy się tylko o 2ms FPS spada od razu do 15 klatek na sekundę. = TRZY BUFORY = Trzy bufory pozwalają na asynchroniczne renderowanie i wyświetlanie obrazu. Nie musimy czekać na blanking interval, więc moc obliczeniowa procesora się nie marnuje. Dzieje się tak bo zawsze jest jakiś bufor dostępny do zapisu / renderowania kolejnej klatki. Sytuacja 1 - czas renderowania klatki trwa 30ms. Renderujemy klatkę -> oznaczamy jako skończoną -> renderujemy kolejną klatkę -> oznaczamy jako skończoną itd... W tej sytuacji w każdej sekundzie wykorzystujemy 1000ms na rendering. Czyli nic się nie marnuje. A FPS programu wynosi: 33 klatki na sekundę. Oznacza to że każdej sekundzie wyświetlacz pominie 3 klatki, ale nie będzie to miało wpływu na komfort użytkowania programu. Sytuacja 2 - czas renderowania klatki trwa 10ms. Renderujemy klatkę -> oznaczamy jako skończoną -> renderujemy kolejną klatkę -> oznaczamy jako skończoną itd... W tej sytuacji w każdej sekundzie wykorzystujemy 1000ms na rendering. Czyli nic się nie marnuje. A FPS programu wynosi: 100 klatek na sekundę. Oznacza to że w każdej sekundzie wyświetlacz pominie 70 klatek, ale nie będzie to miało wpływu na komfort użytkowania programu. Sytuacja 3 - czas renderowania klatki trwa 35ms. Renderujemy klatkę -> oznaczamy jako skończoną -> renderujemy kolejną klatkę -> oznaczamy jako skończoną itd... W tej sytuacji w każdej sekundzie wykorzystujemy 1000ms na rendering. Czyli nic się nie marnuje. A FPS programu wynosi: 28 klatek na sekundę. Oznacza to że wyświetlacz w każdej sekundzie 2 klatki wyświetli dwa razy pod rząd. Na ekranie będzie więc 28 FPS. Dla porównania przy podwójnym buforowaniu w tej samej sytuacji wydajność spadała od razu do 15 FPS. Podsumowując 3 bufory są najlepszym rozwiązaniem jeżeli chodzi o szybkość i płynność wyświetlania. Pozwalają też optymalnie wykorzystać moc obliczeniową procesora. Główną wadą jest wymóg dużej ilości pamięci. Do aplikacji gdzie jest wyświetlany dynamicznie zmieniający się obraz jest to najlepsze rozwiązanie (np konsolka do gier). W sytuacji gdy wyświetlamy tylko statyczny UI z małą ilością animacji, wystarczą 2 bufory, a nawet jeden (przy odpowiedniej optymalizacji programu). Pozdr, Marek Edytowano Styczeń 17, 2022 przez MR1979 2 2 Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
MR1979 Luty 9, 2022 Autor tematu Udostępnij Luty 9, 2022 (edytowany) Witam! Dawno tu nic nie pisałem, ale prace trwają. Przetestowałem już wszystkie komponenty konsolki poza modułem WiFi. I powoli zabieram się za pisanie boot loadera. Plan jest taki: 1. Gdy uruchomimy konsolkę normalnie to bootloader uruchomi ostatnio wczytaną aplikację / grę 2. Gdy uruchomimy konsolkę z wciśniętym przyciskiem menu to pojawi się możliwość wyboru nowej aplikacji / gry z karty SD. USB zostanie też wtedy skonfigurowane w tryb pamięci masowej z możliwością odczytu i zapisu do karty SD. Będzie też można skonfigurować konsolkę pod swoje wymagania (np domyślna jasność ekranu / sieć i hasło WiFi / domyślny poziom dzwięku / kalibracja joysticka itp. Ale bootloader nie będzie ograniczał się tylko do samego uruchamiania / wgrywania aplikacji oraz konfiguracji. Będzie też zawierał wgrane podstawowe drivery do konsoli (np podstawowe funkcje obsługi grafiki i innych peryferiów). Lista wskaźników do poszczególnych funkcji będzie przekazywana do uruchamianej aplikacji. Także aplikacje będą mogły być pisane o jeden poziom abstrakcji wyżej (bez konieczności do odwoływania się do sprzętu) Oto zaplanowana uproszczona mapa pamięci: PAMIĘĆ FLASH: Początkowe 128kB będzie przeznaczone na bootloader oraz drivery. Pozostałe 1920kB będzie dostępne na aplikacje. PAMIĘĆ SRAM: Końcowe 48kB będzie przeznaczone na bootloader oraz drivery. Tam też będzie się znajdować tablica wskażników do funkcji driverów, oraz zmienne i struktury potrzebne do obsługi grafiki / dźwięku / klawiatury itp. Dla aplikacji pozostanie 464kB. PAMIĘĆ SDRAM: Pamięć buforów grafiki będzie "przyklejona" do górnej granicy SDRAM. Wieklość pamięci na framebufory zależy od wybranego trybu graficznego oraz metody buforowania (2 lub 3 bufory). Pozostała część będzie do wykorzystania dla aplikacji. Na zakończenie kolejny krótki filmik: Pozdrawiam, Marek Edytowano Luty 9, 2022 przez MR1979 2 Cytuj Link do komentarza Share on other sites More sharing options...
FlyingDutch Luty 9, 2022 Udostępnij Luty 9, 2022 Cześć @MR1979 Mam jedno pytanie: Czy przewidujesz na tej konsoli emulację jakichś innych starych konsol? Mnie bardzo by interesowała emulacja pierwszej wersji NES (8-mio bitowej). Jeśli dobrze rozumiem, to dostarczasz dla tej konsoli własne biblioteki do multimediów? Pozdrawiam Cytuj Link do komentarza Share on other sites More sharing options...
Popularny post MR1979 Luty 9, 2022 Autor tematu Popularny post Udostępnij Luty 9, 2022 @FlyingDutch Myślałem nad tym. NES chodzi w rozdzielczości 256x240, więc spokojnie dałoby radę takie gry uruchomić. Problemem jest upscalowanie takiego obrazu do 800x480. DMA2D nie wspiera takiej operacji, więc całość operacji spadłaby na uC. Gdybym miał coś takiego robić to bufor małej ramki trzymałbym w SRAM (256x240x8bit = 61kB), a póżniej gdy już ramka będzie gotowa to pojedynczą funkcją scalował i kopiował bym do buforu LTDC. Ale zanim coś takiego będzie możliwe to najpierw muszę ogarnąć ten boot loader. Niestety 16bitowa magistrala danych do SDRAM jest wąskim gardłem całej konstrukcji przy tak dużym wyświetlaczu. Jak będę kiedyś robił kolejną wersję to na pewno będzie magistrala 32bit oraz uC z serii H7. 4 Cytuj Link do komentarza Share on other sites More sharing options...
Harnas Luty 10, 2022 Udostępnij Luty 10, 2022 Przeczytałem właśnie komentarz o rodzaju buforowania i czy nie byłoby jeszcze lepiej przełączać się między podwójnym i potrójnym w zależności od ilości FPS? Przy krótkim czasie renderowania klatki przełączalibyśmy się na podwójne buforowanie i po wyrenderowaniu usypialibyśmy CPU do czasu blanking interval. Wtedy oszczędzalibyśmy nieco energii i rdzeń byłby nieco chłodniejszy. Cytuj Link do komentarza Share on other sites More sharing options...
MR1979 Luty 10, 2022 Autor tematu Udostępnij Luty 10, 2022 27 minut temu, Harnas napisał: Przeczytałem właśnie komentarz o rodzaju buforowania i czy nie byłoby jeszcze lepiej przełączać się między podwójnym i potrójnym w zależności od ilości FPS? Przy krótkim czasie renderowania klatki przełączalibyśmy się na podwójne buforowanie i po wyrenderowaniu usypialibyśmy CPU do czasu blanking interval. Wtedy oszczędzalibyśmy nieco energii i rdzeń byłby nieco chłodniejszy. Można coś takiego robić, ale po co walczyć z problemami które nie istnieją. Doprowadziłoby to do niepotrzebnej komplikacji kodu. uC z rodziny STM32 praktycznie się nie nagrzewają. Wolny czas procesora lepiej wykorzystać na obsługę np logiki gry. 1 Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!