Archive for Programming

Efektywne stawianie pikseli

2008-01-07 22:00

Obecnie biblioteki graficzne umieją już bardzo, bardzo dużo. W narzędziach takich jak DirectX czy OpenGL mamy ogromne możliwości efektywnego wyświetlania grafiki trójwymiarowej, a jednocześnie ich obsługa staje się z kolejnymi wersjami coraz łatwiejsza. Należy oczywiście poznać nieco matematycznych podstaw stojących za całą grafiką 3D: przekształceniami, oświetleniem, buforem głębi, i tak dalej. Zasadnicza część – czyli cały potok renderowania oraz rasteryzacja – jest już jednak wykonana za nas.
Powiedzmy jednak, że nie zadowala nas ten stan rzeczy i ze względów edukacyjnych – lub możliwych czynników zewnętrznych :) – chcemy sami dowiedzieć (w sposób najbardziej praktyczny, czyli poprzez implementację), jak to wszystko działa. Wówczas do szczęścia potrzebujemy właściwie tylko jednej operacji: wyświetlenia pojedynczego piksela w określonym kolorze. I jest tylko jedno ale: aby to wszystko działało w jakikolwiek sensowny sposób, operacja ta musi być diabelnie szybka.

Niestety (a może właśnie ‘stety’) czasy, w których łatwo możemy pisać bezpośrednio po pamięci ekranu, najwyraźniej już dawno się skończyły. Nowoczesny system operacyjny nie pozwala po prostu na takie niskopoziomowe operacje żadnym programom użytkownika. Z drugiej strony korzystanie z tego, co w zakresie wyświetlania grafiki ów system oferuje, może być niewystarczająco – pod względem wydajnościowym, rzecz jasna.
Dlatego wydaje się, że do eksperymentów dobrze nadają istniejące biblioteki graficzne – tyle że ograniczone do jednej funkcjonalności: renderowania sprite’ów punktowych (point sprites). Polega ona na rysowaniu quadów (kwadratów złożonych z dwóch trójkątów) w taki sposób, że zawsze są one zwrócone przodem do kamery. Biorąc pod uwagę to, że wielkość takich punktów możemy określić, ustawienie jej na równą jednemu pikselowi sprawi, że wyświetlanie sprite’ów punktowych będzie dobrym substytutem “stawiania” pojedyncznych pikseli.
Gdzie w zakamarkach bibliotek graficznych ukryta jest tego rodzaju funkcjonalność? Otóż:

  • W DirectX należy ustawić stan renderowania D3DRS_POINTSCALEENABLE na FALSE (co zresztą jest domyślną wartością) oraz D3DRS_POINTSIZE na 1.0f, czyli wielkość odpowiadającą jednemu pikselowi. Co ciekawe, trzeci o nazwie D3RS_POINTSPRITEENABLE wbrew pozorom lepiej jest zostawić ustawiony domyślnie na FALSE. Nie dotyczy on bowiem (co jest dość mylące) samej możliwości rysowania sprite’ów punktowych, a jedynie sposobu mapowania współrzędnych tekstur – co w przypadku symulowania pikseli nie jest nam potrzebne.
    Po tych zabiegach wstępnych możemy rysować punkty, podając format wierzchołków z polami D3DFVF_XYZRHW oraz D3DFVF_DIFFUSE przy pomocy dowolnych wywołań DrawPrimitive[UP] z typem prymitywów D3DPT_POINTLIST.
  • W OpenGL nie musimy ustawiać niczego związanego specyficznie ze sprite’tami punktowymi (bo np. ich domyślna wielkość to 1.0f). Jeśli zaś chodzi o samo rysowanie punktów to jednym ze sposobów (i pewnie nie najlepszym :)) jest ustawienie wszystkich macierzy na jednostkowe i podawanie współrzędnych punktów jako wektorów 4D z ustaloną współrzędną Z (np. 0) oraz W równą 1, do funkcji glVertex4*. Musi być ona umieszczona oczywiście między glBegin (z parametrem GL_POINTS) a glEnd, zaś kolor punktu-piksela określić możemy przez glColor*.

