» »

Novosti DirectXa 9 - 1. del

No pa smo vendarle dočakali novo, dolgo pričakovano, različico knjižnice DirectX, ki jo je zlobni Microsoft, bolj ali manj skrbno skrival pred radovednimi očmi javnosti. V približno enaki tajnosti sta nastala tudi ta dva članka, ki vam bosta, vsaj upam tako, uspela razložiti vse čare in novosti novega programskega vmesnika. V članku, ki ga trenutno berete, si bomo ogledali vse novosti, ki jih je Microsoft namenil novim programabilnim funkcijam sedanjih in prihajajočih grafičnih kartic. Sedaj že splošno znano dejstvo se namreč glasi, da je potrebno programerjem pustiti čim bolj proste roke pri tem, kakšne grafične učinke bodo uporabljali v svojih igrah in kako bodo do njih prišli. Dosti blebečenja, začnimo z vsebino!


Novi osenčevalniki točk

Pixel shaderji, osenčevalniki točk, so gotovo ena izmed stvari, ki se je spremenila naravnost dramatično glede na DirectX 8.1. Če je do sedaj za pixel shaderje še nekako veljalo, da so le podaljšek že obstoječih zmogljivosti, je nova verzija tega osenčevalnika nekaj povsem prenovljenega. Glavni novosti sta gotovo to, da so lahko novi shaderji bistveno daljši od obstoječih, ter to, da vedno operirajo s plavajočo vejico, kar zagotavlja večjo natančnost pri izvajanju teh operacij. Novost je tudi to, da sta sedaj nabora ukazov v osenčevalnikih trikotnikov ter točk (vertex shaderjih in pixel shaderjih) praktično enaka. Pa si poglejmo, kako visoko so se dvignile meje pixel shaderjev:

Lastnosti DirectX 8.x DirectX 9.0
1.0-1.3 1.4 2.0 2.x 3.0
Število aritmetičnih ukazov 8 8* 64 do 512 do 32768
Število ukazov dostopanje do tekstur 4 6* 32 brez omejitev brez omejitev
Skupno število ukazov 12 14* 96 do 512 do 32768
Število različnih tekstur 4 6 16 16 16
Število konstantnih registrov 8 8 32 32 224
Število začasnih registrov 2 6 12 do 32 32
Poljubne premene komponent registrov ne ne ne možnost da
Statičen nadzor izvajanja shaderjev ne ne ne možnost (do 4 nivoje) da (4 nivoje)
Dinamičen nadzor izvajanja shaderjev ne ne ne možnost (do 24 nivojev) da (24 nivojev)
Največje število izvedenih ukazov 12 28 96 brez omejitev brez omejitev
Največja globina odvisnih branj 2 2 4 brez omejitev brez omejitev
Gradient ukazi ne ne ne možnost da

* Pixel shaderji 1.4 so lahko sestavljeni iz dveh faz, od katerih je ena namenjena pripravi koordinat za teksture, druga pa operacijam z barvami. Tako je možno število ukazov podvojiti.


Pixel Shader 2.0

Ker proizvajalci grafičnih čipovij začnejo z razvojem nove strojne opreme, še preden se začnejo sploh pogajati o podpori s strani DirectXa, je na tem področju kar precejšnja zmeda. Ker pa Microsoft ni želel ponoviti napake iz DirectX 8.x, ko je bilo na razpolago kar pet različnih inačic osenčevalnikov točk, so se tokrat odločili vključiti samo dve verziji (2.0 in 3.0). Da pa bi programerjem iger in proizvajalcem strojne opreme vseeno pustili več maneverskega prostora, pa je tukaj še vmesna verzija 2.x, ki omogoča razširjanje zmogljivosti osenčevalnika skoraj do verzije 3.0 glede na sposobnosti posameznih kartic. Radeon 9700 tako pade v kategorijo 2.0, GeForceFX pa v 2.x kategorijo.

Z verzijo osenčevalnikov točk 1.4 je prišlo do poenostavitve programskega modela, saj so bili vsi kompleksni ukazi zamenjani z enim samim (ki je teksel iz podane koordinate naložil v želeni register) in možnostjo, da si pixel shader svobodno izračuna koordinate željenih tekslov. Ta trend se nadaljuje -- v verziji 2.0 je možno aritmetične ukaze poljubno mešati z ukazi za dostopanje do tektur. Sedaj je na teksture bolj prikladno gledati kot na 1D, 2D in 3D tabele, kot pa le enostavne 2D slike. Poglejmo si za primer, kako v različnih verzijah pixel shaderjev naredimo enivronment bump mapping, ki ga sicer znajo narediti tudi nekatere kartice brez pixel shaderjev:

PS 1.1

ps_1_1 
tex t0 
texbem t1, t0 
mov r0, t1
]

PS1.4
ps_1_4 
texld r0, t0 
texcrd r1.xyz, t1 
bem r1.xy, r0, r1 
phase 
texld r1, r1 
mov r0, r1
]

PS 2.0
ps_2_0 
dcl t0 
dcl t1 
dcl_2d s0 
dcl_2d s1 
texld r0, t0, s0 
dp2add r0.x, c0, t1, r0.x 
dp2add r0.y, c1, t1, r0.y 
texld r0, r0, s1 
mov oC0, r0


Od vseh treh verzij se morda ravno verzija 2.0 zdi najbolj zapletena, vendar še najbolj naravna in enako hitra kot ostali dve (ukazi dcl se uporabljajo za navodila gonilniku ter ne štejejo za ukaze). Čas je, da preklopimo na višjenivojske jezike.

Pri delu s koordinatami tekstur pa pride v novem osenčevalniku točk še posebej do izraza povečana natančnost računanja. Koordinate tekstur so vedno podane z 32-bitno natančnostjo na komponento. Če smo hoteli to koordinato (ki ima sedaj dosti bolj splošen pomen) uporabiti v kakšnem izračunu, se je njena natančnost hitro skrčila na [-8,8] v verziji 1.4 in [-1,1] v ostalih, obenem pa nismo več mogli v coloti dostopati do tekstur, večjih od 256x256 točk. V pixel shaderjih 2.0 imamo garantirano vsaj 16-bitno natančnost s plavajočo vejico, kar zmanjša problem z obsegom, obenem pa omogoča tudi večjo natančnost, saj lahko še vedno dostopamo do tekstur velikosti 1024x1024.

Naslednja pomembna stvar v novih pixel shaderjih pa je kar bistveno povečanje števila vzorcev tekstur. To programerjem omogoča, da bodo lahko izdelovali povsem svoje filtre, ki bodo pač najbolj ustrezali funkciji, kateri bo tekstura namenjena. Tako linearno kot anizotropno filtriranje se pri teksturah, ki so zapisane v formatu s plavajočo vejico, ne obneseta najbolje. Drugi del problema pa je, če je v teksturi zapisanega kaj drugega kot slika. Če imamo tako v teksturi zapisane vektorje, jih praktično moramo popravljati v pixel shaderjih.

Več vzorcev tekstur pa odpre tudi možnost, da pridemo do efektov, kot so zabris gibanja (motion blur), globina polja (depth of field) in mnogo drugih v samem jedru osenčevalnika.

Naslednja zanimivost pa so nedvomno statične in dinamične vejitve ter zanke, ki omogočajo, da se različne pike na zaslonu lahko izrišejo po povsem ločenih poteh. Prvi problem, ki se pri tem pojavi je, da se lahko en piksel izvaja po bistveno daljši poti, kot drugi. Ker pa grafični čipi izrisujejo pike v pomnilnik v blokih, morajo vsi že končani piksli čakati, da se končajo še vse časovno zahtevne pike. Vejitve omogočajo veliko novosti v grafičnem svetu. Med drugim je možno doseči tudi precej slovito sledenje žarkom (ray tracing) ter izrisati celotno 3D teksturo v enem samem prehodu, kar eventuelno omogoča vokslasto grafiko.

Statična vejitev

if b3 
// Izvedeni ukazi, če je b3 true 
else 
// Izvedeni ukazi, če je b3 false 
endif


Dinamična vejitev
if_lt r0.x, r1.y 
// Izvedeni ukazi, če je r0.x manjši od r1.y 
else 
// Izvedeni ukazi, če je r1.y večji ali enak r0.x 
endif


Primerjava med statičnimi in dinamičnimi vejitvami. Pri statičnih vejitvah vedno operiramo le z konstantnimi boolean registri, ki jih nastavi aplikacija. Pri dinamičnih vejitvah pa si lahko privoščimo odločanje glede na to, kakšen je nek parameter v primerjavi z drugim

Zelo blizu vejitvam so tudi t.i. gradientni ukazi (gradient instructions), ki nam povedo, koliko se bo razlikoval parameter, ki smo ga podali, čez serijo sosednjih pik (ločeno v x in y smeri). S pomočjo teh ukazov je možno izračunati normalo za vsako piko sproti, samo s pomočjo višinske mape. Pri izdelavi lastnih filtrov si s temi ukazi lahko pomagamo in dosežemo podoben učinek kot ga ima običajno anizotropno filtriranje. Z njimi pa si lahko pomagamo tudi pri renderiranju senc z mehkimi robovi (soft shadows).


