1 Ostatnio edytowany przez nosty (2014-12-28 10:02:45)

Mam prośbę o radę.

Zacząłem optymalizować procedury rysujące obiekty w TOMKU. Poczytałem trochę jakie funkcje ma blitter w ST (http://pl.wikipedia.org/wiki/Blitter) i co z tego powinno być zaimplementowane i stanąłem przed ścianą.

Wyszło mi, że obiekt można rysować:
1) w inversie albo bez
2) z odbiciem w pionie lub bez
3) z odbiciem w poziomie lub bez
4) bez maski, z osobną maską, z maską stworzoną z któregoś koloru (min. to kolor 00)
5) w jednym z trybów: nadpisywania, OR, AND, XOR

Większość tych opcji się krzyżuje, co daje w ekstremum jakieś 32 kombinacje.
Do tego muszę zrobić osobne procedury dla rysowania obiektow z obcięciem na brzegach ekranu i bez obcinania, oraz ultraszybką procedurę do rysowania obiektów o szerokości 1 słowa (np pociski).

To daje razem ponad 64 wersje jednej tylko procedury rysującej.

I nie wiem jak z tego wybrnąć.
Wadą PIC'a jest to, że pamięć programu jest we FLASH'u i nie może być przepisana i wykonywana z RAM'u.
Czyli program nie może być automodyfikowalny (jak to jest w Atari).
A większość operacji jakie opisałem, musi być wykonywana na każdym słowie danych obiektu, czyli w samym środku pętli.

Czyli: albo pójdę na łatwiznę i zrobię jedną procedurę a w środku pętli będę miał choinkę rozgałęzień i warunków, co kilkukrotnie zmniejsz wydajność, albo czeka mnie powtarzanie całej procedury dziesiątki razy, różniących się np tylko jedną instrukcją w środku pętli (np XOR zamiast AND). To zwiększy zajętość pamięci programu i co gorsza, uczyni koszmarem jakiekolwiek przyszłe poprawki.

Nie chodzi mi o to, żebyście głosowali na jedno z tych dwóch rozwiązań ;) Raczej o radę, czy nie przegapiłem czegoś? Może wyważam otwarte drzwi, a jest jakies inne prostsze rozwiązanie tego dylematu...

2

może napisz taką prockę najpierw w języku innym niż PIC-owy a na wiele pytań znajdziesz odpowiedź, odbicie w osi X, Y to jedna procka zależnie od znaku dla DX, DY, mam nadzieję że PIC-owy język umożliwia coś takiego jak parametr procedury

inwers to dodawanie stałej wartości do każdej nowo powstałej wartości, konkretnie 128, no ale może być to wartość 0..255

napisz prościej, a potem komplikuj sobie życie

*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

3 Ostatnio edytowany przez nosty (2014-12-28 14:08:31)

@tebe - ale nie o to chodzi. Ja wiem jak zrobić te wszystkie operacje w asemblerze PIC'a. Bardziej lub mniej optymalnie. Chodzi mi tylko o to co opisałem: że albo postawię na wydajność a wtedy będę miał 64 prawie identyczne procedury, trudne w serwisowaniu, albo zunifikuję wszystko kosztem wydajności.

A propos tego co napisałeś o odbiciach, to spróbuj takie pomysły zastosować w asm Atari :) Ja w PIC'u też piszę w asemblerze więc sam rozumiesz...

Co do mojego podstawowego problemu to właśnie testuję sposób, który może być złotym środkiem: bufor linii, to którego najpierw przepisuję linię obiektu robiąc w locie odpowiednie operacje (inwersja, odbicie poziome, przesunięcie bitowe), a następnie tę linię nakładam na ekran w odpowiednim trybie (XOR, OR, AND). Specyfika asemblera PIC'a jest taka, że może się to okazać optymalnym i dość uniwersalnym rozwiązaniem. Uniwersalny bufor linii się przyda, gdybym chciał w przyszłości dopisać kolejne operacje, jak np skalowanie poziome obiektu.
Zobaczymy jak to wyjdzie wydajnościowo.
Mam tylko wrażenie, że wynajduję od nowa blitter ;) a przecież ktoś już kiedyś musiał stanąć przed identycznymi problemami.