Jak widać, mechanizmy wspomagające nas w ręcznej, pikselowej robocie są nieco ukryte. Co więcej, w DirectX 10 na przykład sprite’y punktowe zostaną wyeliminowane z biblioteki i konieczne będzie samodzielne tworzenie odpowiednich quadów, jeśli zechcemy uzyskać podobną funkcjonalność. To prawdopodobnie słuszna decyzja, bowiem nawet w swoim głównym zastosowaniu – czyli systemach cząsteczkowych – są one zbyt ograniczone (choćby dlatego, że są cały czas zwrócone do kamery, co wyklucza możliwość obrotu cząstek). A przyznajmy, że samodzielne stawianie “pikseli” jest dość egzotyczne – co nie znaczy, że nie warto wiedzieć, jak to się robi, jeśli kiedykolwiek będzie to nam potrzebne…

Tags: , , ,
Author: Xion, posted under Programming » 2 comments

Odwracanie macierzy 4×4

2008-01-04 20:54

Do napisania czegokolwiek z dziedziny programowania gier lub grafiki potrzebna jest zawsze chociaż skromna biblioteka, zawierają podstawowe obiekty matematyczne. Jej częścią na pewno muszą być wektory, a nie od rzeczy są także macierze, kwaterniony, obiekty reprezentujące linie i płaszczyzny, i tak dalej. Taką bibliotekę zwykle albo pisze się raz, albo wykorzystuje jedną z już istniejących. Jakikolwiek byłby nasz wybór, mogłoby się wydawać, że sprawę z nią można załatwić raz na zawsze.
Cóż, nic bardziej błędnego :) Możemy być oczywiście bardzo przywiązani do narzędzi, którymi się posługujemy – języka programowania, platformy, itd. – ale kiedyś na pewno przyjdzie nam zmierzyć się z zupełnie innym językiem i innym środowiskiem. A wtedy trzeba jakoś ten problem matematycznej biblioteki rozwiązać choćby na szybko.

Ostatnio przytrafiło mi się właśnie coś takiego. Nie jest to naturalnie nic pasjonującego, bowiem implementowania dodawania, odejmowania czy mnożenia wektorów jest zajęciem raczej nużącym. Jednak okazało się, że istnieje przynajmniej jedna potrzebna, a niezbyt oczywista matematyczna operacja, którą należy koniecznie uwzględnić. To odwracanie macierzy.
Chcąc tego dokonać programowo, możemy wykorzystać na przykład któryś z tych trzech sposobów:

  • Metoda bezpośrednia. Jest to prostu zaimplementowanie ręcznego sposobu odwracania macierzy, wykorzystywanego zazwyczaj w połączeniu z kartką i długopisem :) Dla tych, co przysypiali na wykładach z algebry (lub nie mieli jeszcze szczęścia na nie uczęszczać ;)) przypominam, że polega to na wykonaniu określonych operacji (mnożenia przez liczbę oraz dodawania) na wierszach naszej macierzy oraz jednocześnie na wierszach macierzy jednostkowej. W ich wyniku nasza macierz sama ma zmienić się w jednostkową, zaś ta druga będzie wtedy szukaną odwrotnością. Wiadomo skądinąd, że złożoność tego algorytmu to O(n3), gdzie n to wymiar macierzy.

    Ręczne odwracanie macierzy

  • Rozwiązanie układów równań. Ta metoda wymaga posiadania procedury rozwiązującej układy równań, jak na przykład eliminacji Gaussa lub znajdowania rozkładu LU. Posługując się nią, możemy obliczyć kolejne wiersze macierzy odwrotnej i też zajmie nam to czas O(n3). Oczywiście trzeba jeszcze mieć (tj. napisać) rzeczoną procedurę, co już takie proste nie jest :)
  • Dopełnienie algebraiczne. Ten sposób charakteryzuje się głównie tym, że wymaga obliczenia sporej liczby wyznaczników – nie tylko tej macierzy, którą odwracamy, ale i każdej z jej podmacierzy. Wiadomo zaś, ze wyliczanie wyznaczników nie należy do operacji tanich. W istocie złożoność tej metody odwracania leży gdzieś w okolicach wykładniczej.

