[Alg] Opzet rendering spel

Pagina: 1
Acties:

  • JeRa
  • Registratie: Juni 2003
  • Laatst online: 07-05 12:51
Ik ben begonnen aan een Dungeon Keeper-achtig spel gemaakt in SDL i.c.m. OpenGL, en ik probeer nu te bepalen welke van de twee onderstaande methodes het beste is om de scenes te renderen:

1) Er is geen maximum voor frames per seconde; van elke frame wordt het verschil in tijd gemeten. Een voordeel hiervan is dat je heel makkelijk bewegingen kunt programmeren:

nieuwe plaats object = snelheid (m/s) * frametime (s) * global multiplier

De nadelen zijn dat je geen consistente framerate hebt (kan wel oplopen tot 200 frames per seconde en dalen tot 20 fps) en dat je altijd 100% CPU usage hebt als het spel draait.

2) Er wordt een fixed maximum framerate ingesteld (bijvoorbeeld de refresh rate van de monitor). Deze wordt bijgehouden door het programma te laten pauzeren met de sleep-functies die aanwezig zijn in SDL:

pauzetijd = (1 / verwacht aantal frames per seconde) - rendertijd laatste frame - denktijd laatste frame

Voordeel hiervan is dat je de CPU minimaal gebruikt (het spel slaapt immers veel) maar de nadelen hiervan zijn dat het nogal onnauwkeurig kan zijn (ligt aan de sleepfuncties, hier is natuurlijk een limiet) en imho moeilijker goed te implementeren is.

Wat zou ik volgens jullie moeten doen? Andere (betere) methodes zijn natuurlijk welkom :)

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Wat is het probleem dat je 100 % cpu time gebruikt?
Als het is dat andere programma's wel door moeten kunnen draaien kan je gewoon zorgen dat jouw process een lagere prioriteit heeft.

De tweede methode lijkt ook wel redelijk op de eerste. Je moet dan alleen in je render loop een conditie opnemen die kijkt of er al wel toe is aan een nieuwe paint ronde, zoniet dan moet hij nog een tijdje sleepen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Verwijderd

Een voordeel voor een fixed framerate zou kunnen zijn dat je extra processorkracht zou kunnen besteden aan A.I. oid, lijkt me.....

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik gebruik altijd methode 1), maar dan met framerate limiter. Als je frametime dan onder een bepaalde waarde komt kun je een sleep doen oid. Overigens is 100% CPU niet erg, integendeel zelfs, een goed geschreven spel maakt optimaal gebruik van z'n CPU resources, en die is dus altijd 100%

Overigens heeft het wat meer voeten in aarde dan gewoon als frametime de verstreken tijd tussen de huidige en de vorige frame te nemen hoor. Want dat is de verkeerde tijd, je moet eigenlijk de tijd hebben tussen nu en het moment dat de huidige klaar gaat zijn. Nou kun je dat niet berekenen, dus dat moet je een beetje zien te voorspellen. bijvoorbeeld door naar de afgeleide functie te kijken van de framerate, zodat het bij framerate-fluctuaties al een stukje soepeler loopt.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • JeRa
  • Registratie: Juni 2003
  • Laatst online: 07-05 12:51
.oisyn schreef op 18 december 2003 @ 18:50:
Overigens heeft het wat meer voeten in aarde dan gewoon als frametime de verstreken tijd tussen de huidige en de vorige frame te nemen hoor. Want dat is de verkeerde tijd, je moet eigenlijk de tijd hebben tussen nu en het moment dat de huidige klaar gaat zijn.
Op het moment doe ik zoiets om de frametime te berekenen:
code:
1
2
3
4
5
6
begin loop {
frametime = Nu() - startpunt;
startpunt = Nu();
DenkNa();
Render();
}

In feite heb ik dan als frametime de complete tijd die het renderen én het "nadenken" van de vorige frame heeft gekost. Nu kan dit dus een fluctuatie opleveren als een scène plotseling heel druk of heel soepel wordt, maar hoe dit anders te doen weet ik nog niet:
Nou kun je dat niet berekenen, dus dat moet je een beetje zien te voorspellen. bijvoorbeeld door naar de afgeleide functie te kijken van de framerate, zodat het bij framerate-fluctuaties al een stukje soepeler loopt.
Ik kan zo niet op een idee komen hoe een afgeleide functie te maken uit een framerate-functie, daar ik de FPS bereken door gewoonweg een teller te laten lopen en deze elke seconde te laten zien. Zou je een voorbeeld kunnen geven van hoe ik dit evt. beter zou kunnen doen, en rekening kan houden met fluctuaties?

  • Juicy
  • Registratie: December 2000
  • Laatst online: 17:01
