Belangrijke datastructuren en hun rol in RenderingNG

Chris Harrelson
Chris Harrelson
Daniel Cheng
Daniel Cheng
Filip Rogers
Philip Rogers
Koji Ishi
Koji Ishi
Ian Kilpatrick
Ian Kilpatrick
Kyle Charbonneau
Kyle Charbonneau

Eerdere berichten in deze serie gaven een overzicht van de doelen, belangrijkste eigenschappen en componenten op hoog niveau van de RenderingNG-architectuur. Laten we nu eens kijken naar de belangrijkste datastructuren die input en output zijn voor de renderingpijplijn.

Deze datastructuren zijn:

  • Framebomen , die zijn samengesteld uit lokale en externe knooppunten die vertegenwoordigen welke webdocumenten zich in welk weergaveproces bevinden en welke Blink-renderer.
  • De onveranderlijke fragmentboom vertegenwoordigt de uitvoer van (en invoer voor) het lay-outbeperkingsalgoritme.
  • Eigenschappenbomen , die de transformatie-, clip-, effect- en schuifhiërarchieën van een webdocument vertegenwoordigen, en die in de hele pijplijn worden gebruikt.
  • Weergavelijsten en verfbrokken zijn de invoer voor de raster- en gelaagdheidsalgoritmen.
  • Compositorframes kapselen oppervlakken, renderoppervlakken en GPU-textuurtegels in die worden gebruikt om te tekenen met behulp van de GPU.

Voordat ik door deze datastructuren loop, wil ik het volgende eenvoudige voorbeeld laten zien wat voortbouwt op een van de vorige post. Ik zal dit voorbeeld een paar keer gebruiken in dit bericht en laten zien hoe de datastructuren erop van toepassing zijn.

<html>
  <div style="overflow: hidden; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
      id="one" src="foo.com/etc"></iframe>
  </div>
  <iframe style="top:200px;
    transform: scale(1.1) translateX(200px)"
    id="two" src="bar.com"></iframe>
</html>

Kader bomen

Chrome kan er soms voor kiezen om een ​​cross-origin-frame in een ander weergaveproces weer te geven dan het bovenliggende frame.

In het voorbeeld uit de inleiding zijn er in totaal drie frames:

Een bovenliggend frame foo.com, met twee iframes.

Met site-isolatie gebruikt Chromium twee weergaveprocessen om deze webpagina weer te geven. Elk weergaveproces heeft zijn eigen weergave van de frameboom voor die webpagina:

Twee framebomen die de twee weergaveprocessen vertegenwoordigen.

Een frame dat in een ander proces wordt weergegeven, wordt weergegeven als een extern frame. Een extern frame bevat de minimale informatie die nodig is om als tijdelijke aanduiding te fungeren bij de weergave, zoals bijvoorbeeld de afmetingen ervan. Het externe frame bevat verder geen informatie die nodig is om de werkelijke inhoud ervan weer te geven.

Een lokaal frame vertegenwoordigt daarentegen een frame dat de standaard weergavepijplijn zal doorlopen die in eerdere berichten is beschreven. Het lokale frame bevat alle informatie die nodig is om de gegevens voor dat frame (zoals de DOM-boom en stijlgegevens) om te zetten in iets dat kan worden weergegeven en weergegeven.

De weergavepijplijn werkt op basis van de granulariteit van een lokaal frameboomfragment . Beschouw een ingewikkelder voorbeeld met foo.com als hoofdframe:

<iframe src="bar.com"></iframe>

En het volgende bar.com subframe:

<iframe src="foo.com/etc"></iframe>

Hoewel er nog steeds slechts twee renderers zijn, zijn er nu drie lokale frameboomfragmenten, waarvan twee in het renderproces voor foo.com en één in het renderproces voor bar.com :

Een weergave van de twee renders en drie frameboomfragmenten.