Trzeba jednak zauważyć pewną rzecz. Otóż do celów graficznych potrzebujemy jedynie macierzy o stałym rozmiarze, i to niewielkim – zwykle 4×4. Przy tak małym rozmiarze danych złożoność algorytmu (która niejawnie zakłada, że rozmiar ten jest bardzo duży) nie jest miarodajna. Liczy się bowiem dokładna ilość faktycznie wykonywanych operacji. A przy takim podejściu spotyka nas niespodzianka, jako że najwyraźniej najlepsza okazuje się metoda ostatnia. Jest ona używana na przykład w funkcji D3DXMatrixInverse, zatem posiada całkiem dobrą rekomendację :)
I ma chyba tylko jedną wadę. Po rozpisaniu występującej w niej pętli (co jest możliwe, jeśli znamy z góry rozmiar macierzy) zamienia się ona w dość odstraszającą szpaltę kodu z kilkunastoma długimi, niemal identycznie wyglądającymi wierszami, które różnią się tylko permutacją cyferek oraz plusów i minusów. Ale przecież tak wygląda kod w zasadzie każdego działania na macierzach i właśnie dlatego tak lubimy je implementować ;-)

Tags:
Author: Xion, posted under Programming » 3 comments

Niemal standardowe nazwy klas

2008-01-02 13:48
Model-View-Controller
Model-View-Controller (MVC),
bardzo modny ostatnio ‘wzorzec’

Wzorce projektowe (design patterns) to w założeniu ogólne modele związków pomiędzy klasami (i samych klas), jakie mogą pojawiać się w projektach. Każdy taki wzorzec jest przeznaczony do dość ściśle określonych okoliczności, dokładnie opisany i, przede wszystkim, nazwany. Na pewno każdy średnio zaawansowany programista miał okazję spotkać się z takimi terminami jak Iterator, Fabryka czy Singleton. Są one już na tyle długo używane, że w większości przypadków nie ma problemów ze zrozumieniem tego, co w danej sytuacji oznaczają.

Wzorce nie są oczywiście doskonałe. Ze względu na względnie dużą ścisłość nie mogą opisywać wszystkich rozwiązań, jakie mogą być konieczne, jeśli myślimy o zaprojektowaniu dowolnego programu. Nie jest więc tak, że każda klasa, jaka przyjdzie nam głowy, może być od razu wpasowana w jakiś gotowy szablon.
Przeglądając gotowe kody i różnego rodzaju dokumentacje stwierdziłem jednak, że często powtarzają się w nich różnego rodzaju “pseudowzorce”. Objawiają się one głównie używaniem pewnych słów w nazwach klas, dzięki którym można mniej więcej domyślić się, jaka jest rola poszczególnych typów, do czego służą, jak – z grubsza – działają oraz jakie wykazują zależności z innymi klasami. Naturalnie mogą występować spore różnice pomiędzy poszczególnymi bibliotekami i językami, ale przynajmniej dla dwóch największych zbiorów klas, jakie są obecnie w powszechnym użyciu (czyli .NET Framework i JDK), rozbieżności nie są zbyt duże. Co więcej, ponieważ wielu programistów używa któregoś z tych dwóch narzędzi, często przejmują oni te wzorce nazewnictwa (świadomie lub nie) i stosują je we własnych kodach. Kto wie, może dzięki temu przeciętna czytelność kodu produkowanego przez statystycznego programistę (jeśli w ogóle istnieje ktoś taki :]) ma szansę choć odrobinę wzrosnąć?…

