RenderingNG-architectuur

Chris Harrelson
Chris Harrelson

Hier vindt u hoe de componentonderdelen van RenderingNG zijn opgezet en hoe de renderingpijplijn er doorheen stroomt.

Beginnend op het hoogste niveau zijn de taken van het renderen:

  1. Render inhoud in pixels op het scherm.
  2. Animeer visuele effecten op de inhoud van de ene staat naar de andere.
  3. Scroll als reactie op invoer.
  4. Leid invoer efficiënt naar de juiste plaatsen, zodat ontwikkelaarsscripts en andere subsystemen kunnen reageren.

De inhoud die moet worden weergegeven, bestaat uit een boom met frames voor elk browsertabblad, plus de browserinterface. En een stroom ruwe invoergebeurtenissen van aanraakschermen, muizen, toetsenborden en andere hardwareapparaten.

Elk frame bevat:

  • DOM-staat
  • CSS
  • Doeken
  • Externe bronnen, zoals afbeeldingen, video, lettertypen en SVG

Een frame is een HTML-document, plus de URL ervan. Een webpagina die op een browsertabblad wordt geladen, heeft een frame op het hoogste niveau, onderliggende frames voor elk iframe dat is opgenomen in het document op het hoogste niveau, en hun recursieve iframe-afstammelingen.

Een visueel effect is een grafische bewerking die op een bitmap wordt toegepast, zoals scrollen, transformeren, knippen, filteren, dekking of overvloeien.

Architectuurcomponenten

In RenderingNG zijn deze taken logisch verdeeld over verschillende fasen en codecomponenten. De componenten komen terecht in verschillende CPU-processen, threads en subcomponenten binnen die threads. Ze spelen allemaal een belangrijke rol bij het bereiken van betrouwbaarheid , schaalbare prestaties en uitbreidbaarheid voor alle webinhoud.

Pijpleidingstructuur renderen

Diagram van de renderingpijplijn.
Pijlen geven de in- en uitgangen van elke fase aan. Fasen worden op kleur aangegeven om aan te geven welke draad of welk proces ze uitvoeren. In sommige gevallen kunnen etappes op meerdere plaatsen worden uitgevoerd, afhankelijk van de omstandigheden. Daarom hebben sommige fasen twee kleuren. Groene fasen maken het proces de rode draad; geel zijn renderprocescompositors; oranje fasen zijn het proces.

Het renderen verloopt in een pijplijn, waarbij onderweg een aantal fasen en artefacten worden gecreëerd. Elke fase vertegenwoordigt code die één goed gedefinieerde taak binnen het renderen uitvoert. De artefacten zijn datastructuren die input of output zijn van de fasen.

De fasen zijn:

  1. Animeren: verander berekende stijlen en muteer eigendomsbomen in de loop van de tijd op basis van declaratieve tijdlijnen.
  2. Stijl: pas CSS toe op de DOM en maak berekende stijlen .
  3. Lay-out: bepaal de grootte en positie van DOM-elementen op het scherm en creëer de onveranderlijke fragmentboom .
  4. Voorschilderen: bereken eigenschappenbomen en maak eventuele bestaande weergavelijsten en GPU- textuurtegels ongeldig .
  5. Scrollen: update de scroll-offset van documenten en scrollbare DOM-elementen door eigenschappenbomen te muteren.
  6. Verf: bereken een weergavelijst die beschrijft hoe GPU-textuurtegels vanuit de DOM moeten worden gerasterd.
  7. Commit: kopieer eigenschappenbomen en de weergavelijst naar de compositor-thread.
  8. Gelaagd maken: deel de weergavelijst op in een samengestelde lagenlijst voor onafhankelijke rastering en animatie.
  9. Raster-, decodeer- en verfwerkjes: zet respectievelijk weergavelijsten, gecodeerde afbeeldingen en verfwerkletcode om in GPU-textuurtegels .
  10. Activeren: maak een compositorframe dat weergeeft hoe GPU-tegels op het scherm moeten worden getekend en gepositioneerd, samen met eventuele visuele effecten.
  11. Aggregeren: combineer compositorframes van alle zichtbare compositorframes in één enkel, globaal compositorframe.
  12. Tekenen: voer het geaggregeerde compositorframe uit op de GPU om pixels op het scherm te creëren.