Om één compositorframe voor de webpagina te produceren, vraagt ​​Viz tegelijkertijd een compositorframe op uit het hoofdframe van elk van de drie lokale framebomen, en voegt deze vervolgens samen. (Zie ook de sectie over compositorframes verderop in dit bericht.)

Het foo.com hoofdframe en het foo.com/other-page maken deel uit van dezelfde frameboom en worden in hetzelfde proces weergegeven. De twee frames hebben echter nog steeds onafhankelijke documentlevenscycli , omdat ze deel uitmaken van verschillende lokale frameboomfragmenten. Om deze reden is het niet mogelijk om één compositorframe voor beide in één update te genereren. Het weergaveproces beschikt niet over voldoende informatie om het compositorframe dat is gegenereerd voor foo.com/other-page rechtstreeks samen te voegen in het compositorframe voor het foo.com hoofdframe. Het out-of-process bovenliggende frame van bar.com kan bijvoorbeeld de weergave van het foo.com/other-url iframe beïnvloeden, door het iframe te transformeren met CSS of delen van het iframe af te sluiten met andere elementen in zijn DOM.

De update-waterval voor visuele eigenschappen

Visuele eigenschappen zoals de apparaatschaalfactor en de viewportgrootte beïnvloeden de gerenderde uitvoer en moeten worden gesynchroniseerd tussen de lokale frameboomfragmenten. Aan de hoofdmap van elk lokaal frameboomfragment is een widgetobject gekoppeld. Updates van visuele eigenschappen gaan naar de widget van het hoofdframe voordat ze van boven naar beneden worden doorgegeven aan de overige widgets. Als de grootte van het venster bijvoorbeeld verandert:

Diagram van het proces dat in de voorgaande tekst is uitgelegd.

Dit proces vindt niet onmiddellijk plaats, dus de gerepliceerde visuele eigenschappen bevatten ook een synchronisatietoken. De Viz-compositor gebruikt dit synchronisatietoken om te wachten tot alle lokale frameboomfragmenten een compositorframe hebben ingediend met het huidige synchronisatietoken. Dit proces vermijdt het mengen van compositorframes met verschillende visuele eigenschappen.

De onveranderlijke fragmentboom

De onveranderlijke fragmentboom is de uitvoer van de lay-outfase van de renderingpijplijn. Het vertegenwoordigt de positie en grootte van alle elementen op de pagina (zonder toegepaste transformaties).

Weergave van de fragmenten in elke boom, waarbij één fragment is gemarkeerd als lay-out nodig.

Elk fragment vertegenwoordigt een deel van een DOM-element. Normaal gesproken is er slechts één fragment per element, maar er kunnen er meer zijn als het bij het afdrukken over verschillende pagina's wordt verdeeld, of over kolommen als het in een context met meerdere kolommen staat.

Na de lay-out wordt elk fragment onveranderlijk en wordt het nooit meer gewijzigd. Belangrijk is dat we ook een aantal aanvullende beperkingen opleggen. Wij doen niet:

  • Sta alle "omhoog"-verwijzingen in de boom toe. (Een kind kan geen verwijzing naar zijn ouder hebben.)
  • "bubbel"-gegevens in de boomstructuur (een kind leest alleen informatie van zijn kinderen, niet van zijn ouder).

Deze beperkingen stellen ons in staat een fragment opnieuw te gebruiken voor een volgende lay-out. Zonder deze beperkingen zouden we vaak de hele boom moeten regenereren, wat duur is.

De meeste lay-outs zijn doorgaans incrementele updates, bijvoorbeeld een web-app die een klein deel van de gebruikersinterface bijwerkt als reactie op het klikken van de gebruiker op een element. Idealiter zou de lay-out alleen moeten werken in verhouding tot wat er daadwerkelijk op het scherm is veranderd. Dit kunnen we bereiken door zoveel mogelijk delen van de vorige boom te hergebruiken. Dit betekent dat we (meestal) alleen de ruggengraat van de boom opnieuw hoeven op te bouwen.