Jakie są więc te nieformalne “wzorce”? Otóż znalazłem kilka następujących:

  • Manager to egzemplarz występujący bardzo często i niestety chyba najmniej jednoznaczny z nich wszystkich. W dosłownym tłumaczeniu jest to ‘zarządca’ czegoś i właściwie niewiele więcej można o nim powiedzieć. Nazywanie wszystkiego ‘menedżerem’ jest popularne zwłaszcza wśród programistów gier: mamy więc menedżery zasobów, tekstur, stanów, czcionek – i tak dalej. Zastanawiam się tylko, skąd to upodobanie to tego akurat terminu. Czyżby programiści z sentymentem wspominali w ten sposób Menedżera Programów, czyli sławetną “powłokę” systemu Windows w archaicznych wersjach 3.x? :)
  • Handler (ew. listener) jest już znacznie precyzyjniejszym pojęciem. Zazwyczaj jest to bowiem obiekt, który ma wykonywać jakieś działanie w reakcji na określenie zdarzenie – czyli ma je ‘obsługiwać’. Najczęściej oznacza to, że typ handlera jest tylko abstrakcyjną klasą bazową lub interfejsem, po którym powinniśmy dziedziczyć, a następnie zaimplementować jego metody i wreszcie “podpiąć” powstały obiekt pod mechanizm obsługi zdarzeń. Typowym zastosowaniem takiego rozwiązania jest reakcja na zdarzenia interfejsu użytkownika (klikanie w przyciski, wciśnięcia klawiszy, itd.) albo powiadamianie o przebiegu operacji asynchronicznych (np. komunikacji sieciowej).
  • Provider jest z kolei obiektem, który ma ‘dostarczać'(lub ‘zapewniać’) jakąś funkcjonalność. Od strony technicznej często działa on tak samo jak handler (czyli w oparciu o polimorfizm metod wirtualnych), tyle że jego przeznaczenie i sposób użycia są nieco inne. Takiego providera podajemy w funkcji lub w obiekcie, dzięki czemu może ona wykonać przy jego pomocy jakieś czynności. Prostym przykładem jest chociażby sortowanie, któremu podajemy obiekt komparatora, odpowiedzialny za dokonywanie porównań. Tak naprawdę jest to więc po prostu wzorzec Strategia (Polityka – policy), któremu ktoś z niewiadomych powodów nadał bardziej “profesjonalną” nazwę.
  • Context oznacza najczęściej taki obiekt, który definiuje jakieś ‘środowisko’, w którym działają inne obiekty. Są one zależne od obiektu reprezentującego kontekst, ale nie można powiedzieć, aby ten się z nich składał (może on nawet “nie wiedzieć”, że jest przez nie wykorzystywany). Po prostu dzięki niemu obiekty “potomne” mogą wykonywać swoje czynności.

Prawdopodobnie dałoby się wyróżnić jeszcze kilka pozycji (chociaż część byłaby dokładnym odpowiednikiem klasycznych wzorców projektowych), ale, jak widać, w sumie chyba nie jest ich zbyt dużo. To w gruncie rzeczy całkiem dobra wiadomość, gdyż nietrudno zauważyć, że wszystkie te nazwy są raczej “magiczne” i na pierwszy rzut nie przywołują jakichś natychmiastowych skojarzeń – zwłaszcza, jeśli nie jesteśmy do nich przyzwyczajeni. Ale taki już urok projektowania zorientowanego obiektowo, polegającego na tworzeniu dziwnych bytów i jeszcze dziwniejszych zależności między nimi :)

Tags: ,
Author: Xion, posted under Programming » 4 comments

Sprawa kalendarzowa

2007-12-31 11:40

Dzisiejszy dzień nie jest zwyczajny – ale tylko dlatego, że kiedyś tak postanowiono. Z bliżej nieokreślonych przyczyn Nowy Rok świętujemy wtedy, gdy za oknami jest szaro, buro i ponuro (względnie biało i zimno). Oczywiście wszystko zależy od punktu widzenia, a ten od punktu siedzenia – lub raczej zamieszkania tudzież klimatu – ale można sobie wyobrazić lepiej dobrany termin…
W ogóle należy stwierdzić, że używany przez nas kalendarz jest wybitne nielogiczny :) Wprawdzie na pewne występujące w nim liczby (zwłaszcza 365 jako najczęstsza liczbę dni w roku) nie da się nic poradzić, ale inne – jak 12, 24, 60, itp. – nie mają specjalnego uzasadnienia ani też żadnej logiki w sobie. Może więc dałoby się jakoś ten system usprawnić? Proponuję “subtelne” przerobienie go na modłę koderską, aby kluczową rolę odgrywały w nim… potęgi dwójki :) Kalendarz ten mógłby wyglądać następująco:

  • Zaczynamy od sekundy, którą aczkolwiek zostawimy w spokoju, jako że jest bardzo dobrze zdefiniowana sposób fizyczny. U nas miałaby więc tę samą długość.
  • Minuta powinna mieć naturalnie 64 (26) sekundy.
  • Analogicznie, godzinę ustalimy jako trwającą 64 minuty.
  • Z długością dnia jest pewien problem, jako że 24 znajduje się dokładnie między dwoma potęgami dwójki (16 i 32). Dwa powody przemawiają jednak za tym, aby ustalić tę długość na 32 godziny. Po pierwsze dlatego, iż pozwoli nam to później dobrać dogodniejszą długość roku. A po drugie: dla kodera obecna 24-godzinna doba jest zwykle za krótka, więc dobrze by było ją wydłużyć :-)
  • Tydzień miałby oczywiście 8 dni. Zauważmy, że jeśli weekend dalej byłby dwudniowy, to w tym systemie miałby on 64 godziny – czyli o 50% więcej!
  • Z miesiącem też nie ma kłopotu: okrągłe 4 tygodnie dałyby łącznie 32 dni w każdym miesiącu. Równa długość miesięcy jest zresztą bardzo praktyczna, bowiem wtedy każda data miałaby jednoznacznie przypisany dzień tygodnia.
  • Jeśli zaś chodzi o liczbę miesięcy w roku, to powinna ona wynosić 8. Wtedy nasz koderski rok będzie miał bardzo przyjemną liczbę 256 dni :)