Fasen van de weergavepijplijn kunnen worden overgeslagen als ze niet nodig zijn. Animaties van visuele effecten en scrollen kunnen bijvoorbeeld de lay-out, pre-paint en paint overslaan. Daarom zijn animatie en scrollen in het diagram gemarkeerd met gele en groene stippen. Als layout, pre-paint en paint kunnen worden overgeslagen voor visuele effecten, kunnen ze volledig op de compositor-thread worden uitgevoerd en de hoofdthread worden overgeslagen.

De weergave van de browser-UI wordt hier niet direct weergegeven, maar kan worden gezien als een vereenvoudigde versie van dezelfde pijplijn (en in feite deelt de implementatie ervan een groot deel van de code). Video (ook niet direct afgebeeld) wordt over het algemeen weergegeven met onafhankelijke code die frames decodeert in GPU-textuurtegels die vervolgens worden aangesloten op compositorframes en de tekenstap.

Proces- en draadstructuur

CPU-processen

Het gebruik van meerdere CPU-processen zorgt voor prestatie- en beveiligingsisolatie tussen sites en van de browserstatus, en stabiliteit en beveiligingsisolatie van GPU-hardware.

Diagram van de verschillende delen van de CPU-processen

  • Het weergaveproces rendert, animeert, scrollt en routeert invoer voor een enkele combinatie van site en tabblad. Er zijn verschillende renderprocessen.
  • Het browserproces rendert, animeert en routeert invoer voor de gebruikersinterface van de browser (inclusief de adresbalk, tabbladtitels en pictogrammen), en stuurt alle resterende invoer door naar het juiste weergaveproces. Er is één browserproces.
  • Het Viz-proces verzamelt composities van meerdere weergaveprocessen plus het browserproces. Het rastert en tekent met behulp van de GPU. Er is één Viz-proces.

Verschillende sites eindigen altijd in verschillende weergaveprocessen.

Meerdere browsertabbladen of vensters van dezelfde site gebruiken doorgaans verschillende weergaveprocessen, tenzij de tabbladen gerelateerd zijn, zoals de ene die de andere opent . Onder sterke geheugendruk op de desktop kan Chromium meerdere tabbladen van dezelfde site in hetzelfde weergaveproces plaatsen, zelfs als ze niet gerelateerd zijn.

Binnen één browsertabblad bevinden frames van verschillende sites zich altijd in verschillende weergaveprocessen, maar frames van dezelfde site bevinden zich altijd in hetzelfde weergaveproces. Vanuit het perspectief van weergave is het belangrijke voordeel van meerdere weergaveprocessen dat cross-site iframes en tabbladen prestatie-isolatie van elkaar bereiken. Bovendien kunnen origines kiezen voor nog meer isolatie .

Er is precies één Viz-proces voor heel Chromium, omdat er meestal maar één GPU en scherm is om naar toe te tekenen.

Het scheiden van Viz in zijn eigen proces is goed voor de stabiliteit in het geval van bugs in GPU-stuurprogramma's of hardware. Het is ook goed voor beveiligingsisolatie, wat belangrijk is voor GPU-API's zoals Vulkan en beveiliging in het algemeen .

Omdat de browser veel tabbladen en vensters kan hebben, en ze allemaal browser-UI-pixels hebben om te tekenen, vraag je je misschien af: waarom is er precies één browserproces? De reden is dat slechts één van hen tegelijk gefocust is; in feite zijn niet-zichtbare browsertabbladen meestal gedeactiveerd en wordt al hun GPU-geheugen verwijderd. Complexe browser-UI-renderingfuncties worden echter ook steeds vaker geïmplementeerd in renderprocessen (bekend als WebUI ). Dit is niet om redenen van prestatie-isolatie, maar om te profiteren van het gebruiksgemak van de webweergave-engine van Chromium.