In de toekomst zal dit onveranderlijke ontwerp ons in staat stellen interessante dingen te doen, zoals het indien nodig doorgeven van de onveranderlijke fragmentboom over draadgrenzen (om volgende fasen op een andere draad uit te voeren), het genereren van meerdere bomen voor een vloeiende lay-outanimatie, of het uitvoeren van parallelle speculatieve lay-outs . Het geeft ons ook het potentieel van een multi-threading lay-out zelf.

Inline fragmentitems

Inline-inhoud (voornamelijk gestileerde tekst) gebruikt een iets andere weergave. In plaats van een boomstructuur met vakken en verwijzingen, vertegenwoordigen we inline inhoud in een platte lijst die de boom vertegenwoordigt. Het belangrijkste voordeel is dat een platte lijstrepresentatie voor inlines snel is, handig voor het inspecteren of bevragen van inline datastructuren en geheugenefficiënt is. Dit is uiterst belangrijk voor de prestaties van webweergave, omdat het weergeven van tekst zeer complex is en gemakkelijk het langzaamste deel van de pijplijn kan worden, tenzij sterk geoptimaliseerd.

Als interessante historische opmerking: dit lijkt sterk op de manier waarop Internet Explorer voorheen zijn DOM vertegenwoordigde, aangezien het aanvankelijk op een vergelijkbare manier als een teksteditor was gebouwd.

De platte lijst wordt gemaakt voor elke inline-opmaakcontext in de volgorde van een diepgaande zoekactie in de inline-indelingssubboom. Elke vermelding in de lijst is een tupel van (object, aantal nakomelingen). Beschouw bijvoorbeeld deze DOM:

<div style="width: 0;">
  <span style="color: blue; position: relative;">Hi</span> <b>there</b>.
</div>

(Houd er rekening mee dat de eigenschap width is ingesteld op 0, zodat de regel zich tussen 'Hallo' en 'daar' bevindt.) Wanneer de inline-opmaakcontext voor deze situatie wordt weergegeven als een boomstructuur, ziet deze er als volgt uit:

{
  "Line box": {
    "Box <span>": {
      "Text": "Hi"
    }
  },
  "Line box": {
    "Box <b>": {
      "Text": "There"
    }
  },
  {
    "Text": "."
  }
}

De platte lijst ziet er als volgt uit:

  • (Lijnbox, 2)
  • (Vak <span>, 1)
  • (Tekst "Hallo", 0)
  • (Lijnbox, 3)
  • (Vak <b>, 1)
  • (Tekst "daar", 0)
  • (Tekst ".", 0)

Er zijn veel consumenten van deze datastructuur: toegankelijkheids-API's en geometrie-API's zoals getClientRects en contenteditable . Elk heeft verschillende vereisten. Deze componenten hebben toegang tot de platte datastructuur via een handige cursor.

De cursor heeft API's zoals MoveToNext , MoveToNextLine , CursorForChildren . Deze cursorweergave is om meerdere redenen zeer krachtig voor tekstinhoud:

  • Het herhalen in de diepte-eerst-zoekvolgorde gaat erg snel. Dit wordt heel vaak gebruikt omdat het vergelijkbaar is met cursorbewegingen. Omdat het een platte lijst is, verhoogt diepte-eerst zoeken alleen maar de array-offset, wat snelle iteraties en geheugenlocatie oplevert.
  • Het biedt de mogelijkheid om eerst in de breedte te zoeken, wat bijvoorbeeld nodig is bij het tekenen van de achtergrond van lijn- en inline-vakken.
  • Als u het aantal nakomelingen kent, kunt u snel naar de volgende broer of zus gaan (verhoog gewoon de array-offset met dat aantal).

Eigendomsbomen

Zoals u weet, is de DOM een boom van elementen (plus tekstknooppunten) en kan CSS verschillende stijlen op elementen toepassen.