Nietrudno zauważyć, że możliwe są jeszcze trzy inne warianty tego kalendarza, zależne od liczby godzin w dobie i miesięcy w roku (które to w normalnym kalendarzu są niefortunnie ‘pośrodku’). Dwa skrajne produkowałyby jednak rok bardzo różniący długością od obecnego. Trzeci – z 16-godzinnym dniem i 512-dniowym rokiem – miałby z kolei zbyt dużo dni w roku: liczbie 365 (lub 366) jest bowiem nieco bliżej do 256.
Najciekawsze jest to, że w kalendarzu opisanym powyżej rok byłby tylko o 6,33% dłuższy od obecnego. Jeśli więc wystartowalibyśmy koderski rok dzisiaj o północy, to jego koniec przypadłby nad ranem 23 stycznia 2009 roku. Myślę jednak, że istnieją o wiele lepsze punkty startowe, wśród których od razu nasuwa się początek epoki uniksowej, czyli północ 1 stycznia 1970 roku w UTC.

Oczywiście ta wielce pożądana reforma kalendarza napotyka pewne problemy praktyczne. Inna długość doby zapewne byłaby jednym z nich, chociaż oznaczałaby jedynie, że nowa data pojawia się na zmianę albo w środku nocy, albo w środku dnia. Niestety nawet ta prawidłowość dość szybko przestałaby obowiązywać, a w dalszej perspektywie te 6,33% różnicy w długości roku też zaczęłoby być widoczne. I w ten sposób ten genialny w swej prostocie (czy raczej głupocie ;]) pomysł należałoby niestety odrzucić. Po prostu astronomia nie chce z nami współpracować ;P
Ale jest i pewna dobra strona. Otóż napisanie prostego zegarka, wyświetlającego czas w tym systemie “koderiańskim” to zajęcie nieskomplikowane i mało pracochłonne – idealne jako odpoczynek po sylwestrze :)


Author: Xion, posted under Programming » 7 comments

Wyliczenia na sterydach

2007-12-28 21:57

Co można powiedzieć o typach wyliczeniowych (enums)? Choćby to, że przydają się tam, gdzie mamy do czynienia ze stanami lub atrybutami, które należą do jakiegoś skończonego zbiory. Z technicznego punktu widzenia to kilka(naście/dziesiąt/set) liczb nazwanych stałymi identyfikatorami. I zazwyczaj języki programowania nie oferują wiele ponad to.
Lecz Java musiała się tu wyróżnić :) Z wierzchu jej enumy wyglądają niby zwyczajnie:

  1. public enum Answer { YES, NO, DONT_KNOW, DONT_CARE }

ale w środku kryją bardzo szerokie i pewnie trochę udziwnione możliwości…