Wczoraj robiłem testy nowych procedur rysujących (maksymalnie szybkich, bez obcinania obiektów na brzegach, bez odbić, inwersji itp.) dla 4-kolorowego duszka.
W czasie kiedy antic nie rysuje obrazu udało mi się narysować 120 duszków o wymiarach 8 bajtów x 16 linii, w trybie z maską niezależną od kolorów (dokładnie jest to operacja: (maska AND obiekt) OR (INV(maska) AND tło)  ). Przy czym połowę czasu zajęło Atari na samo wysyłanie rozkazów rysowania do PIC'a.
Śmiesznie to wyglądało na ekranie :)

4

Rozwiązania nie podam, ale że ten problem mnie zaciekawił :) to i mam małe pytanko, bo nie do końca rozumiem problem wydajności opcji związanej z 'choinką rozgałęzień i warunków'
Dlaczego napisałeś, że to rozwiązanie było by kilkukrotnie wolniejsze? Skoro pisząc, że drugie rozwiązanie polegało by na stworzeniu ileśdziesięciu identycznych procedur różniących się tylko jedną operacją, to przecież przy takich założeniach  dało by się stworzyć pojedyncze rozgałęzienia, które dla jednego przypadku robiło by tylko ten przypadek i już. Trochę to by rozwlekło kod, ale spadkiem wydajności tego nie można by nazwać, a już na pewno nie kilkukrotnym. No chyba, że czegoś mocno nie rozumiem :)

5 Ostatnio edytowany przez nosty (2014-12-28 17:03:20)

To nie tak. Kilkukrotnie wydłużyłby się czas wykonania, jeśli stworzyłbym jedną uniwersalną procedurę, a warnunki wstawił w sam środek pętli kopiującej. Tak jest najwygodniej, ale i najmniej wydajnie.

Zrobienie warunków poza pętlami i przekierowywanie do jednej z np. 60 procedur jest wydajne, ale poza rozwleczeniem kodu, mocno kłopotliwe. Co jeśli znajdę błąd? Będę go musiał poprawiać we wszystkich procedurach. Co jeśli zmienię format obiektów?
Taki kod jest cholernie klopotliwy w "utrzymaniu". I nieelegancki :P

6

nosty napisał/a:

Taki kod jest cholernie klopotliwy w "utrzymaniu". I nieelegancki :P

Od tego właśnie są makra.

KMK
? HEX$(6670358)

7

Moim zdaniem nie powinieneś się w ogóle sugerować blitterem z ST. Nie jest to nalpeszy blitter pod słońcem. Może zacznij od tego jakich operacji potrzebujesz?

Poza tym, jestem pewien, że jakiś macro assemble albo preprocesor w C umożliwiłby zrobienie wszystkich kombinacji w oparciu o sprytne makra bez komplikacji, o których piszesz.

What can be asserted without proof can be dismissed without proof.

8 Ostatnio edytowany przez qbahusak (2014-12-28 21:55:44)

Jeśli dobrze rozumiem, Nosty chciałby mieć jednocześnie zwięzły KOMPILAT _i_ PRĘDKOŚĆ.
Prawda jest taka, że te dwie rzeczy się wykluczają.

A makra dotyczą porządkowania żródeł, a nie do kompilatu. Po zastosowaniu makr  i skompilowaniu Nosty otrzyma swoje SZYBKIE rozwiązanie, ale rozwleczone na 64 prawie takie same procedury. To, co chcę tu przekazać, że makra tak, ale Nosty na pewno ma je w paluszku i zadając pytanie nie zwracał uwagi na szczegół, który jest narzędziem, a nie rozwiązaniem problemu.

Jedyne, co mi przychodzi do głowy, to owa hybryda linia po linii.

A przecież blitter amigowy bierze z bodajże trzech miejsc i zapisuje do czwartego z operacjami boolowskimi, czy też rysowaniem linii i zapełnianiem powierzchni.

Tak, ten podział na linie to jest rozwiązanie. Oczywiście przy zastosowaniu makr :) Jako narzędzia.

9

Miałem na myśli, że makra są rozwiązaniem tego problemu technicznego:

nosty napisał/a:

... 60 procedur ... Co jeśli znajdę błąd? Będę go musiał poprawiać we wszystkich procedurach. Co jeśli zmienię format obiektów?

KMK
? HEX$(6670358)

10 Ostatnio edytowany przez Nitro (2014-12-28 23:20:08)

w środku pętli będę miał choinkę rozgałęzień i warunków

IFy w środku pętli - programistom grafiki włos jeży się na głowie widząc takowy tekst, przynajmniej mi :) W 99% da się tego uniknąć.