Deze zijn meestal verkrijgbaar in vier effectsmaken:

  • Lay-out: invoer voor het lay-outbeperkingsalgoritme.
  • Verven: hoe u het element (maar niet de nakomelingen ervan) kunt verven en rasteren.
  • Visueel: raster-/tekeneffecten toegepast op de DOM-substructuur, zoals transformaties, filters en knippen.
  • Scrollen: as-uitgelijnde en afgeronde hoeken knippen en scrollen van de opgenomen subboom.

Eigenschappenbomen zijn datastructuren die uitleggen hoe visuele en scrolleffecten van toepassing zijn op DOM-elementen. Ze bieden de middelen om vragen te beantwoorden zoals: waar bevindt zich, ten opzichte van het scherm, een bepaald DOM-element, gegeven de lay-outgrootte en positie ervan? En: welke volgorde van GPU-bewerkingen moet worden gebruikt om visuele en scroll-effecten toe te passen?

Visuele en scrolleffecten op internet zijn in hun volle glorie erg ingewikkeld. Het belangrijkste wat eigenschapsbomen doen is dus die complexiteit vertalen in een enkele datastructuur die precies hun structuur en betekenis weergeeft, terwijl tegelijkertijd de rest van de complexiteit van de DOM en CSS wordt verwijderd. Hierdoor kunnen we met veel meer vertrouwen algoritmen voor compositie en scrollen implementeren. In het bijzonder:

  • Potentieel foutgevoelige geometrie en andere berekeningen kunnen op één plek worden gecentraliseerd.
  • De complexiteit van het bouwen en bijwerken van eigendomsbomen wordt geïsoleerd in één renderingpijplijnfase.
  • Het is veel gemakkelijker en sneller om eigenschappenbomen naar verschillende threads en processen te sturen dan de volledige DOM-status, waardoor het mogelijk wordt om ze voor veel gebruiksscenario's te gebruiken.
  • Hoe meer gebruiksscenario's er zijn, hoe meer winst we kunnen behalen met bovenop gebouwde geometrie-caching, omdat ze elkaars caches kunnen hergebruiken.

RenderingNG gebruikt eigendomsbomen voor vele doeleinden, waaronder:

  • Compositing scheiden van verf, en compositing van de rode draad.
  • Bepalen van een optimale compositie-/tekenstrategie.
  • IntersectionObserver- geometrie meten.
  • Werk vermijden voor offscreen-elementen en GPU-textuurtegels.
  • Efficiënt en accuraat ontlakken van verf en raster.
  • Meten van lay-outverschuiving en grootste inhoudsvolle verf in Core Web Vitals.

Elk webdocument heeft vier afzonderlijke eigenschappenbomen: transformeren, knippen, effect en scrollen.(*) De transformatieboom vertegenwoordigt CSS-transformaties en scrollen. (Een scrolltransformatie wordt weergegeven als een 2D-transformatiematrix.) De clipboom vertegenwoordigt overloopclips . De effectenboom vertegenwoordigt alle andere visuele effecten: dekking, filters, maskers, overvloeimodi en andere soorten clips, zoals clippad. De scrollboom vertegenwoordigt informatie over scrollen, zoals hoe scrolls aan elkaar gekoppeld zijn ; het is nodig om te kunnen scrollen in de compositor-thread. Elk knooppunt in een eigenschappenboom vertegenwoordigt een scroll- of visueel effect dat wordt toegepast door een DOM-element. Als het meerdere effecten heeft, kan er in elke boom meer dan één eigenschapsboomknooppunt voor hetzelfde element zijn.

De topologie van elke boom is als een schaarse weergave van de DOM. Als er bijvoorbeeld drie DOM-elementen met overloopclips zijn, zullen er drie knooppunten van de clipboom zijn, en zal de structuur van de clipboom de blokrelatie tussen de overloopclips volgen. Er zijn ook verbindingen tussen de bomen. Deze links geven de relatieve DOM-hiërarchie aan, en dus de volgorde van toepassing, van de knooppunten. Als een transformatie op een DOM-element bijvoorbeeld onder een ander DOM-element met een filter ligt, dan geldt de transformatie uiteraard vóór het filter.

Elk DOM-element heeft een eigenschapsboomstatus , die uit vier tupels bestaat (transform, clip, effect, scroll) die de dichtstbijzijnde voorouderclip-, transformatie- en effectboomknooppunten aangeeft die van kracht zijn op dat element. Dit is erg handig, omdat we met deze informatie precies weten welke lijst met clips, transformaties en effecten op dat element van toepassing is, en in welke volgorde. Dit vertelt ons waar het op het scherm staat en hoe we het moeten tekenen.

Voorbeeld

( bron )

<html>
  <div style="overflow: scroll; width: 100px; height: 100px;">
    <iframe style="filter: blur(3px);
      transform: rotateZ(1deg);
      width: 100px; height: 300px"
  id="one" srcdoc="iframe one"></iframe>
  </div>
  <iframe style="top:200px;
      transform: scale(1.1) translateX(200px)" id=two
      srcdoc="iframe two"></iframe>
</html>

Voor het voorgaande voorbeeld (dat enigszins afwijkt van dat in de inleiding), zijn hier de belangrijkste elementen van de gegenereerde eigenschappenbomen:

Een voorbeeld van de verschillende elementen in de eigenschappenboom.

Lijsten weergeven en stukjes schilderen

Een weergave-item bevat tekenopdrachten op laag niveau (zie hier ) die kunnen worden gerasterd met Skia . Weergave-items zijn doorgaans eenvoudig, met slechts een paar tekenopdrachten, zoals het tekenen van een rand of achtergrond. De verfboomwandeling herhaalt zich over de lay-outboom en de bijbehorende fragmenten in de CSS-tekenvolgorde om een ​​weergave-itemlijst te produceren.

Bijvoorbeeld:

Een blauw vak met de woorden 'Hallo wereld' in een groene rechthoek.

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="blue" style="width:100px;
  height:100px; background:blue;
  position:absolute;
  top:0; left:0; z-index:-1;">
</div>

Deze HTML en CSS zouden de volgende weergavelijst opleveren, waarbij elke cel een weergave-item is:

Achtergrond van weergave #blue achtergrond #green achtergrond #green inlinetekst
drawRect met maat 800x600 en kleur wit. drawRect met maat 100x100 op positie 0,0 en kleur blauw. drawRect met maat 80x18 op positie 8,8 en kleur groen. drawTextBlob met positie 8,8 en tekst "Hallo wereld".

De lijst met weergave-items is achterstevoren geordend. In het bovenstaande voorbeeld staat de groene div vóór de blauwe div in DOM-volgorde, maar de CSS-verfvolgorde vereist dat de blauwe div met negatieve z-index vóór ( stap 3 ) de groene div ( stap 4.1 ) wordt geschilderd. Weergave-items komen grofweg overeen met atomaire stappen van de CSS-verforderspecificatie. Eén enkel DOM-element kan resulteren in verschillende weergave-items, zoals hoe #green een weergave-item heeft voor de achtergrond en een ander weergave-item voor de inline tekst. Deze granulariteit is belangrijk voor het weergeven van de volledige complexiteit van de CSS-kleurvolgordespecificatie, zoals interleaving gecreëerd door een negatieve marge:

Een groene rechthoek, met een grijs kader gedeeltelijk eroverheen en de woorden 'Hallo wereld.'

<div id="green" style="background:green; width:80px;">
    Hello world
</div>
<div id="gray" style="width:35px; height:20px;
  background:gray;margin-top:-10px;"></div>

Dit zou de volgende weergavelijst opleveren, waarbij elke cel een weergave-item is:

Achtergrond van weergave #green achtergrond #gray achtergrond #green inlinetekst
drawRect met maat 800x600 en kleur wit. drawRect met maat 80x18 op positie 8,8 en kleur groen. drawRect met maat 35x20 op positie 8,16 en kleur grijs. drawTextBlob met positie 8,8 en tekst "Hallo wereld".

De weergave-itemlijst wordt opgeslagen en hergebruikt bij latere updates. Als een lay-outobject tijdens de verfboomwandeling niet is gewijzigd, worden de weergave-items ervan uit de vorige lijst gekopieerd. Een aanvullende optimalisatie is afhankelijk van een eigenschap van de CSS-verforderspecificatie: het stapelen van contexten verft atomair. Als er binnen een stapelcontext geen lay-outobject is gewijzigd, slaat de verfboomwandeling de stapelcontext over en kopieert de volledige reeks weergave-items uit de vorige lijst.

De huidige eigenschappenboomstatus wordt gehandhaafd tijdens de verfboomwandeling en de weergave-itemlijst wordt gegroepeerd in "brokken" weergave-items die dezelfde eigenschappenboomstatus delen. Dit wordt gedemonstreerd in het volgende voorbeeld:

Een roze doos met een gekantelde oranje doos.

<div id="scroll" style="background:pink; width:100px;
   height:100px; overflow:scroll;
   position:absolute; top:0; left:0;">
    Hello world
    <div id="orange" style="width:75px; height:200px;
      background:orange; transform:rotateZ(25deg);">
        I'm falling
    </div>
</div>

Dit zou de volgende weergavelijst opleveren, waarbij elke cel een weergave-item is:

Achtergrond van weergave #scroll #scroll inline tekst #orange achtergrond #orange inline tekst
drawRect met maat 800x600 en kleur wit. drawRect met maat 100x100 op positie 0,0 en kleur roze. drawTextBlob met positie 0,0 en tekst "Hallo wereld". drawRect met maat 75x200 op positie 0,0 en kleur oranje. drawTextBlob met positie 0,0 en tekst "Ik val".

De transformatie-eigenschapsboom en verfbrokken zouden dan zijn (vereenvoudigd voor beknoptheid):

Een afbeelding van de voorgaande tabel, de eerste twee cellen in deel 1, de derde in deel 2, de laatste twee cellen in deel 3.

De geordende lijst met verfbrokken, die groepen weergave-items en een eigenschappenboomstatus zijn, zijn de invoer voor de gelaagdheidsstap van de weergavepijplijn. De volledige lijst met stukjes verf zou kunnen worden samengevoegd tot één samengestelde laag en samen worden gerasterd, maar dit zou elke keer dat de gebruiker scrolde, dure rastering vereisen. Er zou voor elk verfstuk een samengestelde laag kunnen worden gemaakt en afzonderlijk kunnen worden gerasterd om alle herrasterisatie te voorkomen, maar dat zou het GPU-geheugen snel uitputten. De gelaagdheidsstap moet een afweging maken tussen GPU-geheugen en het verlagen van de kosten als er dingen veranderen. Een goede algemene aanpak is om chunks standaard samen te voegen, en geen paint chunks samen te voegen die eigenschappenboomstatussen hebben waarvan verwacht wordt dat ze veranderen in de compositor-thread, zoals bij het scrollen door de compositor-thread of bij compositor-thread-transformatie-animaties.

Het voorgaande voorbeeld zou idealiter twee samengestelde lagen moeten opleveren:

  • Een samengestelde laag van 800 x 600 met de tekenopdrachten:
    1. drawRect met maat 800x600 en kleur wit
    2. drawRect met maat 100x100 op positie 0,0 en kleur roze
  • Een samengestelde laag van 144x224 met de tekenopdrachten:
    1. drawTextBlob met positie 0,0 en tekst "Hallo wereld"
    2. vertalen 0,18
    3. rotateZ(25deg)
    4. drawRect met maat 75x200 op positie 0,0 en kleur oranje
    5. drawTextBlob met positie 0,0 en tekst "Ik val"

Als de gebruiker #scroll scrolt, wordt de tweede samengestelde laag verplaatst, maar is er geen rastering nodig.