Op oudere Android-apparaten worden het weergave- en browserproces gedeeld bij gebruik in een WebView (dit geldt niet voor Chromium op Android in het algemeen, alleen voor WebView). Op WebView wordt het browserproces ook gedeeld met de insluitingsapp, en WebView heeft slechts één weergaveproces.

Er is soms ook een hulpprogramma voor het decoderen van beveiligde video-inhoud. Dit proces is niet weergegeven in de voorgaande diagrammen.

Draden

Threads helpen prestatie-isolatie en reactievermogen te bereiken, ondanks langzame taken, parallellisatie van pijplijnen en meervoudige buffering.

Diagram van het renderproces.

  • De rode draad voert scripts uit, de rendering-gebeurtenislus, de documentlevenscyclus, hittests, verzending van scriptgebeurtenissen en het parseren van HTML, CSS en andere gegevensformaten.
    • Hoofdthreadhelpers voeren taken uit zoals het maken van afbeeldingsbitmaps en blobs waarvoor codering of decodering vereist is.
    • Web Workers voeren een script uit en een rendering-gebeurtenislus voor OffscreenCanvas.
  • De Compositor-thread verwerkt invoergebeurtenissen, voert scrollen en animaties van webinhoud uit, berekent de optimale gelaagdheid van webinhoud en coördineert beelddecodering, verfwerkjes en rastertaken.
    • Compositor-threadhelpers coördineren Viz-rastertaken en voeren afbeeldingsdecoderingstaken uit, verfworklets en fallback-raster.
  • Media-, demuxer- of audio-uitvoerthreads decoderen, verwerken en synchroniseren video- en audiostreams. (Houd er rekening mee dat video parallel wordt uitgevoerd met de hoofdrenderingpijplijn.)

Het scheiden van de hoofdthreads en de compositorthreads is van cruciaal belang voor de prestatie-isolatie van animatie en scrollen van hoofdthreadwerk.

Er is slechts één rode draad per weergaveproces, ook al kunnen meerdere tabbladen of frames van dezelfde site in hetzelfde proces terechtkomen. Er is echter prestatie-isolatie van werk dat wordt uitgevoerd in verschillende browser-API's. Het genereren van afbeeldingsbitmaps en blobs in de Canvas API wordt bijvoorbeeld uitgevoerd in een hoofdthread-helperthread.

Op dezelfde manier is er slechts één compositor-thread per weergaveproces. Het is over het algemeen geen probleem dat er maar één is, omdat alle erg dure bewerkingen op de compositor-thread worden gedelegeerd naar de compositor-werkthreads of het Viz-proces, en dit werk kan parallel worden gedaan met invoerroutering, scrollen of animatie. . Compositor-werkthreads coördineren de taken die in het Viz-proces worden uitgevoerd, maar GPU-versnelling kan overal mislukken om redenen buiten de controle van Chromium, zoals bugs in stuurprogramma's. In deze situaties zal de werkthread het werk doen in een fallback-modus op de CPU.

Het aantal compositor-werkthreads is afhankelijk van de mogelijkheden van het apparaat. Desktops zullen bijvoorbeeld over het algemeen meer threads gebruiken, omdat ze meer CPU-kernen hebben en minder batterijbelast zijn dan mobiele apparaten. Dit is een voorbeeld van opschalen en afschalen .

De threading-architectuur van het renderproces is een toepassing van drie verschillende optimalisatiepatronen:

  • Hulpthreads : stuur langlopende subtaken naar extra threads om de bovenliggende thread te laten reageren op andere, gelijktijdige verzoeken. De hoofdthreadhelper- en compositorhelperthreads zijn goede voorbeelden van deze techniek.
  • Meervoudige buffering : toon eerder gerenderde inhoud tijdens het renderen van nieuwe inhoud, om de latentie van het renderen te verbergen. De compositor-thread gebruikt deze techniek.
  • Parallellisatie van pijpleidingen: voer de weergavepijplijn op meerdere plaatsen tegelijk uit. Dit is hoe scrollen en animatie snel kunnen zijn; zelfs als er een update voor de weergave van de hoofdthread plaatsvindt, kunnen scrollen en animatie parallel worden uitgevoerd.

