kategoria: Asembler
[#1] Blitter ustawiany przez Copper oraz kwestia CPU.
Hej!
Chciałem tylko, dla osób zainteresowanych programowaniem w asm informacyjnie napisać moje "odkrycie" związane z Blitterem i Copperem.

Otóż bardzo zastanawiało mnie, czy jeżeli używamy Coppera do ładowania rejestrów Blittera, czy jest wówczas możliwe - gdy taka Copperlista jest aktywna - do używania Blittera przez CPU (może się zdarzyć taka konieczność).

Problem polegał na tym, że Copper i CPU nie mogą równocześnie korzystać z Blittera.

Rozwiązanie okazało się bardzo proste! Otóż ponieważ wiadome jest, że istnieje specjalny rejestr COPCON. Posiada on bit CDANG, który - jak zapalony - umożliwia Copperowi dostęp do Blittera.

Okazuje się, że przydaje się to właśnie w tej sytuacji. Jeżeli procesor zgasi ten bit (i tylko on może to zrobić, bo COPCON nie jest dostępny dla Coppera!), to wówczas Copper straci możliwość modyfikacji rejestrów Blittera. Wtedy CPU może zaczekać, aż Blitter skończy pracę, uruchomić Blitter, a następnie na powrót włączyć ten bit.

Wówczas nie będzie żadnego konfliktu między CPU i Copperem. Jest to rzecz, której bardzo łatwo się domyślić, ale w dokumentacji nie jest to opisane wprost.

Nawet doświadczeni koderzy o tym nie wiedzieli (pytałem się o to w grupie).
[#2] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@Hexmage960, post #1

A możesz pokazać przykład zastosowania tego odkrycia ?
[#3] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@Norbert, post #2

Swego czasu napisałem mały framework bazujący na potrójnym buforowaniu ekranu, który realizował rysowanie przy użyciu Coppera.

1. CPU przygotowywał copperlistę z operacjami bitblit dla Blittera (wypełniał dane do operacji, inicjował, oczekiwał na zakończenie); nie było przeciwskazań, żeby te operacje wylądowały w copperliście 'ciurkiem' (np. 20 kopiowań obiektów).
2. Copper wykonywał całą pracę nie obciążając CPU (jeśli dobrze pamiętam, była instrukcja wait na przerwanie zakończenia każdej operacji przez Blitter).
3. Narysowany ekran był wyświetlany.

Całość miała wspaniałą wydajność, CPU pracował z pełną prędkością, Blitter też.

Ostatnia aktualizacja: 18.01.2018 07:08:10 przez drsky
[#4] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@drsky, post #3

To fajnie jakby kolega odnalazl ta procedure i wrzucil jako przyklad do nasladowania.
Malo ktory koder tutaj sie udziela a takie rzeczy dla potomnych, to na wage zlota sa. ok, racja
[#5] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@drsky, post #3

yyy... mówisz o OCS/ECS?

Jak blitter (pobieranie z i zapis do bitplane'ów) i copper (pobieranie instrukcji) pracują to wpierdzielają dostęp do CHIPu, więc jak masz powiedzmy konfig 512+512 to cpu jest w stanie się swobodnie dopchać do RAMu tylko wtedy gdy copper czeka i blitter nie pracuje - w innym przypadku jest marginalizowany.

Policzmy sobie. Sam setup blittera jest kosztowny: 4 32-bitowe rejestry bltpt (a więc 2 MOVE'y na każdy), 4 rejestry mod, 2xbltcon i pewnie coś o czym nie pamiętam daje około 14 MOVE'ów, na 4bpp ekranie jeden MOVE wykonuje się 8 loresowych pikseli więc jesteś w stanie setupować blitter najszybciej co 112 pikseli - 3x na linię, ale najpierw musi skończyć swoją pracę. Do tego dodaj czekanie na blit - takie 16x16 to 64 wordy na jednym bitplanie, przy 4 bitplane'ach masz 256 wordów do ustawienia, jak masz odpalone wszystkie kanały to jeden word idzie w 8 cykli, ale liczmy mniej niekorzystnie, że 4, więc 1024 cykli. Na widzialnej linii masz 76 cykli DMA, przy 4bpp połowę zjada wyświetlanie ekranu, zostaje 38 cykli na linię, część z tego zje cpu (ale pomińmy), część copper (marginalne jak czeka), przez co narysowanie 16x16 zajmuje jeśli dobrze liczę coś koło 27 linii - 10 bobów 16x16, a jeszcze wypadałoby je zamazać przed ponownym narysowaniem, więc każdy blitowany dwukrotnie - więc 5 ruchomych bobów 16x16 blitowanych przez maskę. Źle liczę? ;)

Specjalnie założyłem tylko widzialną linię w cyklach na linię - bo ileś czasu zajmie sama logika gry, ileś zajmie napisanie nowej copperlisty w chipie.

Co do głównego tematu - jeśli zlecasz blity to jednak zakładasz, że one się wykonają. Jeśli wyłączysz na jakiś czas dostęp do blittera copperowi to coś nie zostanie narysowane, bo któreś rozkazy zostaną zignorowane. Bierzesz to pod uwagę?

Ostatnia aktualizacja: 18.01.2018 09:44:22 przez teh_KaiN

Ostatnia aktualizacja: 18.01.2018 09:47:29 przez teh_KaiN
[#6] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #5

Kolega to znany autor publikacji "Asm-One", czy "Asembler dla początkujących".
[#7] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@Hexmage960, post #6

Tym bardziej się ucieszę jeśli się okaże, że źle widzę rzeczy, nie mam racji i da radę wycisnąć więcej ;)
[#8] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #5

Oczywiście, że się wykonują współbieżnie. Przecież dostęp, z tego co pamiętam jest miltipleksowany a nie na wyłączność dla danego układu. Cały trik polega na tym, żeby:
1) CPU nie czekał aktywnie na zakończenie operacji przez blittera,
2) alternatywnie CPU nie korzystał z przerwań generowanych przez blitter (brak zmiany kontekstu, konieczności zrzucania rejestrów na stos),
3) CPU pracował z maksymalną wydajnością aby wygenerować copperlistę (przypomina to współczesne plany wykonania kompozycji dla GPU).

Z drugiej strony taka copperlista nadzorująca blittera nie jest zbyt duża (po prostu sporo instrukcji MOVE), konkurencja dostępu jest nikła.

Niestety nie mam obecnie kodów i małe prawdopodobieństwo że się po 20tu latach i tylu samo przeprowadzkach odnajdą. Ale ogólny zarys dość dobrze pamiętam. Kluczem tutaj było m.in. użycie drugiego adresu copperlisty, który przestawiało się na zakończenie copperlisty aktywnej.

Wada (i jednocześnie zaleta) to konieczność wykonania wszystkich blitów w czasie trwania 1 ramki (1/50s dla PAL).

Ostatnia aktualizacja: 18.01.2018 09:49:05 przez drsky
[#9] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #5

Widzę, że się rozminęliśmy w komentarzach. Niestety nie mam czasu przysiąć nad Twoimi wyliczeniami (przyznam, że dawno na Amidze nie programowałem), jednak z tego co pamiętam mają sens. Wątpliwości mam jedynie dlaczego liczysz 64 wordy na blit 16x16? Może tutaj jest błąd w obliczeniach? Z tego co pamiętam uzyskiwałem dużo lepsze rezultaty niż te 5 ruchomych bobów.

Aż chyba spróbuję w wolnej chwili napisać na nowo :)
[#10] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@drsky, post #9

Sorry, tak, 16 wordów nie 64. No, to masz 20 bobów 16x16 w korzystnych warunkach. ;)
[#11] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #10

Co jest już znacząco więcej. W przypadku posiadania pamięci FAST można już wtedy całość przydzielić blitterowi, wtedy czasy dla hblank i vblank są do wykorzystania. A samą copperlistę nawet robić w FAST potem kopiować do CHIP.

W ramach ciekawostki, podobne podejście było wykorzystywane m.in. w Hardwired (48 glenz faces), ale dla grafiki wektorowej.

W ramach eksperymentów można próbować jeszcze metod hybrydowych (część grafiki generować za pomocą CPU - w szczególności drobniejsze elementy) - ale ma to sens tylko w przypadku niewielkich obiektów i z użyciem pamięci FAST.
[#12] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@drsky, post #11

Aż się chyba szarpnę żeby przeimplementować robienie blitów na coppera. Już mi się nawet w głowie układa gdzie bym mógł przyoszczędzić - interleaved mocno upraszcza, zamazywanie bobów powinno być ciut szybsze bo mniej kanałów.

Teraz nie mogę skupić się w pracy tylko myślę nad kodem amigowym. Dzięki! ;)
[#13] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #10

Sprawdź sobie: link.

dla 4bpl to masz w teorii około 46 bobów 16x16.

W Blazing Guns 25 bobów 16x16x5 bez problemu dawało radę. Oczywiście mówimy tylko od wyświetlaniu ich. Logika gry i inne rzeczy też swoje zżerają.
[#14] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@Hexmage960, post #1

Metoda znana i wykorzystywana przez wielu. (po przeczytaniu pierwszych 13 postów.)
[#15] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@asman, post #13

Odkopmy ten temat, bo w końcu usiadłem do implementacji sterowania blitterem przy pomocy coppera. Przy okazji parę optymalizacji jakie wyszły po drodze. Trochę się pożalę, ale może ktoś mi coś podpowie.

W tej chwili mój kod wygląda tak, że najpierw wszystkie boby hurtem są zamalowywane zapamiętanym tłem na ich starej pozycji (D := A), potem tło na nowej pozycji jest zapamiętywane (D := A), potem rysowane z maską (D := AB + ~A C).

Robiąc ciche założenie że boby mają ten sam rozmiar, można zrobić wspólny bufor na zapamiętywanie tła. Dzięki temu w pierwszym kroku ustawia się raz bltpta i dla kolejnych bobów tylko zmienia się bltptd, w drugim kroku na odwrót. Dodatkowo większości rejestrów nie trzeba ponownie ustawiać, nieznaczna optymalizacja ale zawsze coś.

I to wszystko fajnie działa, tylko że wyłącznie gdy się ma podwójne buforowanie. Ja w swojej grze mam teraz pojedyncze i problem polega na tym, że operacje blitowania zajmują trochę czasu przez co na części ekranu nie mogą znajdować się boby bo inaczej jest ryzyko że nie zostaną narysowane. Rysunek poglądowy poniżej.



Więc myślę sobie dalej, że undraw/save/draw trzeba by rozdzielić i zrobić oddzielnie dla każdego boba - dojdzie trochę instrukcji coppera ale nie za dużo. Wtedy można wydzielić dla każdego z nich czas kiedy blit może nastąpić - przed wyświetleniem (ale nie tuż przed bo do końca się nie odmaluje) lub już za. Pomyślałem więc o kolejce priorytetowej (w oparciu o kopiec), tak by przy tworzeniu copperlisty były zdejmowane boby w kolejności wyświetlania. I to by pewnie działało, gdyby nie problemy:

- boby mogą na siebie nachodzić - odrysowanie tła nachodzącego zamaże ten który jest pod nim
- jedne są rysowane wyżej niż inne (swobodnie obracalna wieżyczka czołgu wychodząca poza jego budę) - a więc trzeba zrobić sortowanie po wysokości warstwy ale nie tak że najpierw wszystko z z=0 a potem z=1 tylko żeby rysowanie szło możliwie najbardziej od góry do dołu ekranu

Wszystkie te problemy nie istnieją jak wprowadzę podwójne buforowanie, ale nie mogę tego zrobić bo moja mapa w grze ma 256x256 kafli o boku 32px, po Y mogę zawinąć bufor ale nadal jest to 256x8 kafli więc sporo czipu to wpierdziela. Scrolling trick po X dałoby radę zrobić i chyba to jest jedyne wyjście, ale wprowadzi mi to pewno kolejne źródło bugów w wyświetlaniu.

Jak żyć?
[#16] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #15

Pożalę się jeszcze.

Zrobiłem jakieś podwójne buforowanie, 20 bobsów z blithogiem 16x20 śmiga, copperowi zostaje jeszcze trochę czasu ekranowego, ale problem w tym że przy 25 bobach brakuje mi czasu by w tej samej ramce wygenerować nową copperlistę. Z ciekawości sprawdziłem ile to zajmuje CPU z czekaniem na blitter. UAE dało mi takie wyniki:





Pewno undraw na CPU nie jest do końca widoczny, więc zakładam że różnica w prędkości jest nieznaczna. Przy czym droga copperem ma dodatkowe minusy:

- trzeba poświęcić czas na napisanie nowej (zaktualizowanie starej) copperlisty
- jeśli gra wyjdzie poza czas jednej ramki, stara copperlista się restartuje i zamazuje nowym zapamiętanym tłem na starej pozycji boba, uszkadzając obraz w buforze.

Oczywistym plusem jest to że jak copper steruje blitterem to cpu nie czeka i może robić swoje. Tylko co z tego skoro się nie dopcha dostateczną liczbę razy do CHIPu żeby zaktualizować instrukcje dla coppera? Zawsze można puścić blit, trochę policzyć w międzyczasie, dla przyzwoitości potem sprawdzić czy blitter skończył i zlecić mu nową rzecz. Chyba że robię coś nie tak?
[#17] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #16

boby ile mają bitplanów?
Generujesz copperliste od zera, czy tylko aktualizujesz ?
Kod dla blittera w copperze jest generowany czy aktualizowany ?

Jak wygląda kod czekania na blitera ?
[#18] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@asman, post #17

Boby mają 4bpp, tyle co tło. Tryb interleaved, więc ustawiam blitter trzy razy na boba: raz na undraw, raz na zapisanie tła, raz na draw. Stąd trzy kolory tła oprócz czarnego.

W ramach testów copperlistę odpowiedzialną za blitter pisałem od nowa, ale nie stoi nic na przeszkodzie by ją tylko selektywnie aktualizować - chcę mieć wydajnościowo odporne rozwiązanie na sytuację, kiedy mocno muszę odbudować copperlistę bo parę bobów zniknęło lub się pojawiło. Ponownych alokacji w tle nigdzie nie ma, CPU pisze bezpośrednio po CHIPie.

Czekanie na blitter to WAIT na pozycję 0,0 z bitami porównywania V,H wyzerowanymi i jedynką odpowiedzialną za czekanie na blit. Działało na podwójnym WAIT, ale działało też na pojedynczym więc zostawiłem pojedynczy.

Kod czekania na blitter gdy nie robię tego copperem to podwójne sprawdzenie w dmaconr czy blitter skończył.

Jak chcesz się wgryźć w mój kod to mogę Ci dać zrzut surowych wordów copperlisty lub jeśli chcesz bardzo wnikać to mogę wrzucić kod na repozytorium.

Twoje pytania spowodowane są tym, że przydługo mi się rzeczy rysują, czy tym że nie wyrabiam się z odświeżeniem copperlisty przed końcem ramki?
[#19] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #18

Wrzuć kod - może być na mejla albo do repozytorium.

Twoje pytania spowodowane są tym, że przydługo mi się rzeczy rysują, czy tym że nie wyrabiam się z odświeżeniem copperlisty przed końcem ramki?


Odświeżanie copperlisty to nie problem bo możesz ustawić nową copperlistę za pomocą coppera.

Jedyne co podejrzewam, to że generowanie copperlisty zajmuje dużo czasu - ale wydaje mi się to bardzo mało prawdopodobne.
[#20] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@asman, post #19

Ha, no mogę, bo w tej chwili ustawiam przy pomocy CPU w coplc1 adres drugiego bufora copperlisty, tylko gdyby Copper robił to sam, to nie masz pewności że kolejna copperlista będzie gotowa na czas.

tu masz repo, na masterze mój ostatni copperowy kod (21 bobów), na branczy cpu-driven jest 35 bobów. Do kompilacji potrzebujesz ACE'a z gałęzi dbl-bfr, która istnieje póki nie dodam podwójnego buforowania do scrolling tricka.

Możesz spojrzeć na src/gamestates/game/entity.c - tam jest samo gęste odnośnie blittera. O ile w cpu-driven powinno być wszystko w miarę oczywiste bo jadę na rejestrach, to w wariancie copperowym posiłkuj się ace/src/managers/copper.c, ale funkcje copSetWait i copSetMove są raczej oczywiste.

W src/gamestates/game/game.c masz pętlę gry. entityProcessDraw() generuje copperlistę rysowania na buforze lub w cpu-driven rysuje na buforze, copSwapBuffers() przerzuca bufory coppera a viewProcessManagers() między innymi woła simpleBufferProcess(), który wpisuje w copperlistę ustawianie widoku (bplpt i spółka). Prostym sposobem można ten kod wywrócić w copperowym wariancie poprzez zmianę zakresu pętli zawierającej entityAdd() na większy. Linia "TAG_VIEW_COPLIST_RAW_COUNT, 1200" mówi, że chcesz miejsca na 1200 instrukcji w obu buforach na copperlistę.

Jak coś to pytaj, krzycz na mnie że źle robię, czy coś. Jestem otwarty na wszelkie uwagi bo chciałbym mieć jak najwięcej bobów. ;)
[#21] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@teh_KaiN, post #20

Mam problem ze skrośną kompilacją całego ACE. Właże do katalogu ACE i odpalam make i mam komunikat 'no config file' [makefile:94: build \\timer.o].

Na szczęscie był tam plik wynikowy to odpaliłem i popatrzyłem w copperlistę. Zauważyłem że niepotrzebnie powtarzasz przy narzucaniu bobów bltcon. Wystarczy to raz ustawić w bloku gdzie narzucasz boby. Ogólnie to nie ma do czego się przyczepić (bo nie widziałem kodu asemblera procedury odpowiedzialnej za tworzenie copperlist :) ). Myślę, że warto wyłączyć ekran dc.w $0100,0 po linii 256, zyskasz trochę czasu, oczywiście ekran trzeba włączyć też w coperze (dc.w $0100,$4200) gdzie on tam się zaczyna (nie pamiętam dokładnie w której linii).

Generowanie copperlisty można sprytnie oszukać. Dla uproszczenia załóżmy że wszystkie boby są 16x16x4. Ustalasz maksymalną ilośc bobów i generujesz taką copperlistę (*2), Wtedy wystarczy podmieniać tylko te dane które na pewno musisz. W przypadku mniejszej ilości bobów na ekranie (bo został zabity) nie odpalasz blittera - czyli musisz jakoś zanopować długie słowo (dc.w $58,$xx). Na przykład dać czekanie na blitera - bo i tak już czekałeś wcześniej na niego więc nie powinno mocno odbić się na szybkości. Co pociąga za sobą że zawsze musisz zapisywać bltsize w takiej copperliscie, ale dzięki temu nie musisz generować całych bloków copperlisty.
[#22] Re: Blitter ustawiany przez Copper oraz kwestia CPU.

@asman, post #21

Czyli próbowałeś budować przez VBCC? Make supportuje VBCC i GCC, sterujesz tym zmienną ACE_CC którą możesz podawać jako argument make'a.

Dla vbcc (default):
make ACE_CC=vc

dla gcc:
make ACE_CC=gcc

Jeśli dalej Ci nie działa to krzycz - jest to bug którego z jakiegoś powodu nie wyłapałem.

Czyli co, nawet jak diwstrt, diwstop ddfstrt i ddfstop ograniczają obszar działania DMA, to wyłączenie go w dmacon daje jakiś zysk poza ekranem?

Co do selektywnego aktualizowania copperlist to bardzo spoko, tyle że bym nie zamieniał MOVE'a na WAITa bo WAIT podobno trwa jeden takt więcej - to już chyba lepiej zamienić na custom.color[0] := 0x0000 - będzie trwać tyle samo, a przy stałej palecie nie będzie żadnego efektu. Problem w tym że w tej swojej śmiesznej grze mam dynamiczne obciążenie blittera, tzn. raz na planszy są 2 pojazdy i 10 pocisków, raz 4 pojazdy i 0 pocisków, itd. Jak są 2 pojazdy to musiałbym setować "w powietrze" 6 pojazdów (bo maksymalnie mam ich 8) a na to chyba trochę szkoda czasu.

Większy problem jest taki że ze wszystkim naraz się nie wyrobię (8 pojazdów i nie wiem ile pocisków), więc gra jest skazana na zmniejszenie framerate'a gdy za dużo się dzieje na ekranie. Ogólnie copperowe blity by mi bardzo pasowały, bo kod dla CPU jest "czyściejszy" bo nie przeplata się z blitami, ale z tego co widzę problem z wyjechaniem poza czas jednej ramki jest nie do przeskoczenia - bo copperlista się restartuje na vertb i robi się kaszana.

Myślałem nad jakimś systemem checkpointów, żeby copperlista przed każdym blitem ustawiała cop1lc na instrukcje następnego blitu, tak że nawet jak vertb nastąpi to co najwyżej obecny blit nie zostanie wykonany i gra przejdzie do następnego, ale coś mi to nie chciało działać. No i problem z podwójnym buforowaniem copperlisty - bo w tej chwili to robię na ustawianiu cop1lc przy pomocy CPU i w swoim czasie copperlista się przestawi, ale gdyby mieć ten system checkpointów to on by nadpisywał moje ustawienie coplc1 swoim. Zawsze można by to pchać do coplc2 i na przerwaniu vertb zrobić copjmp2 ale jeszcze nie wiem czy skóra jest warta wyprawki. ;)
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem