Onjuist, want juist die dingen lopen op een veel snellere cycle. Die gegevens zijn belangrijk voor het renderen, je wilt dat die zo accuraat mogelijk zijn elke keer dat er een nieuw frame wordt gestart.
Wel dus, afhankelijk van de variant van V-Sync die gebruikt wordt.
Wall of text warning 
TL;DR versie: double-buffered V-Sync = waar Olaf op doelde. Double-buffered = kut, triple-buffered is wat we willen. Alleen zonde van de input lag 
Disclaimer: hoop dat het er allemaal goed uit komt in de post, altijd lastig in tekst en wellicht heb ik me nog ergens vertypt ook 
Ik moet er ook even bij vermelden dat het onderstaande behoorlijk vereenvoudigd is, want er zijn nog veel meer buffers die mee spelen, maar dit geeft de hoofdlijnen weer. Elke situatie die tearing en dergelijke kan veroorzaken komt uiteindelijk op hetzelfde neer: ongesynchroniseerde schrijf bewerkingen. De copy is ook geen echte copy, maar de buffers worden gewoon hernoemd en dergelijke, maar het onderstaande geeft het duidelijker weer voor iemand die niet weet hoe dit in code en game engines zou werken (voor diegenen die dat wel weten: de copy is gewoon het veranderen van een memory offset, als dat tijdens het lezen gebeurt krijg je dus alsnog de verkeerde frames te zien). Ook doen OpenGL en D3D triple buffering anders en is het bij D3D zelfs vervelend doordat er extra latency in zit. Enfin, als puntje bij paaltje komt is het onderstaande een representatie die goed genoeg is om het probleem weer te geven.
Here we go, hoe V-Sync werkt en wat tearing veroorzaakt, want niemand lijkt het te weten. Aannames en definities vooraf:
- 60 Hz monitor
- GPU die in principe meer dan 60 frames per seconde krijgt afgewerkt
- De
monitor haalt frames op uit de
framebuffer bij elke 'refresh' (LCD refresht niet letterlijk, maar dat even terzijde)
Output proces zonder V-Sync
De GPU rendert 60 + x frames per seconde en schrijft deze meteen weg naar de framebuffer zodra een frame klaar is.
Wat zich nu voor kan doen is dat de GPU net frame 3872 aan het wegschrijven is terwijl de monitor juist het frame uit de buffer haalt. Gevolg is dat een dat wat de monitor ophaalt deels 3872 en deels 3871 (het vorige frame) is. Dit is tearing, je ziet twee frames op je scherm.
Schematisch weergegeven ziet dat er uit zoals hieronder waarbij elke 6 puntjes 16.67ms zijn en een pipe een cycle (refresh, write, copy - afhankelijk van welk onderdeel het betreft). Framebuffer is even gefaked om de schrijf vertraging weer te geven, ongeveer 2.75ms (1 puntje).
Correcte, ideale situatie uitgaand van GPU die 120 FPS doet
code:
1
2
3
| [.....|.....|.....|.....|.....|.....|.....|] Monitor
[...|..|..|..|..|..|..|..|..|..|..|..|..|..] Framebuffer
[..|..|..|..|..|..|..|..|..|..|..|..|..|..|] GPU |
Tearing (Tearing op bovenste regel aangegeven met 'x') - dit voorbeeld varieert ergens tussen de 50 en 70 FPS
code:
1
2
3
4
| [.....x...........x.......................]
[.....|.....|.....|.....|.....|.....|.....] Monitor
[.....|....|......|....|.....|.....|.....|] Framebuffer
[....|....|......|....|.....|.....|.....|.] GPU |
Output proces met double-buffered V-Sync
De omschrijving zegt het al, er zijn nu twee buffers. Een framebuffer en een backbuffer. Double buffering heeft enkele restricties en beperkingen:
- De monitor haalt z'n frames nog steeds uit de
framebuffer
- De GPU schrijft nieuwe frames naar de backbuffer, gevolgd door een
copy van back- naar framebuffer
- Omdat de
copy nog steeds tijd kost is de regel dat die
copy pas plaats mag vinden NA een refresh (de GPU krijgt een signaaltje elke keer dat de framebuffer wordt opgehaald, die copy is dus een reactie op die lees actie)
Eind resultaat is dat jouw GPU intern wellicht nog gewoon op 60 + x FPS aan het renderen is, maar jij krijgt er maar 60 te zien; daarom laten Fraps e.d. ook maar 60 FPS zien, omdat zij hun info uit de framebuffer halen. Dit betekent echter dus ook dat sommige frames nooit in de framebuffer terecht komen, afhankelijk van hoe de refresh + copy cycles elkaar opvolgen. Sterker nog, als je GPU 120 FPS aan kan, dan zal de helft van alle frames nooit in de framebuffer komen, alleen kortstondig in de backbuffer. Dit verklaart ook meteen waarom V-Sync inherent input lag heeft, elk frame wordt immers een hele refresh cycle "te laat" weergegeven (om en nabij die 16.67ms in elk geval).
Dit kan echter nog steeds fout gaan. Stel nu dat je FPS tijdelijk naar 40 FPS dropt omdat er ineens wat grafische pracht en praal te zien is. Dat betekent dat er dus tijdens elke monitor refresh cycle slechts twee derde van een frame gerenderd kan worden. Dat betekent dat een deel van de frames twee keer wordt weer gegeven, omdat de GPU pas naar de framebuffer mag schrijven meteen NA een refresh cycle. Het gevolg is dat hoewel je GPU wellicht op 40 FPS zit te renderen, jij NOG minder frames daadwerkelijk te zien krijgt!
Wederom schematisch weergegeven zoals hierboven. Let op, ik heb het start punt van de GPU even verschoven, omdat dit een situatie "midden in" een sessie is, en niet vanaf het absolute begin. Met andere woorden, er zijn al frames gerenderd van tevoren, dus de buffers zijn niet leeg. Daarnaast geef ik even iets meer frames weer zodat je duidelijk ziet wat er gaande is.
Correcte, ideale situatie uitgaand van GPU die 120 FPS doet - je ziet dus ook dat bij 60+ FPS een groot deel van de frames nooit op je scherm komt
code:
1
2
3
4
| [.....|.....|.....|.....|.....|.....|.....|] Monitor
[......|.....|.....|.....|.....|.....|.....] Framebuffer
[.|..|..|..|..|..|..|..|..|..|..|..|..|..|.] Backbuffer
[|..|..|..|..|..|..|..|..|..|..|..|..|..|..] GPU |
40 FPS situatie
code:
1
2
3
4
| [.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|] Monitor
[............|...........|.....|...........|.....|...........] Framebuffer
[.........|........|........|........|........|........|.....] Backbuffer
[........|........|........|........|........|........|......] GPU |
Zoals je ziet worden de eerste twee frames hier twee keer weer gegeven, maar frame 3 volgt frame 2 wel meteen op. Probleem is echter dat waar de GPU deze mooi 25ms uit elkaar heeft gerenderd, ze elkaar in 16.67ms opvolgen, wat in sommige situaties vreemd uit kan zien. Je ziet ook dat er in deze situatie maar liefst 10 refresh cycles van de monitor zijn geweest en slechts 5 nieuwe frames te zien geweest zijn. 30 FPS op die manier dus.
Dit is ook de V-Sync variant die wij zo tyfus irritant vinden, want om dit te vermijden moet er eigenlijk ook exact op een aantal frames gerenderd worden dat goed in die cycle valt: 60, 30, 20, 15 - elk getal waarvan 60 een veelvoud is. Veel engines doen dit dan ook; zodra de 60 FPS niet gehaald wordt, wordt er meteen gehakt naar 30 FPS. Dat is natuurlijk onzin als jij wel constant 55-59 FPS kunt renderen, want dan krijg je automatisch slechts 30 FPS te zien.
Output proces met triple-buffered V-Sync
Yep, je raadt het al, triple-buffering voegt een tweede backbuffer toe. De ideale situatie ga ik hier nu niet meer schetsen, want daar is in principe niets veranderd. Je zit wel nog steeds met 1 frame input lag natuurlijk.
40 FPS situatie
code:
1
2
3
4
5
| [.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|] Monitor
[............|.....|...........|.....|...........|.....|.....] Framebuffer
[........|........|........|........|........|........|......] Backbuffer #1
[.......|........|........|........|........|........|.......] Backbuffer #2
[......|........|........|........|........|........|........] GPU |
Ik zou de grafiek nog langer moeten maken om het goed te laten zien (ik moet dit eigenlijk eens een keer in Excel gooien en gewoon in grafieken uitwerken), maar zoals je hier al kunt zien wordt nu slechts 1 op de 3 frames dubbel weergegeven. Met andere woorden, we krijgen twee derde van de frames te zien op de eerstvolgende cycle; dat is 40 FPS, precies waar we op zitten te renderen. De engine hoeft zichzelf dus ook niet te limiteren. Helaas doen UE3 games geen triple-buffering (waarom weet ik niet precies) en kost dit natuurlijk ook meer video geheugen, omdat er constant niet 1, niet 2 maar 3 frames in het geheugen zitten.
[
Voor 4% gewijzigd door
Werelds op 07-04-2013 16:00
]