Novi osenčevalnik trikotnikov (vertex shader)

Izboljšave vertex shaderja so druga pomembna sprememba glede na DirectX 8.1. Nova osenčevalna enota ima lahko vsaj dvakrat več ukazov kot dosedanja ter mora imeti vsaj statični nadzor izvajanja shaderjev. Obenem pa morajo vertex shaderji sedaj podpirati vsaj 256 konstantnih registrov.

Lastnosti DirectX 8.x DirectX 9.0
1.1 2.0 2.x 3.0
Število ukazov 128 256 256 do 32768
Število konstant vsaj 96 vsaj 256 vsaj 256 vsaj 256
Število začasnih registrov 12 12 do 32 32
Statičen nadzor izvajanja shaderjev ne da (do 4 nivoje) da (do 4 nivoje) da (4 nivoje)
Dinamičen nadzor izvajanaj shaderjev ne ne možnost (do 24 nivojev) da (24 nivojev)
Poljubne premene komponent registrov da da da da
Dostop do tekstur ne ne ne da (4 teksture)
Največje število izvedenih ukazov 128 brez omejitev brez omejitev brez omejitev

Vertex shaderji 2.0

Podobno kot se je zgodilo v pixel shaderjih se je tudi pri vertex shaderjih. Da bi ugodili čim večim željam, je Microsoft tudi tukaj uvedel dva osnovna modela in enega razširjenega. Tudi tukaj Radeon 9700 pade v 2.0 kategorijo, GeForceFX pa v 2.x kategorijo, saj poleg statičnih vejitev podpira tudi dinamične. Zanke in vejitve se lahko uporabijo za združevanje večih osenčevalnikov v enega, kar zmanjša število potrebnih le-teh in zmanjša število menjav shaderjev v igrah (te menjave namreč zahtevajo nekaj dragocenega procesorskega časa). Zanke skupaj s podprogrami pa omogočajo tudi izogibanje podvajanju delov kode, s čimer lahko z enakim številom ukazov naredimo bistveno več.

Dinamične vejitve skupaj z večjim številom konstant pa bodo omogočile precejšen skok naprej pri animiranju likov v igrah. Brez dinamičnih vejitev je običajno potrebno lik razbiti na več delov in vsak del animirati posebej.


Visokonivojski programski jezik za osenčevalnike

Že zaradi dejstva, da so novi osenčevalniki vedno daljši ter vedno bolj splošni, se vedno bolj pojavlja potreba po višjenivojskih jezikih za pisanje shaderjev. DirectX 9 prinaša svoj HLSL (High Level Shading Language), ki je zelo podoben klasičnemu Cju in omogoča vse, kar se je do sedaj dalo narediti z zbirnikom.

Primerjava treh osenčevalnikov

Ročno napisana koda

ps_2_0
dcl t0.xyz
dcl t1.xyz
dp3 r0.w, t0, t0
rsq r0.w, r0.w
mul r0.xyz, t0, r0.w
dp3 r1.w, t1, t1
rsq r1.w, r1.w
mul r1.xyz, t1, r1.w
dp3 r2.x, r0, c0
mul r3, r2.x, c2
dp3 r2.y, r0, r1
pow r2.y, r2.y, c1.x
add r0, r3, r2.y
mov oC0, r0


Koda v DX9 HLSL
truct Input
{
  float3 Normala:TEXCOORD0;
  float3 Halfway:TEXCOORD1;
};

struct Output
{
  float4 Color:COLOR0;
};

// Konstante
float4 Barva={0.96f, 0.89f, 0.22f, 0.0f};
float3 Luc={-1.0f, 0.0f, -0.5f};
float Eksponent=32.0f;

Output main(Input Data)
{
  Output Out=(Output)0;
  // Normaliziramo normalo in halfway vektor
  float3 Normala=normalize(Data.Normala);
  float3 Halfway=normalize(Data.Halfway);

  // Izračunamo diffuse in specular senčenje
  Out.Color=dot(Normala,Luc)*Barva+
  pow(dot(Normala,Halfway),Eksponent);

  return Out;
}


Prevajalnikova koda
ps_2_0
dcl t0.xyz
dcl t1.xyz
dp3 r0.w, t1, t1
rsq r2.w, r0.w
mul r9.xyz, r2.w, t1
dp3 r9.w, t0, t0
rsq r9.w, r9.w
mul r4.xyz, r9.w, t0
dp3 r4.w, r4, r9
dp3 r11.w, r4, c1
log r6.w, r4.w
mul r1.w, r6.w, c2.x
exp r8.w, r1.w
mad r10, r11.w, c0, r8.w
mov oC0, r10