.oisyn schreef op 18 december 2003 @ 18:50:
Overigens is 100% CPU niet erg, integendeel zelfs, een goed geschreven spel maakt optimaal gebruik van z'n CPU resources, en die is dus altijd 100%
Dit is natuurlijk bullshit ... Als het spel de processor tijd alleen maar gebruikt om nutteloze frames te genereren dan is het natuurlijk niet goed geschreven. Wie wil nu 200 fps als 100-150 fps meer dan voldoende is alleen maar om de processor te belasten ?!

Doe dan sociaal en laat wat tijd over voor de andere processen. :)

-


  • DiGuru
  • Registratie: April 2003
  • Laatst online: 05-09-2008
JeRa schreef op 21 december 2003 @ 00:41:
[...]

Op het moment doe ik zoiets om de frametime te berekenen:
code:
1
2
3
4
5
6
begin loop {
frametime = Nu() - startpunt;
startpunt = Nu();
DenkNa();
Render();
}
Je kunt ook gewoon na "startpunt = Nu();" kijken hoe lang het nog duurt tot de volgende refresh, daarvoor kun je de functie aanroepen die wacht totdat de volgende vertical retrace begint. Dan heb je een bovengrens. En als je daarna doet: "wachttijd = Nu() - startpunt;", dan weet je precies hoe lang het heeft geduurt.

  • Ericston
  • Registratie: Maart 2001
  • Laatst online: 30-03 17:41
JeRa schreef op 18 december 2003 @ 17:43:
[...]

Wat zou ik volgens jullie moeten doen? Andere (betere) methodes zijn natuurlijk welkom :)
Update je physics afzonderlijk van je rendering!

Roep bijvoorbeeld 30 maal per seconde (regel dit vanuit je 'Main' module) je physics code aan die de posities van objecten aanpast en bijvoorbeeld collision detection en andere leuke physics dingen doet, en roep je rendering code zovaak aan als maar kan of met een delimiter als je wil.

Als je alleen maar Gauss Euler integratie doet over de snelheid ("want dat is wat je feitelijk doet" - quoth every physics tut out there) zoals bij 1) dan zal je niet zo snel problemen krijgen.

Zodra je echter wrijving en collision detection frame based gaat berekenen ga je problemen krijgen, ook al hou je rekening met de frame interval. Door afrondingsfouten en dergelijke werkt je simulatie misschien wel op je eigen bak (met de juiste constants) maar vliegen je objecten op een andere bak met een andere framerate met de snelheid van het licht door een muur heen. :)

Als je hier geen zin in hebt, zou ik voor 1) gaan, zoals .oisyn zegt. :)

edit:
Probeer je gewichtig te klinken, heb je de namen verkeerd...

[ Voor 5% gewijzigd door Ericston op 22-12-2003 00:10 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Een mooie oplossing is natuurlijk een dual-threaded oplossing, met een render-thread en een physics/AI thread. De physics thread is een low-prio thread, die een dual-buffered world update. De huidige wereld is read-only, de volgende wereld is read/write. De high-prio renderthread pakt telkens de huidige wereld, en tekent die. Op die manier ontkoppel je de framerate van je wereld-update rate. Het balanceren van de twee threads doe je door de render thread te suspenden. De policy die je daarvoor kiest bepaalt je uiteindelijke performance.

Een bijkomend voordeel is dat een HT Pentium of dual-proc machine ook sneller loopt.

@oisyn: je wil niet je CPU zo heet stoken dat ie begint te throttlen. Als er dan wel iets moeilijks gebeurt heb je een lagere CPU snelheid. Bovendien klappen al die overgekloke PCtjes van die tweakers als je ze te lang op 100% CPU laat lopen :)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Ericston
  • Registratie: Maart 2001
  • Laatst online: 30-03 17:41
Dat klinkt goed. Wat bedoel je precies met dual-buffered world, wat moet ik me daar bij voorstellen?