Zacznijmy od tego, że takie stałe wyliczeniowe są bezpieczne pod względem typowania – nie można więc jawnie rzutować ich na zwykłe liczby typu int. Słowo kluczowe enum tworzy też – podobnie jak w C# – przestrzeń nazw, więc do stałych odwołujemy się nazwami kwalifikowanymi (Answer.YES) i nie trzeba stosować żadnych przedrostków.
Ale to są wręcz oczywistości. Kolejnym punktem programu jest możliwość dostępu do zbioru zadeklarowanych stałych oraz ich wypisywania w postaci tekstowych nazw:

  1. System.out.println ("[Poll] Are enums in Java too exaggerated?");
  2. for (Answer answer : Answer.values())
  3.    System.out.printf ("( ) %s\n", answer);

Takie nazwy mogą być jednak brzydkie. Żaden problem, bowiem w Javie z każdą stałą może być związany dowolny zestaw danych:

  1. public enum Answer
  2. {
  3.    YES("Yes"), NO("No"), DONT_KNOW("I don't know"), DONT_CARE("I don't care");
  4.  
  5.    // deklaracja dodatkowych danych w postaci pól
  6.    private final String message;
  7.  
  8.    // konstruktor (!)
  9.    Answer(String message)   { this.message = message; }
  10.  
  11.    // metoda dostępowa
  12.    public String getMessage()   { return message; }
  13. }

które – jak widać – deklarujemy podobnie jak składniki klas. A poszczególne “stałe” typu wyliczeniowego nie są już na pewno tylko liczbami: to pelnoprawne obiekty:

  1. System.out.println ("[Poll] Is this poll much better?");
  2. for (Answer answer : Answer.values())
  3.    System.out.printf ("( ) %s\n", answer.getMessage());

Widzimy, że metody są również dozwolone. Żeby było śmieszniej, mogą być one nawet “wirtualne”. Możemy bowiem zapewnić, aby inne ich wersje były wywoływane dla różnych stałych. Robi wrażenie, prawda?…

Cóż, pewnie niekoniecznie – zwłaszcza, że nietrudno osiągnąć podobną funkcjonalność przez instrukcję wyboru. Zgoda, będzie ona bardziej podatna i trudniejsza w konserwacji. Lecz jeśli alternatywą jest przeładowanie języka dziwnymi i nie do końca oczywistymi mechanizmami, to stary dobry switch niekoniecznie musi być rozwiązaniem złym.
Co oczywiście nie zabrania nam być pod wrażeniem pomysłowości twórców języka Java :-)

Tags: ,
Author: Xion, posted under Programming » 12 comments

CUDA ogłaszają

2007-12-22 11:59

Mijający rok (jak zresztą kilka poprzednich) to czas zwiększającej się wydajności układów graficznych. Obecnie są one już nieporównywalnie szybsze niż tradycyjne procesory w komputerach. Charakteryzują się jednak o wiele mniejszą uniwersalnością (wyspecjalizowaniem we współbieżnych obliczeniach na złożonych danych), co jest oczywiście całkiem zrozumiałe – w końcu głównym zadaniem układów GPU jest, jak sama nazwa wskazuje, efektywne przetwarzanie grafiki.
Mimo tego pojawiają się coraz śmielsze pomysły, by zaprząc karty graficzne także do innych zadań. Od kiedy funkcjonalność shaderów, jakie można na nich uruchamiać, stała się stosunkowo dużo, jest to zupełnie możliwe. Zastosowania tzw. GPGPU – czyli wykonywania ogólnych (niekoniecznie graficznych) obliczeń na GPU obejmują przetwarzanie dźwięku, analizę sygnałów (np. szybką tranformatę Fouriera), sieci neuronowe czy nawet operacje na bazach danych.

Logo technologii nVidia CUDATen trend zdaje się być wspomagany przez głównych graczy na rynku. I tak nVidia na początku roku przedstawiła technologię o wdzięcznej nazwie CUDA (Compute Unified Device Architecture). W skrócie jest to zestaw narzędzi, które umożliwiają programowanie układów graficznych (jak na razie tylko kart nVidii serii 8) w języku C. Obejmuje on kompilatory, debuggery i biblioteki matematyczne, które mają na celu wspomaganie tego procesu. Wszystko to wygląda całkiem obiecująco, zwłaszcza w programowaniu gier (wydaje się np. całkiem możliwe liczenie fizyki wielu małych obiektów w sposób współbieżny na GPU), chociaż na razie jest to raczej melodia przyszłości.
Jeszcze bardziej mgliście wygląda sprawa z przyszłą wersją DirectX, oznaczoną numerem 11. Pojawiła się plotka, że pojawi się w niej nowy (czwarty już) typ shadera, czyli compute shader. Miałby on służyć właśnie do takich ogólnych obliczeń na GPU w sposób ustalony całkowicie przez programistę. Widać więc, że jeśli producenci kart chcą dotrzymywać kroku w zgodności z wiodącym graficznym API, takie CUDA jak u nVidii będą wkrótce nie cudowne, ale zupełnie zwyczajne :)