Browserproces

Een browserprocesdiagram dat de relatie toont tussen de Render- en compositing-thread, en de render- en compositing-thread-helper.

  • De render- en compositingthread reageert op invoer in de gebruikersinterface van de browser en leidt andere invoer naar het juiste weergaveproces; lay-out en schildert de browser-UI.
  • De render- en compositing-thread-helpers voeren afbeeldingsdecoderingstaken uit en fallback-raster of decodering.

De render- en compositingthread van het browserproces zijn vergelijkbaar met de code en functionaliteit van een renderproces, behalve dat de hoofdthread en de compositorthread in één zijn gecombineerd. Er is in dit geval slechts één thread nodig omdat er geen behoefte is aan prestatie-isolatie van lange hoofdthreadtaken, aangezien deze er niet zijn door het ontwerp.

Zie proces

Het Viz-proces omvat de GPU-hoofdthread en de displaycompositor-thread.

  • De GPU-hoofdthreadrasters geven lijsten en videoframes weer in GPU-textuurtegels, en tekenen compositorframes naar het scherm.
  • De display-compositor-thread verzamelt en optimaliseert de compositie van elk weergaveproces, plus het browserproces, in een enkel compositor-frame voor presentatie op het scherm.

Raster en draw gebeuren over het algemeen op dezelfde thread, omdat ze allebei afhankelijk zijn van GPU-bronnen, en het moeilijk is om op betrouwbare wijze multi-threaded gebruik te maken van de GPU (gemakkelijkere multi-threaded toegang tot de GPU is één motivatie voor het ontwikkelen van de nieuwe Vulkan- standaard ). Op Android WebView is er een aparte renderthread op besturingssysteemniveau voor tekenen, vanwege de manier waarop WebViews zijn ingebed in een native app. Andere platforms zullen in de toekomst waarschijnlijk zo'n draad hebben.

De weergavecompositor bevindt zich op een andere thread omdat deze te allen tijde moet reageren en geen enkele mogelijke bron van vertraging op de GPU-hoofdthread mag blokkeren. Eén oorzaak van de vertraging van de GPU-hoofdthread zijn oproepen naar niet-Chromium-code, zoals leverancierspecifieke GPU-stuurprogramma's, die op moeilijk te voorspellen manieren traag kunnen zijn.

Componentenstructuur

Binnen elke hoofd- of compositor-thread van het weergaveproces zijn er logische softwarecomponenten die op gestructureerde manieren met elkaar communiceren.

Render de hoofdthreadcomponenten van het proces

Diagram van de Blink-renderer.

In de Blink-renderer:

  • Het lokale frameboomfragment vertegenwoordigt de boom van lokale frames en de DOM binnen frames.
  • De component DOM en Canvas API's bevat implementaties van al deze API's.
  • De documentlevenscyclusloper voert de renderingpijplijnstappen uit tot en met de commit-stap.
  • De component voor het testen en verzenden van invoergebeurtenissen voert hittests uit om erachter te komen welk DOM-element het doelwit is van een gebeurtenis, en voert de algoritmen voor het verzenden van invoergebeurtenissen en het standaardgedrag uit.

De planner en runner van de renderende gebeurtenislus beslissen wat er op de gebeurtenislus moet worden uitgevoerd en wanneer. Het plant de weergave in een ritme dat overeenkomt met de weergave van het apparaat.

Een diagram van de frameboom.

Lokale frameboomfragmenten zijn een beetje ingewikkeld. Bedenk dat een frameboom de hoofdpagina en de onderliggende iframes is, recursief. Een frame is lokaal voor een weergaveproces als het in dat proces wordt weergegeven, en anders bevindt het zich op afstand.

U kunt zich voorstellen dat u frames kleurt op basis van hun weergaveproces. In de voorgaande afbeelding zijn de groene cirkels allemaal frames in één renderproces; de oranje zijn in een seconde, en de blauwe in een derde.

Een lokaal frameboomfragment is een verbonden component van dezelfde kleur in een frameboom. Er zijn vier lokale framebomen in de afbeelding: twee voor site A, één voor site B en één voor site C. Elke lokale frameboom krijgt zijn eigen Blink-renderercomponent. De Blink-renderer van een lokale frameboom bevindt zich mogelijk niet in hetzelfde renderproces als andere lokale framebomen. Het wordt bepaald door de manier waarop weergaveprocessen worden geselecteerd, zoals eerder beschreven.

Geef de threadstructuur van de procescompositor weer

Een diagram dat de componenten van de renderprocescompositor toont.

De componenten van de renderprocescompositor omvatten:

  • Een gegevenshandler die een samengestelde lagenlijst, weergavelijsten en eigenschappenbomen bijhoudt.
  • Een levenscyclusloper die de animatie-, scroll-, composiet-, raster- en decodeer- en activeringsstappen van de renderingpijplijn uitvoert. (Houd er rekening mee dat animeren en scrollen zowel in de hoofdthread als in de compositor kunnen voorkomen.)
  • Een invoer- en hittesthandler voert invoerverwerking en hittests uit met de resolutie van samengestelde lagen, om te bepalen of scrollbewegingen kunnen worden uitgevoerd op de compositorthread, en op welke renderproceshittests zich moeten richten.

Voorbeeldarchitectuur in de praktijk

In dit voorbeeld zijn er drie tabbladen:

Tabblad 1: foo.com

<html>
  <iframe id=one src="foo.com/other-url"></iframe>
  <iframe  id=two src="bar.com"></iframe>
</html>

Tabblad 2: bar.com

<html>
 …
</html>

Tab 3: baz.com html <html> … </html>

De proces-, thread- en componentenstructuur voor deze tabbladen zien er als volgt uit:

Diagram van het proces voor de tabbladen.

Laten we elk van de vier hoofdtaken van weergave met één voorbeeld bekijken. Een herinnering:

  1. Render inhoud in pixels op het scherm.
  2. Animeer visuele effecten op de inhoud van de ene staat naar de andere.
  3. Scroll als reactie op invoer.
  4. Leid invoer efficiënt naar de juiste plaatsen, zodat ontwikkelaarsscripts en andere subsystemen kunnen reageren.

Om de gewijzigde DOM voor tabblad één weer te geven :

  1. Een ontwikkelaarsscript verandert de DOM in het weergaveproces voor foo.com.
  2. De Blink-renderer vertelt de compositor dat er een render nodig is.
  3. De compositor vertelt Viz dat er een render nodig is.
  4. Viz signaleert het begin van de render terug naar de compositor.
  5. De compositor stuurt het startsignaal door naar de Blink-renderer.
  6. De hoofdthreadgebeurtenislooprunner voert de levenscyclus van het document uit.
  7. De hoofdthread verzendt het resultaat naar de compositorthread.
  8. De compositor-gebeurtenislooprunner voert de compositing-levenscyclus uit.
  9. Alle rastertaken worden voor raster naar Viz verzonden (er zijn vaak meer dan één van deze taken).
  10. Viz rastert inhoud op de GPU.
  11. Viz bevestigt voltooiing van de rastertaak. Opmerking: Chromium wacht vaak niet tot het raster is voltooid, maar gebruikt in plaats daarvan iets dat een synchronisatietoken wordt genoemd en dat moet worden opgelost door rastertaken voordat stap 15 wordt uitgevoerd.
  12. Er wordt een compositorframe naar Viz gestuurd.
  13. Viz verzamelt de compositorframes voor het foo.com-weergaveproces, het bar.com iframe-weergaveproces en de browsergebruikersinterface.
  14. Viz plant een gelijkspel.
  15. Viz tekent het geaggregeerde compositorframe naar het scherm.