edit:
Hmmz, dit zeker ('t is al laat. :)):
De huidige wereld is read-only, de volgende wereld is read/write. De high-prio renderthread pakt telkens de huidige wereld, en tekent die.

[ Voor 36% gewijzigd door Ericston op 22-12-2003 02:47 ]


  • JeRa
  • Registratie: Juni 2003
  • Laatst online: 07-05 12:51
Ericston schreef op 21 december 2003 @ 23:57:
[...]

Update je physics afzonderlijk van je rendering!

Roep bijvoorbeeld 30 maal per seconde (regel dit vanuit je 'Main' module) je physics code aan die de posities van objecten aanpast en bijvoorbeeld collision detection en andere leuke physics dingen doet, en roep je rendering code zovaak aan als maar kan of met een delimiter als je wil.
Ik heb dit ook al eens eerder toegepast, probleem was dat het geen nut heeft om > 30 frames per seconde te gaan renderen aangezien er in minimaal 1 van die frames niets verandert (de physics/posities/etc worden immers maar 30 keer per seconde geupdate).
Een mooie oplossing is natuurlijk een dual-threaded oplossing, met een render-thread en een physics/AI thread. De physics thread is een low-prio thread, die een dual-buffered world update. De huidige wereld is read-only, de volgende wereld is read/write. De high-prio renderthread pakt telkens de huidige wereld, en tekent die. Op die manier ontkoppel je de framerate van je wereld-update rate. Het balanceren van de twee threads doe je door de render thread te suspenden. De policy die je daarvoor kiest bepaalt je uiteindelijke performance.
Mooi idee, maar ik snap niet hoe je het probleem voorkomt dat ik hierboven beschreven heb. Zou ik de renderthread moeten gaan suspenden totdat de FPS ongeveer overeenkomt met de physics/AI-thread?

edit: overigens zou het heel handig zijn als je over dat systeem online documenten o.i.d. zou kunnen geven (of iig de naam van zoiets), dan kan ik het op m'n gemak gaan bestuderen :)

[ Voor 7% gewijzigd door JeRa op 22-12-2003 11:43 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Juicy schreef op 21 december 2003 @ 00:52:

Dit is natuurlijk bullshit...
niet echt
Als het spel de processor tijd alleen maar gebruikt om nutteloze frames te genereren dan is het natuurlijk niet goed geschreven. Wie wil nu 200 fps als 100-150 fps meer dan voldoende is alleen maar om de processor te belasten ?!
Sorry, maar er is meer dan alleen maar dingen om te renderen. Als jij een nieuw spel maakt dat 150-200 fps haalt dan is je geometrie te simpel en stelt het verder niet veel voor, je kunt er namelijk veel meer uit halen. Ik heb het dan ook over moderne spellen die gemaakt zijn door mensen met ervaring, en nee, die spellen doen geen 150-200 fps, maar met de juiste settings (verschilt natuurlijk per hardware-configuratie) iets van 60 fps haalt. En in die moderne spellen is er nog altijd een cpu-schaarste. Dan moet je een afweging maken tussen dingen als graphics, physics en AI, en je hebt nooit genoeg.

En aangezien je resources schaars zijn moet je het break-even point ertussen vinden zodat een bepaalde resource niet de bottleneck vormt. En dat betekent automatisch al je CPU voor 100% gebruiken.

Dat kun je bullshit vinden, maar dat is gewoon de harde realiteit.

En andere processen? Kom op, een game hoort eigenlijk niet eens onder een multi-tasking OS te draaien, dat is gewoon een dodende factor omdat dat betekent dat je resources niet meer voorspelbaar zijn. Een game hoeft zich dus al helemaal niet te bekommeren om andere processen, als een game draait dan heeft die game gewoon de prioriteit boven de rest, en dat vindt de gamer ook

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op 22 december 2003 @ 01:10:
Een mooie oplossing is natuurlijk een dual-threaded oplossing, met een render-thread en een physics/AI thread. De physics thread is een low-prio thread, die een dual-buffered world update. De huidige wereld is read-only, de volgende wereld is read/write. De high-prio renderthread pakt telkens de huidige wereld, en tekent die. Op die manier ontkoppel je de framerate van je wereld-update rate. Het balanceren van de twee threads doe je door de render thread te suspenden. De policy die je daarvoor kiest bepaalt je uiteindelijke performance.
physics wil je meestal niet in een andere thread, dan moet je ten eerste idd al zorgen voor een kopie van de wereld, en ten tweede zit je met synchronisatie tussen de threads. En het hoeft ook niet, omdat tussen 2 frames ook gewoon 1 physics stapje gedaan moet worden, dus dat kan prima in de renderthread.

We hebben het in coronel geprobeerd, verschillende threads, maar het liep gewoon niet lekker. Het liep veel te schokkerig, omdat de physics-thread stapjes van vaste grootte nam. De renderthread pakt dan steeds een scenario dat al klaar is, maar dat scenario is niet berekend voor het moment waarop het getoond wordt. Je krijgt dus frame-aliasing, en dan loopt het niet vloeiend meer.

Voordeel van de dualthread variant is idd wel dat je voordeel hebt bij multi-cpu systemen, maar het is gewoon te irritant om goed te implementeren
je wil niet je CPU zo heet stoken dat ie begint te throttlen. Als er dan wel iets moeilijks gebeurt heb je een lagere CPU snelheid. Bovendien klappen al die overgekloke PCtjes van die tweakers als je ze te lang op 100% CPU laat lopen :)
dat is niet aan de developer maar aan de eindgebruiker. Als een gamer geen fatsoenlijke koeling op z'n cpu heeft waardoor z'n snelheid afneemt is dat zijn eigen schuld, niet die van de developer. Als je daar als developer ook al rekening mee moet gaan houden dan kun je de PC als spelplatform al helemaal in de trashcan kiepen (wat eigenlijk nu al het geval is ;))

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 15:26