Tags: , , ,
Author: Xion, posted under Programming » Comments Off on CUDA ogłaszają

Granice

2007-12-20 20:20

Bariery, granice, strefy rozdzielające i podobne twory potrafią często utrudniać życie i prowadzić do straty cennego czasu. Jednak w prawdziwym świecie jest możliwe, aby przynajmniej w pewnym obszarze je znieść lub uczynić niewidocznymi – tak jak to stanie się dzisiaj o północy z zachodnią i południową granicą Polski. Naturalnie jest to wielkie przedsięwzięcie logistyczne, organizacyjne i polityczne. Ale doprowadzono je do końca, nie pierwszy raz zresztą – widać więc, że to możliwe.
Podczas programowania też napotykamy na przeróżne granice. Tutaj sytuacja nie przedstawia się aż tak różowo. Nikt ich nie zniesie jakimś odgórnym traktatem i zawsze pozostanie nam ich mozolne przekraczanie…

Cóż to za bariery? Są one bardzo różnego rodzaju, mają jednak pewną wspólną cechę. Występują mianowicie tam, gdzie spotykają się takie okołoprogramistyczne twory, które nie są ze sobą do końca kompatybilne – a mimo to muszą ze sobą współpracować. Przykładów jest mnóstwo i zwykle każdy z nich wymaga osobnego rozwiązania. Oto kilka próbek:

  • Wzorzec Most
    Wzorzec Most pozwala przekraczać granicę
    między interfejsem a implementacją

    Pewne języki programowania (z C/C++ na czele) mienią się wieloplatformowymi. Teoretycznie więc granica między systemami operacyjnymi powinna być łatwo przekroczona poprzez ponowną kompilację. W praktyce pisanie programów działających na wielu systemach wymaga albo wielkiej dbałości o zgodność, albo napisania po prostu osobnych kodów dla fragmentów, które na różnych systemach implementuje się różnie.

  • Programy do grafiki 3D zapisują stworzone modele w wielu różnych formatach. Niektóre odpowiadają naszym oczekiwaniom i możemy je odczytywać bezpośrednio (co aczkolwiek banalne nie jest, ale o tym kiedy indziej), ale jeśli chcemy zapisywać z modelem jakieś niestandardowe informacje albo robić to w nieprzewidziany sposób, potrzebujemy zwykle pluginu do edytora 3D. W tym przypadku wtyczka ta jest sposobem na przekroczenie granicy między wspomnianym edytorem a naszym silnikiem.
  • Kiedy aplikacja zaczyna się komunikować przez sieć, owo przełamywanie barier (w tym przypadku jednej maszyny) jest od razu widoczne i, oczywiście, przysparza kłopotów :) Zależnie od tego, o jakim poziomie abstrakcji mówimy, możemy spotkać się z: taką obsługą odczytu i zapisu danych, by nie blokowało to reszty aplikacji; niedostatkami istniejących i nieco przestarzałych protokołów sieciowych (albo koniecznością tworzenia własnych) czy nawet inną kolejnością bajtów przy interpretowaniu danych liczbowych na maszynie źródłowej i docelowej.

Można by niemalże pomyśleć, że kodowanie polega przede wszystkim na godzeniu ze sobą takich niezgodnych platform, systemów, bibliotek, i tak dalej. Jednak wśród tych wszystkich granic tak naprawdę tą najważniejszą, którą powinniśmy nieustannie przekraczać, jest granica naszych własnych możliwości.

Tags:
Author: Xion, posted under Programming, Thoughts » 2 comments
 


© 2021 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.