Summa summarum

Microsoftu je očitno uspelo izdelati zelo dober prevajalnik, ki zna kodo zelo dobro optimaliizirati. V večini primerov namreč izenači na roko napisano kodo, če pa že pride do odstopanj, pa ta niso velika (eden ali dva ukaza) in tudi HLSL občasno najde kakšno možnost za optimizacijo več kot pa programer. V prihajajočih igrah bodo višjenivojski jeziki odigrali odločilno vlogo in programerji bodo odločili, kateri od teh jezikov se bodo obdržali in kateri bodo zdrsnili v pozabo. Po vsej verjetnosti pa se bodo obdržali kar vsi trije, o katerih se danes največ govori: DirectX 9 HLSL, Cg in OpenGL 2.0 HLSL.

Pixel shaderji 3.0 in vertex shaderji 3.0 so trenutno največ, kar nam ponuja DirectX, vendar strojne podpore za te shaderje še nekaj časa ne bomo videli. Model 3.0 je že skoraj povsem poenoten, saj omogoča dostop do tekstur iz vertex shaderja in ima skoraj povsem enak nabor ukazov za vertex shaderje in pixel shaderje. Omejitve so zelo visoko, saj so lahko shaderji dolgi kar 32768 ukazov, kar je bistveno več od tega kar nam strojna oprema ponuja danes. Shaderji 3.0 so nedvomno napoved, kaj se nam obeta v DirectX 10, kjer bodo shaderji po vsej verjetnosti že povsem brez omejitev kar se tiče dolžine in brez omejejevanja globine gnezdenja zank, kar bo omogočalo tudi rekurzivne metode (torej funkcije, ki kličejo same sebe).

Grafične kartice v bližni prihodnosti tako ne bodo več imele enot za vertex shaderje in cevovodov za izrisovanje pik (pixel pipelineov), ampak bodo imele le še neko število vekorskih aritmetičnih enot, ki jim bodo gonilniki sposobni dinamično določati delo (torej ali bo določena enota izvajala vertex shaderje ali pixel shaderje). Zagotovo se nam obeta še zanimiva prihodnost.

Čudežno popotovanje skozi grafični cevovod II

Čudežno popotovanje skozi grafični cevovod II

V prvem delu smo si ogledali strukturo sodobne grafične kartice. Med drugim smo omenili shaderje, ki so zaradi svoje programabilnosti, nekakšno srce in duša sodobnih GPU-jev. Različni shaderji so odgovorni za različne strukture v grafiki (v Direct3D 10 so to oglišča, ...

Preberi cel članek »

Test 7800 GTX

Test 7800 GTX

GeForce napada v sedmo Po napetem tekmovanju za najhitrejši grafični procesor med NVIDIO in ATI-jem se razvoj novih grafičnih kartic kot kaže umirja. Od splavitve GeForce 6800 je minilo že dobro leto in kljub pritisku ATI-ja v obliki X850 sta oba proizvajalca nekako ostala v pat poziciji. NVIDIA je ...

Preberi cel članek »

DirectX 9.0 - Osvetljevanje

DirectX 9.0 - Osvetljevanje

Tokrat si bomo ogledali eno izmed možnosti osvetljevanja v knjižnici DirectX 9.0. Ker tudi DirectX 9.0 še vedno pozna le osnovne osvetlitvene modele (flat in Gouraud), kljub temu da je strojna oprema že precej časa zmožna tudi česa več, si bomo pomagali s pixel ...

Preberi cel članek »

Nova bitka v vojni grafičnih kartic

Nova bitka v vojni grafičnih kartic

Da je računalništvo hitro se razvijajoča panoga čivkajo že vrabčki na vejah. Da se grafične kartice razvijajo še hitreje od ostalih področij znotraj računalništva, je takisto lahko jasno vsakemu, ki vsaj vsake toliko preleti kakšen cenik ...

Preberi cel članek »

Register Combiners vs. Pixel Shaders

Register Combiners vs. Pixel Shaders

Tokrat bo članek nekoliko bolj tehnične narave. Namen tega članka pa je prikazati kontrast med register combinerji (na primeru NVidine implementacije) in pixel shaderji v DirectX 8.x oziroma klasičnim SetTextureStageState mehanizmom v DirectXu. Za vse, ki se bodo ustrašili, ...

Preberi cel članek »