Voor het voorbeeld hier , uit het vorige gedeelte over eigendomsbomen, zijn er zes verfbrokken. Samen met hun eigenschappenboomstatussen (transformeren, knippen, effect, scrollen) zijn dit:

  • Documentachtergrond: document scrollen, documentclip, root, document scrollen.
  • Horizontale, verticale en scrollhoek voor div (drie afzonderlijke verfbrokken): document scrollen, documentclip, #one vervaging, document scrollen.
  • Iframe #one : #one roteren, overflow scroll-clip, #one vervagen, div scrollen.
  • Iframe #two : #two schaal, documentclip, root, documentscroll.

Compositorframes: oppervlakken, renderoppervlakken en GPU-textuurtegels

Zoals besproken in het vorige bericht (een uitgewerkt voorbeeld vindt u hier ), beheren de browser- en renderprocessen de rasterisatie van inhoud en verzenden vervolgens compositorframes naar het Viz-proces voor presentatie op het scherm. Compositorframes zijn de manier waarop RenderingNG representeert hoe gerasterde inhoud aan elkaar wordt samengevoegd en deze efficiënt wordt getekend met behulp van de GPU.

Tegels

In theorie zou een renderproces of browserprocescompositor pixels kunnen rasteren in een enkele textuur met de volledige grootte van de weergavepoort van de renderer en die textuur naar Viz kunnen sturen. Om het weer te geven, hoeft de weergavecompositor alleen maar de pixels van die enkele textuur naar de juiste positie in de framebuffer (bijvoorbeeld het scherm) te kopiëren. Als die compositor echter ook maar één enkele pixel zou willen bijwerken, zou hij de volledige viewport opnieuw moeten rasteren en een nieuwe textuur naar Viz moeten sturen.

In plaats daarvan is de viewport verdeeld in tegels. Een afzonderlijke GPU-textuurtegel ondersteunt elke tegel met de gerasterde pixels voor een deel van de viewport. De renderer kan vervolgens individuele tegels bijwerken of zelfs alleen de positie op het scherm voor de bestaande tegels wijzigen. Wanneer u bijvoorbeeld door een website scrollt, verschoof de positie van bestaande tegels naar boven en slechts af en toe zou een nieuwe tegel moeten worden gerasterd voor inhoud verderop op de pagina.

Vier tegels.

De afbeelding hierboven toont een afbeelding van een zonnige dag, met vier tegels. Wanneer er een scroll plaatsvindt, begint een vijfde tegel te verschijnen. Eén van de tegels heeft slechts één kleur (hemelsblauw), en er staat een video en een iframe bovenaan. Wat leidt tot het volgende onderwerp.

Quads en oppervlakken

GPU-textuurtegels zijn een speciaal soort quad , wat slechts een mooie naam is voor de ene of andere textuurcategorie. Een quad identificeert de invoertextuur en geeft aan hoe deze kan worden getransformeerd en er visuele effecten op kunnen worden toegepast. Normale inhoudtegels hebben bijvoorbeeld een transformatie die hun x-, y-positie in het tegelraster aangeeft.

GPU-textuurtegels.

Deze gerasterde tegels zijn verpakt in een render pass , een lijst met quads. De renderpas bevat geen pixelinformatie; in plaats daarvan heeft het instructies over waar en hoe elke quad moet worden getekend om de gewenste pixeluitvoer te produceren. Er is een tekenquad voor elke GPU-textuurtegel. De weergavecompositor hoeft alleen maar door de lijst met quads te bladeren en ze allemaal met de opgegeven visuele effecten te tekenen, om de gewenste pixeluitvoer voor de renderpass te produceren. Het samenstellen van draw quads voor een renderpass kan efficiënt op de GPU worden gedaan, omdat de toegestane visuele effecten zorgvuldig worden gekozen zodat deze rechtstreeks aan GPU-functies zijn gekoppeld.

Naast de gerasterde tegels zijn er nog meer typen tekenquads. Er zijn bijvoorbeeld effen kleurentekenquads die helemaal niet door een textuur worden ondersteund, of textuurtekenquads voor niet-tegeltexturen zoals video of canvas.