Een CSS-transformatieovergang op tabblad twee animeren :

  1. De compositor-thread voor het bar.com-weergaveproces tikt een animatie aan in de compositor-gebeurtenislus door de bestaande eigenschappenbomen te muteren. Hierdoor wordt de levenscyclus van de compositor opnieuw uitgevoerd. (Raster- en decodeertaken kunnen voorkomen, maar worden hier niet weergegeven.)
  2. Er wordt een compositorframe naar Viz gestuurd.
  3. Viz verzamelt de compositorframes voor het foo.com-weergaveproces, het bar.com-weergaveproces en de browsergebruikersinterface.
  4. Viz plant een gelijkspel.
  5. Viz tekent het geaggregeerde compositorframe naar het scherm.

Om door de webpagina te scrollen op tabblad drie:

  1. Een reeks input (muis, aanraking of toetsenbord) komt naar het browserproces.
  2. Elke gebeurtenis wordt doorgestuurd naar de renderprocescompositorthread van baz.com.
  3. De compositor bepaalt of de hoofdthread op de hoogte moet zijn van de gebeurtenis.
  4. De gebeurtenis wordt indien nodig naar de hoofdthread verzonden.
  5. De hoofdthread vuurt luisteraars voor input ​​( pointerdown , touchstar , pointermove , touchmove of wheel ) om te zien of luisteraars preventDefault voor de gebeurtenis zullen aanroepen.
  6. De hoofdthread retourneert of preventDefault naar de compositor is aangeroepen.
  7. Als dit niet het geval is, wordt de invoergebeurtenis teruggestuurd naar het browserproces.
  8. Het browserproces converteert het naar een scrollgebaar door het te combineren met andere recente gebeurtenissen.
  9. Het scrollgebaar wordt nogmaals naar de renderprocescompositorthread van baz.com gestuurd,
  10. De scroll wordt daar toegepast en de compositor-thread voor het weergaveproces van bar.com tikt een animatie aan in de compositor-gebeurtenislus. Hierdoor wordt de scroll-offset in de eigenschappenbomen gemuteerd en wordt de levenscyclus van de compositor opnieuw uitgevoerd. Het vertelt de hoofdthread ook om een scroll af te vuren (hier niet afgebeeld).
  11. Er wordt een compositorframe naar Viz gestuurd.
  12. Viz verzamelt de compositorframes voor het foo.com-weergaveproces, het bar.com-weergaveproces en de browsergebruikersinterface.
  13. Viz plant een gelijkspel.
  14. Viz tekent het geaggregeerde compositorframe naar het scherm.

Een click routeren naar een hyperlink in iframe #twee op tabblad één:

  1. Er komt een input (muis, aanraking of toetsenbord) naar het browserproces. Het voert een geschatte hittest uit om te bepalen of het iframe-weergaveproces van bar.com de klik moet ontvangen en deze daarheen stuurt.
  2. De compositor-thread voor bar.com stuurt de click naar de hoofdthread voor bar.com en plant een rendering-gebeurtenislustaak om deze te verwerken.
  3. De invoergebeurtenisprocessor voor de hoofdthreadhittests van bar.com om te bepalen op welk DOM-element in het iframe is geklikt, en vuurt een click af zodat scripts deze kunnen observeren. Als u geen preventDefault hoort, navigeert het naar de hyperlink.
  4. Bij het laden van de bestemmingspagina van de hyperlink wordt de nieuwe status weergegeven, met stappen die vergelijkbaar zijn met het vorige voorbeeld "geef gewijzigde DOM weer". (Deze daaropvolgende wijzigingen worden hier niet weergegeven.)

Afhaal

Het kan veel tijd kosten om te onthouden en te internaliseren hoe weergave werkt.

De belangrijkste conclusie is dat de renderingpijplijn, door middel van zorgvuldige modularisering en aandacht voor detail, is opgesplitst in een aantal op zichzelf staande componenten. Deze componenten zijn vervolgens verdeeld over parallelle processen en threads om de schaalbare prestaties en uitbreidbaarheidsmogelijkheden te maximaliseren.

Elke component speelt een cruciale rol bij het mogelijk maken van de prestaties en functies van moderne webapps.

Blijf lezen over de belangrijkste datastructuren , die net zo belangrijk zijn voor RenderingNG als codecomponenten.


Illustraties door Una Kravets.