Janoz

Moderator Devschuur®

!litemod

.oisyn schreef op 22 december 2003 @ 15:53:

We hebben het in coronel geprobeerd, verschillende threads, maar het liep gewoon niet lekker. Het liep veel te schokkerig, omdat de physics-thread stapjes van vaste grootte nam. De renderthread pakt dan steeds een scenario dat al klaar is, maar dat scenario is niet berekend voor het moment waarop het getoond wordt. Je krijgt dus frame-aliasing, en dan loopt het niet vloeiend meer.
Dat is uiteraard het grote probleem bij die oplossing, maar is het niet mogenlijk om de renderlus een (lineaire) extrapolatie te laten gebruiken? Hierdoor heb je wel vloeiende bewegingen bij hogere framerates (afhankelijk natuurlijk van de gekozen extrapolatie methode) maar ook een vaste physics stap. Hierdoor kun je 'framerate-cheats' als in quake voorkomen en iedereen dezelfde wereld voorschotelen en toch optimaal gebruik maken van de beschikbare rekenkracht.

Ik praaat hier natuurlijk puur theoretisch, maar om naast de wereld data ook een 'richtings coeficient' per vertex op te slaan lijkt me niet een al te grote impact. Het lijkt me dat het meeste geheugen wordt opgebruikt door de textures.

Een problemen die ik bij voorbaat zie is effecten vergelijkbaar met de tearing (die optreed bij het niet syncen op retrace) doordat de physics een hogere framerate heeft dan de render lus (waardoor de renderlus uit 2 verschillende werelden renderd) Dit zou opgelost kunnen worden door 3 versies van de wereld. 1 die de renderer gelocked heeft, 1 source voor de physics en 1 target voor de physics. In principe is dit op die manier makkelijk op te lossen omdat alleen de laatste hiervan writable moet zijn.

Een ander probleem zou de extrapolatie kunnen zijn aangezien dit vaak niet lineair zal zijn. Misschien is het beter om deze te combineren met de 2e orde afgeleide, maar dat kost weer extra data...

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Janoz: aan die extrapolatie hebben wij ook gedacht, maar dat kan in principe alleen goed door het hele physics proces nog een keer te doorlopen met de extra stap-tijd. En dan zit je dus alweer met physics in je renderthread, waardoor het nut van de extra physicsthread al een beetje wegvalt.

Maar het grote probleem hiermee was een niet-kloppend resultaat. Zoals je misschien weet is physics in games heel moeilijk stabiel te krijgen, veel physics engine neigen naar een explosie bij te onregelmatige stap-grootten of teveel krachten die ergens op werken (door afrondingsfouten en onnauwkeurige integral solvers). Als je bijvoorbeeld een kubus hebt die met een hoek van 45 graden op de grond terecht komt, precies op een punt dus, dan kan ie linksom of rechtsom draaien. Door de extrapolatie kan het bijvoorbeeld linksom vallen, maar omdat de physics de berekeningen weer opnieuw doet van voor die extrapolatie, met een andere stapgrootte, kunnen de resultaten dus anders zijn, en valt ie ineens rechtsom. De verschillen tussen de frames kunnen hierdoor best groot zijn, waardoor het geheel een beetje raar overkomt.