Het is ook mogelijk dat een compositorframe een ander compositorframe insluit. De browsercompositor produceert bijvoorbeeld een compositorframe met de gebruikersinterface van de browser en een lege rechthoek waarin de inhoud van de rendercompositor wordt ingebed. Een ander voorbeeld zijn site-geïsoleerde iframes. Deze inbedding gebeurt via oppervlakken .

Wanneer een compositor een compositorframe indient, gaat dit vergezeld van een identificatie, een zogenaamde Surface ID , waardoor andere compositorframes het door middel van referentie kunnen insluiten. Het nieuwste compositorframe dat met een bepaald oppervlak-ID wordt ingediend, wordt opgeslagen door Viz. Een ander compositorframe kan er later naar verwijzen via een Surface Draw Quad , en daardoor weet Viz wat hij moet tekenen. (Houd er rekening mee dat oppervlaktetekenquads alleen oppervlakte-ID's bevatten, en geen texturen.)

Tussentijdse renderpasses

Sommige visuele effecten, zoals veel filters of geavanceerde overvloeimodi, vereisen dat twee of meer quads naar een tussenliggende textuur worden getrokken. Vervolgens wordt de tussenliggende textuur in een bestemmingsbuffer op de GPU (of mogelijk een andere tussenliggende textuur) getekend, waarbij tegelijkertijd het visuele effect wordt toegepast. Om dit mogelijk te maken, bevat een compositorframe feitelijk een lijst met renderpassen. Er is altijd een root-renderpass, die als laatste wordt getekend en waarvan de bestemming overeenkomt met de framebuffer, en er kunnen er meer zijn.

De mogelijkheid van meerdere renderpasses verklaart de naam 'render pass'. Elke doorgang moet opeenvolgend op de GPU worden uitgevoerd, in meerdere "doorgangen", terwijl een enkele doorgang kan worden voltooid in een enkele, massaal parallelle GPU-berekening.

Aggregatie

Er worden meerdere compositorframes naar Viz verzonden en deze moeten samen naar het scherm worden getekend. Dit wordt bereikt door een aggregatiefase die ze omzet in een enkel, geaggregeerd compositorframe. Aggregatie vervangt oppervlaktetekenquads door de compositorframes die ze specificeren. Het is ook een kans om onnodige tussenliggende texturen of inhoud buiten het scherm te optimaliseren. In veel gevallen heeft het compositorframe voor een site-geïsoleerd iframe bijvoorbeeld geen eigen tussentextuur nodig, en kan het rechtstreeks in de framebuffer worden getekend via geschikte tekenquads. In de aggregatiefase worden dergelijke optimalisaties uitgezocht en toegepast op basis van mondiale kennis die niet toegankelijk is voor individuele rendercompositors.

Voorbeeld

Hier zijn de feitelijke compositorframes die het voorbeeld uit het begin van dit bericht vertegenwoordigen.

  • foo.com/index.html oppervlak: id=0
    • Render pass 0: teken naar uitvoer.
      • Render pass draw quad: teken met 3px vervaging en clip in render pass 0.
        • Renderpas 1:
          • Teken quads voor de tegelinhoud van #one iframe, met x- en y-posities voor elk.
      • Oppervlaktetekening quad: met ID 2, getekend met schaal en vertaaltransformatie.
  • Browser UI-oppervlak: ID=1
    • Render pass 0: teken naar uitvoer.
      • Teken quads voor browser-UI (ook betegeld)
  • bar.com/index.html oppervlak: ID=2
    • Render pass 0: teken naar uitvoer.
      • Teken quads voor de inhoud van #two iframe, met x- en y-posities voor elk.

Conclusie

Bedankt voor het lezen! Samen met de vorige twee berichten rondt dit het overzicht van RenderingNG af. Vervolgens zullen we dieper ingaan op de uitdagingen en technologie binnen veel van de subcomponenten van de renderingpijplijn, helemaal van begin tot eind. Die komen binnenkort!

Illustraties door Una Kravets.