Canvas
Servo tukee neljää canvas-kontekstityyppiä:
CanvasRenderingContext2D(2d-konteksti)WebGLRenderingContext(webgl-konteksti)WebGL2RenderingContext(webgl2-konteksti)GPUCanvasContext(webgpu-konteksti)
Jokainen canvas-konteksti toteuttaa CanvasContext-traitin, joka vaatii kontekstien toteuttavan joitakin yhteisiä ominaisuuksia yhtenäisellä tavalla:
context_idresize: tämä metodi tyhjentää maalarin kuvan asettamalla sen läpinäkyväksi alfaksi (kaikki tavut nolliksi)get_image_data: käytetään canvas-kuvan hankkimiseen, yleensä kutsumallatoDataUrl,toBlob,createImageBitmapcanvas-elementillä tai epäsuorasti piirtämällä yhden canvasin toiseenupdate_the_rendering: renderöinnin päivityksen käynnistämiseen (yleensä vaihtamalla screen-buffer ja back-buffer)canvas: hanki kytketty canvas-elementti (tämä voi ollaHTMLCanvasElementtaiOffscreenCanvas, joka voidaan myös kytkeäHTMLCanvasElement-elementtiin kontekstillaplaceholder) samalla tarjoten hyviä oletustoteutuksia (onscreen,origin_is_clean,size,mark_as_dirty).mark_as_dirtykutsutaan funktioista, jotka vaikuttavat maalarin kuvaan, ja se kertoo layoutille renderöimään canvas-elementin uudelleen (merkitsemälläHTMLCanvasElementlikaiseksi solmuksi).
HTML-tapahtumasilmukka ja renderöinti
flowchart TB
subgraph Sisältöprosessi
subgraph Script-kulku
JS-->utr[Päivitä renderöinti]-->Layout
end
end
subgraph Pääprosessi
subgraph Maalarit
WGPU[WGPU-säie]
WEBGL[WebGL-säie]
CPT[Canvas Paint Thread]
end
%% actual update in painters
Painters--CreateImage-->Compositor
Painters--UpdateImage-->Compositor
Compositor-->WR
WR[WebRender]--lock,unlock-->Painters
end
%% init canvas
JS--create context-->Painters--ImageKey, CanvasId-->JS
%% update canvas rendering
utr<--Update rendering-->Painters
%% rendering
Layout--DisplayList (contains ImageKey)-->Compositor
HTML-tapahtumasilmukan osana script-säie suorittaa tehtävän (jäsentäminen, skriptin evaluointi, callbackit, tapahtumat, …) ja sen jälkeen se suorittaa microtask-checkpointin, joka tyhjentää microtask-jonon.
Window event loopissa jonotamme globaalin tehtävän renderöinnin päivittämiseen, jos on rendering opportunity (yleensä compositorin ohjaama laitteistopäivitystaajuuden perusteella).
Servossa emme itse asiassa jonota tehtävää, vaan ajamme update the rendering -algoritmin ScriptThreadin IPC-viestien jälkeen ja sitten suoritamme myös microtask-checkpointin, kuten tapahtumasilmukka olisi tehnyt tehtävän valmistuttua.
Update the rendering suorittaa erilaisia resize-, scroll- ja animaatiovaiheita (mukaan lukien microtask-checkpointin avoimien promisejen ratkaisemiseksi) ja sitten ajaa animation frame -callbackit (callbackit, jotka on lisätty requestAnimationFrame -kutsulla).
Tässä vaiheessa piirtokomennot lähetetään maalareille uuden animaatiokehyksen luomiseksi.
Lopuksi käynnistämme reflow’n (layout), joka ensin päivittää canvasien renderöinnin (fluskaamalla likaiset canvasit) ja animoidut kuvat, sitten käy DOM:n ja sen tyylit läpi, rakentaa DisplayList:in ja lähettää sen WebRenderille renderöintiä varten.
Kun canvas-kontekstin luontia pyydetään (canvas.getContext('2d')), script-säie blokkaa maalarisäieellä sen alustuessa ja luodessa uuden WebRender-kuvan (CreateImage), ja lopulta lähettää siihen liittyvän ImageKey:n takaisin scriptille.
sequenceDiagram
Script->>Constellation: Create Context
Constellation->>Painter: Create Context
Painter->>Compositor: GenerateImageKey
Compositor->>WebRender: GenerateImageKey
opt
Painter<<->>Compositor: ExternalImageId
end
WebRender->>Compositor: ImageKey
Compositor->>Painter: ImageKey
Painter->>Compositor: CreateImage
Compositor->>WebRender: CreateImage
Painter->>Script: PainterIPCSender, ImageKey, CanvasId
Jokainen canvas-konteksti toteuttaa LayoutCanvasRenderingContextHelpers, joka palauttaa ImageKey:n, jota layout käyttää DisplayList:issään, tai None, jos canvas on tyhjennetty tai muuten ei maalattavissa koon vuoksi.
WebRender lukee tuloksena olevan kuvadatan renderöinnin yhteydessä annetun ImageKey:n perusteella.
WebGL- ja WebGPU-maalareissa tämä tehdään toteuttamalla mukautettu WebrenderExternalImageApi; se tarjoaa lock- ja unlock-metodit WebRenderille varsinaisen kuvadatan hankkimiseksi.
2D-canvaksissa kuvadata toimitetaan suoraan CreateImage- ja UpdateImage-IPC-viestien kautta.
sequenceDiagram
Script->>Painter:Update rendering (flush)
Painter->>Compositor:UpdateImage
Compositor->>WebRender: UpdateImage
opt
Painter->>Script: Done
end
Note over Script: Layout
Script->>Compositor: DisplayList
Compositor->>WebRender: DisplayList
opt
Compositor<<->>WebRender: Query ExternalImage Registery
WebRender->>+Painter: lock ExternalImage
WebRender->>Painter: unlock ExternalImage
deactivate Painter
end
2D-canvas-konteksti
flowchart LR
CanvasRenderingContext2d --- HTMLCanvasContext
subgraph OffscreenCanvasRenderingContext2D
subgraph CanvasRenderingContext2d
CS'[CanvasState]
end
end
OffscreenCanvasRenderingContext2D --- OffscreenCanvas
PaintRenderingContext2D --- PaintWorklet
subgraph PaintRenderingContext2D
CS''[CanvasState]
end
Useimmat canvasit käyttävät samaa DOM-tyyppiä onscreen- ja offscreen-konteksteilleen, mutta 2D-canvaksissa näin ei ole niiden pitkän historian vuoksi. Web-standardit määrittelevät kolme 2D-canvas-kontekstityyppiä:
CanvasRenderingContext2D(kytkettyHTMLCanvasContext-elementtiin)OffscreenCanvasRenderingContext2D(kytkettyOffscreenCanvas-elementtiin)PaintRenderingContext2D(saatavilla vainPaintWorklet:issä)
CanvasRenderingContext2D ja PaintRenderingContext2D on toteutettu wrapper:eina CanvasState:n ympärille, kun taas OffscreenCanvasRenderingContext2D on toteutettu wrapper:ina CanvasRenderingContext2D:n ympärille samankaltaisen logiikan vuoksi duplikaation välttämiseksi.
flowchart LR
HTMLCanvasElement --getContext('2d')--> CanvasRenderingContext2d
CanvasRenderingContext2d --strokeRect--> CanvasState
CanvasState --IPC
strokeRect--> CanvasPaintThread
CanvasPaintThread --Done--> CanvasState
CanvasState toteuttaa varsinaisen 2D-piirron logiikan asettamalla sopivan tilan ja lähettämällä IPC-viestejä Canvas Paint Threadille.
Jotkin komennot muuttavat vain sisäistä tilaa, mutta eivät lähetä viestejä ennen kuin on varsinainen piirtokomento.
Kaikki “likaiset” 2D-canvasit tallennetaan Document:iin ja flusataan reflow’n aikana lähettämällä IPC-viestejä, jotka käynnistävät update_the_rendering-metodin jokaiselle canvasille.
Kun piirretään yksi 2D-canvas toiseen 2D-canvasiin, lähetämme DrawImageInOther-viestin, erityisen IPC-viestin, joka välttää bitmapin kopioimisen canvas paint thread -säieeltä ulos.
WebGL-canvas-konteksti
flowchart LR
WebGLRenderingContext --- c["HTMLCanvasElement
OffscreenCanvas"]
subgraph WebGL2RenderingContext
WebGLRenderingContext
end
WebGL(2)-canvas-kontekstit ovat WebGLRenderingContext tai WebGL2RenderingContext, ja Servossa WebGL2RenderingContext wrap:aa ja laajentaa WebGLRenderingContext:ia.
Nämä kontekstit tallentavat tilaa ja lähettävät IPC-viestejä WebGL-säieelle, joka suorittaa varsinaiset OpenGL- (tai OpenGL ES) -komennot ja palauttaa tulokset IPC:n kautta.
Script-säie blokkaa WebGL-säieellä odottaen jokaisen operaation valmistumista.
Kaikki “likaiset” WebGL-canvasit tallennetaan Document:iin ja flusataan reflow’n osana lähettämällä yksi IPC-viesti, joka sisältää kaikki likaiset kontekstitunnukset, ja sitten blokkaamalla WebGL-säieellä, kunnes kaikki canvasit on flusattu.
Fluskaus vaihtaa framebufferin, joista toinen on esitystä varten (WebRender lukee sen) ja toista käytetään piirtämiseen GL-komentojen kohteena.
WebGPU-canvas-konteksti
WebGPU-esitys on erityisin, koska se on täysin asynkroninen (ei-blokkaava). Lisätietoa siitä, miten asynkronisuus toteutetaan WebGPU:ssa, löytyy WebGPU-luvusta.
sequenceDiagram
loop Context Creation
Script->>WGPU: CreteContext
WGPU->>WebRender: CreateImage
WebRender->>WGPU: ImageKey
WGPU->>Script: ImageKey
end
alt animation Callback
Note over Script: getCurrentTexture
Script-)WGPU:CreateTexture
activate Script
Note over Script: draw operations into current texture
else update the rendering
Note over Script: expire current texture
Script-)WGPU:SwapchainPresent
WGPU-)WGPU: Copy texture to one of staging buffer
WGPU-)+WGPU poller: Map stagging buffer to CPU as GPUPresentationBuffer
opt presentationId is newer than existing
WGPU poller -)-WebRender: UpdateImage
end
WGPU poller -)WGPU poller: Unmap GPUPresentationBuffer
Script-)WGPU:DestroyTexture
deactivate Script
end
loop rendering
WebRender<<->>+WGPU: lock ExternalImage and read GPUPresentationBuffer
WebRender->>WGPU: unlock ExternalImage
deactivate WGPU
end
Kaikilla onscreen WebGPU-konteksteilla update_the_rendering suoritetaan osana renderöinnin päivitystä HTML-tapahtumasilmukassa.
Tämä vanhentaa (tuhoaa) current texture -objektin, mutta ennen sitä lähetämme SwapChainPresent-pyynnön, joka kopioi tekstuuridatan yhteen 10 esityspuskurista GPU:lla.
Kopioinnin valmistuttua map:ataan uusi puskuri asynkronisesti CPU:lle.
Koska prosessi on asynkroninen, merkitsemme jokaisen esityspuskurin kasvavalla u64-tunnuksella, ja korvaamme aktiivisen esityspuskurin vain, jos puskurimme tunnus on uudempi.
Epäaktiivinen esityspuskuri unmap:ataan.
flowchart TD
S[Staging Presentation Buffer] --copy_texture_to_buffer, mapAsync-->
Mapping --mapAsync done-->
UpdateWR --yes-->
Mapped[Mapped, Unmapped old]
UpdateWR--else unmap-->S
Tämä on mallinnettu myös TLA+:ssa: https://gist.github.com/gterzian/aa5d96a89db280017b04917eee67f6ac
Sekä WebRenderin lock että get_image_data käyttävät aktiivisen esityspuskurin sisältöä.
Resurssit
- https://medium.com/@polyglot_factotum/fixing-servos-event-loop-490c0fd74f8d
- Update the rendering of canvas (#35733)
- webgpu: renovate gpucanvascontext and webgpu presentation to match the spec (#33521)
- webgpu: Fix HTML event loop integration (#34631)
- webgpu: Introduce PresentationId to ensure updates with newer presentation (#33613)
- webgpu: Make uploading data to wr with less copies (#33368)