Wat je verder zegt over tearing, je moet sowieso een kopie maken van je status (het doublebuffered idee waar MSalters over had), zodat je renderthread altijd met een versie werkt die niet meer veranderd. Anders krijg je natuurlijk grove fouten, als je renderthread iets neerzet wat je physicsthread net aan het wijzigen is. :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 15:26

Janoz

Moderator Devschuur®

!litemod

De extrapolatie gegeven zou ik opslaan op het moment van de physics render. Naast de nieuwe wereld worden daar dus ook de verander parameters opgeslagen (1e en 2e afgeleiden ofzo). De waarden zijn simpel te achterhalen omdat je de vorige physics state ook nog hebt ;). Deze warden worden bij de renderer gewoon gebruikt om van de laatst gephysicrenderde wereld de huidige wereld te maken. De physics moet ongeveer een framerate van 30 (oid) hebben. Hierdoor zullen de sprongen van objecten die toch wat anders dan verwacht bewegen minimaal zijn (hoop ik).

Ik zou trouwens nog kunnen overwegen om, in het geval van 1st person, de kijkrichting rechtstreeks van de input af te lezen. Hierdoor voelt het spel wel erg direct aan.

[ Voor 7% gewijzigd door Janoz op 22-12-2003 16:28 ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ah ja, dat zou natuurlijk ook kunnen. Maar je moet natuurlijk wel denken dat je veelal gebruik maakt van een 3rd party physics library, en dat je zit met deadlines. Even in de code kijken en wat dingen aanpassen is er dan niet zomaar bij :)

Maar het is idd iets om te overwegen als je er de resources voor hebt

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Wat is eigenlijk de praktische invloed van V-sync? De theorie is dat in dat geval je videokaart zal kiezen wanneer de render-tyread moet draaien. Je physics thread heeft een goed idee wanneer dat gaat gebeuren, en kan dus de scene opzetten voor dat exacte moment. Dat voorkomt in elk geval tijdfouten.

De enige vraag is hoe je het rekenwerk op tijd af krijgt. Je moet namelijkt een consistente state hebben op het moment dat je rendert. Dat kan bijvoorbeeld als je je physics thread zo opzet je begint met het opzetten van de huidge scene, en daarna pas vooruitplant (AI en zo). Bij gebrek aan tijd (render ondebreekt je AI werk) verlaag je tijdelijk het grafische detail. Als bij de volgende render het AI werk wel op tijd af is kan je het weer verhogen.
Om deze opzet te laten werken moet je wel een opzet hebben waarin je genoeg werk hebt dat een frame kan wachten (AI, path-planning).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:32

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op 22 december 2003 @ 17:31:
Wat is eigenlijk de praktische invloed van V-sync? De theorie is dat in dat geval je videokaart zal kiezen wanneer de render-tyread moet draaien.
de invloed is vrijwel 0, het enige dat verschilt is dat de frame mogelijk wat later getoond wordt. Maar de cpu staat niet te wachten zoals vroeger, omdat dat hardware-matig geregeld wordt (met een automatische page-flip of een buffer blit tijdens de v-sync). Na het opleveren van een frame kan dus direct aan de volgende worden begonnen.

Goed, er moet natuurlijk nog steeds gewacht worden als de 2e frame al klaar is en de eerste wordt nog niet getoond, maar dan maakt het ook niet zoveel meer uit :)

Overigens loopt het renderen van polygonen vrijwel parallel met de cpu. De commando's en de data wordt over de AGP bus gepompt, en de gpu gaat renderen. Hier hoeft de CPU nauwelijks op te wachten, hij kan de volgende commando's en data al doorsturen. Om dit alles zo efficient mogelijk te laten lopen is wel weer van belang dat je bepaalde vertex-buffer roulerings technieken gebruikt en dat je grote polygoonbatches gebruikt, maar in theorie is het mogelijk om cpu en gpu geheel parallel aan elkaar te laten lopen

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1