Popieram tebe'go, nie rozpisywanie trzech pierwszych przypadków na osobne procedury to powinien być rozsądny kompromis jeśli w PICu wszystkie instrukcje kosztują cykl - adresowanie pośrednie nie boli.
Dalej maska - okej, wydzielamy osobną procedurę.
Finalnie podpunkt 5, o ile mikrokontroler jest ośmiobitowy - generalnie kopiujesz i robisz operacje na jednym bajcie na raz to wystarczą dwie osobne procedury - nadpisywanie oraz operacja logiczna. Operacja logiczna - trzymasz trzy 256 bajtowe 'tablice prawdy' dla AND OR XOR'a, na starcie ustawiasz adres w zależności od tego jaka operacja jest potrzebna.

Alternatywnie aby to wszystko wyżyłować to również popieram przedmówców - makroasembler. Chyba najpopularniejszy loader na C64 - Krill's Loader ma mnóstwo opcji konfiguracyjnych, oprócz tego obsługuje też Commmodore +4, kod napisany w CA65 to jedna wielka choinka makr, #ifdef #endif i #include. Wypełniamy config, robimy build i dostajemy loader skrojony pod siebie bez grama niepotrzebnego ASMowego tłuszczyku :)

11

sqward napisał/a:

Moim zdaniem nie powinieneś się w ogóle sugerować blitterem z ST. Nie jest to nalpeszy blitter pod słońcem.

nie jest najlepszy ale jest dobry, no i w pewnych operacjach szybszy od konkurencji :p

Lynx I / Mega ST 1 / 7800 / Portfolio / Lynx II / Jaguar / TT030 / Mega STe / 800 XL / 1040 STe / Falcon030 / 65 XE / 520 STm / SM124 / SC1435
DDD HDD / AT Speed C16 / TF536 / SDrive / PAK68/3 / Lynx Multi Card / LDW Super 2000 / XCA12 / SkunkBoard / CosmosEx / SatanDisk / UltraSatan / USB Floppy Drive Emulator / Eiffel / SIO2PC / Crazy Dots / PAM Net
http://260ste.atari.org

12

Oh, finalnie rzucę ciekawostką - chyba najbardziej spasiony blitter, potem nadeszły shadery:
http://developer.download.nvidia.com/as … biners.pdf

13

Kuba, przeceniasz mnie ;) Faktycznie nie pomyślałem o makrach, bo na Atari praktycznie nigdy ich nie używam (tak samo jak prawie żadnych pseudorozkazów - lubię widzieć czysty i cały kod).
Pomysł słuszny i dobry.

Właściwie już wiem jak to zrobię: cudów nie ma, pójdę na kompromis. Najczęściej używane opcje będą wydzielone do osobnych wersji procedur, żeby działaly najwydajniej. Makra pomogą utrzymać spójność kodu. Będzie tego kilka wersji.
Reszta opcji będzie w zbiorczej, wolniejszej procedurze z warunkami, operującej na buforze linii.

Dzięki za pomoc.

14

Książka "Piękny kod. Tajemnice mistrzów programowania" - rozdział 8 "Generowanie w locie kodu do przetwarzania obrazów".

https://www.youtube.com/watch?v=jofNR_WkoCE

15

@Fox - PIC ma architekturę harwardzką, więc żadnego kodu w locie nie wygeneruje. Z drugiej strony napisanie sobie generatorów źródeł asemblera w jakimś języku skryptowym i skonkretyzowanie ich dla wszystkich przypadków mogłoby mieć sens.

16

Ten akurat może mieć część programu w RAMie.

hex, code and ror'n'rol
niewiedza buduje, wiedza rujnuje

17

@mono - nie może. Przecież to by rozwiązało mój problem :)
Nawet organizacja pamięci jest inna i kłopotliwa: w pamięci RAM adres parzysty to słowo (16-bit), a w pamięci programu adres to 24 bity.

Można jedynie podmapować część pamięci programu (paczkę 32KB) do obszaru widzianego jako RAM, w celu odczytania danych tam przechowywanych.
Ale nie można w żaden sposób odpalić kodu z RAM'u.

Czyli automodyfikacje odpadają i to był mój problem.
Budowa rozwiązania pośredniego trwa.

18

Że "w żaden sposób" nie można odpalić kodu z ramu? Owszem, można, tylko interpretowany. Ale wiem, czepiam się :)

19

qba - Czy masz na myśli, że miałbym napisać sobie jakiś rodzaj języka i pisać w nim program w RAM? :)

20

Tokenami mogą być skoki do procedur a tokeny można umieszczać na stosie.

hex, code and ror'n'rol
niewiedza buduje, wiedza rujnuje