pub struct WebGl2Backend { /* private fields */ }Expand description
The complete WebGL 2 backend: a context plus the three shadows that make a frame cheap.
§Performance
Three things are shadowed rather than re-queried. The fixed-function
state is shadowed by a GlRenderState copy, so applying a state
costs only the calls for fields that actually changed. The current
texture unit is shadowed by a u32, so binding a texture to the unit
that is already active costs no activeTexture at all. The current
program is shadowed by a JsValue compared with Object.is, so
rebinding the same program costs no useProgram. None of the three
can be answered by asking the driver without a synchronous
round trip, which is exactly why they are tracked here instead.
Implementations§
Source§impl WebGl2Backend
Implements construction and lifecycle for the WebGL 2 backend.
impl WebGl2Backend
Implements construction and lifecycle for the WebGL 2 backend.
Sourcepub fn init(config: &RenderConfig) -> Result<WebGl2Backend, WebGl2InitError>
pub fn init(config: &RenderConfig) -> Result<WebGl2Backend, WebGl2InitError>
Builds a backend from a render configuration, resolving the
canvas, scaling the backing store by the device pixel ratio, and
acquiring the webgl2 context.
The shadow state is seeded with the values a freshly created GL
context actually starts in rather than with all-zero
placeholders, so the first WebGl2Backend::apply_state emits
only the calls that genuinely differ from what the driver already
set up.
§Arguments
&RenderConfig- The rendering configuration.
§Returns
Result<WebGl2Backend, WebGl2InitError>- The backend, or a typed error describing the specific failure.
Sourcepub fn from_init(init: WebGl2BackendInit) -> Self
pub fn from_init(init: WebGl2BackendInit) -> Self
Builds a backend around an already-acquired context.
init is the only other constructor and it needs a live
document: it queries the canvas by selector and asks for a
webgl2 context. This one takes the handles directly, so a caller
that already has a context (a test, or a canvas re-created after a
context loss) does not have to go through DOM lookup. The shadow
state is seeded with the values a freshly created GL context
already holds, so the first apply_state emits only what
genuinely differs.
§Arguments
WebGl2BackendInit- The canvas, context, and viewport.
§Returns
WebGl2Backend- The backend, with an empty readback buffer.
Sourcepub fn is_available() -> bool
pub fn is_available() -> bool
Reports whether the browser can create a WebGL 2 context at all.
Creates a throwaway off-DOM canvas and requests a webgl2
context. No shaders are compiled and nothing on the page is
touched, so this is cheap enough to call as a capability probe
before deciding which backend to construct.
§Returns
bool-truewhen awebgl2context could be acquired.
Sourcepub fn resize(&mut self, width: u32, height: u32)
pub fn resize(&mut self, width: u32, height: u32)
Resizes the GL viewport to the given backing-store size and records it in the shadow state.
The call is issued to the driver, not merely folded into the
shadow. The shadow is the engine’s record of what the driver already
holds, so writing a value there without the matching gl.viewport
makes the two disagree: WebGl2Backend::apply_state would diff
against a viewport the driver never received and skip the call
forever. That is not hypothetical — a caller that sets
canvas.width and then calls resize (every demo loop does) left
the driver viewport frozen at its init size while the drawing buffer
grew, so the frame rendered into a sub-rect of the surface and the
browser stretched that corner across the whole element.
Because the viewport is a per-frame-sized property and gl.viewport
is a single cheap call, issuing it here costs nothing measurable and
removes the “who re-asserts the viewport” question from every caller.
§Arguments
u32- The new physical pixel width, already multiplied by the device pixel ratio.u32- The new physical pixel height.
Sourcepub fn is_context_lost(&self) -> bool
pub fn is_context_lost(&self) -> bool
Resizes the canvas backing store and re-asserts the GL viewport Reports whether the context has been lost, which happens when the browser reclaims the GPU: a tab backgrounded for long enough, a driver reset, a device change.
Worth checking once per frame. Every subsequent GL call against a lost context is a no-op that reports no error, so a renderer that does not check silently draws nothing with a clean console.
§Returns
bool-truewhen the context is lost and must be rebuilt.
Source§impl WebGl2Backend
Implements the state-diffing apply and the frame-level entry points.
impl WebGl2Backend
Implements the state-diffing apply and the frame-level entry points.
Every method here exists to make the per-frame cost proportional to what actually changed rather than to what was asked for.
Sourcepub fn apply_state(&mut self, state: &GlRenderState)
pub fn apply_state(&mut self, state: &GlRenderState)
Applies a whole GlRenderState, issuing a GL call only for
each field that differs from the currently-bound state.
The diff is against a shadow copy of the last applied state, not
against the driver, so no getParameter round trip is needed: the
driver is known to agree with the shadow because the shadow is
only ever updated after the calls that establish it. This is the
single largest saving in the whole backend, since a batch of a
thousand draws sharing one state pays for it once instead of for a
thousand enable / depthFunc / blendFunc / cullFace /
colorMask sequences, each of which the driver re-validates.
§Arguments
&GlRenderState- The state to make current.
Sourcepub fn invalidate_state(&mut self, width: u32, height: u32)
pub fn invalidate_state(&mut self, width: u32, height: u32)
Resets the shadow to the context’s own initial state, which forces the next apply to re-assert everything.
Needed after anything that can change GL state behind the backend’s back: a context restore, a debug extension, or a driver’s own reset. Without it the shadow would claim a state the driver no longer holds and every diffed call would be skipped.
§Arguments
u32- The viewport width to seed the shadow with.u32- The viewport height to seed the shadow with.
Sourcepub fn clear_depth(&self, context: &WebGl2RenderingContext)
pub fn clear_depth(&self, context: &WebGl2RenderingContext)
Clears only the depth buffer, leaving color untouched.
Split out because a second pass that needs a fresh depth range without disturbing the color underneath is common in deferred and post-process work, and a full clear here would throw away results the pass is about to read.
§Arguments
&WebGl2RenderingContext- The context to clear through.
Sourcepub fn render_frame(
&mut self,
context: &WebGl2RenderingContext,
program: &GlProgram,
color: Color,
vertex_count: u32,
)
pub fn render_frame( &mut self, context: &WebGl2RenderingContext, program: &GlProgram, color: Color, vertex_count: u32, )
Renders a complete frame: clears to color, binds program, and
issues one triangle-list draw.
The one-call frame shape a demo loop needs, built from the same
primitives the multi-pass API exposes. Geometry is generated
inside the vertex shader from gl_VertexID, so there are no
vertex buffers to bind; vertex_count is the number of vertices
the shader is expected to emit.
§Arguments
&WebGl2RenderingContext- The context to draw through.&GlProgram- The program to draw with.Color- The color to clear to before drawing.u32- The number of vertices to draw.
Source§impl WebGl2Backend
Implements every draw entry point, plus binding helpers and readback.
impl WebGl2Backend
Implements every draw entry point, plus binding helpers and readback.
Sourcepub fn bind_texture_unit(
&mut self,
context: &WebGl2RenderingContext,
unit: u32,
texture: &GlTexture,
)
pub fn bind_texture_unit( &mut self, context: &WebGl2RenderingContext, unit: u32, texture: &GlTexture, )
Binds a texture to a unit, issuing activeTexture only when the
unit is not already current.
§Arguments
&WebGl2RenderingContext- The context to bind against.u32- The texture unit index, as passed to [gl_texture_unit].&GlTexture- The texture to bind to that unit.
Sourcepub fn draw_elements(
&self,
context: &WebGl2RenderingContext,
topology: PrimitiveTopology,
args: &DrawIndexedArgs,
index_format: IndexFormat,
) -> bool
pub fn draw_elements( &self, context: &WebGl2RenderingContext, topology: PrimitiveTopology, args: &DrawIndexedArgs, index_format: IndexFormat, ) -> bool
Draws args.index_count indices from the bound index buffer.
WebGL 2 has no baseVertex, the way WebGPU’s
DrawIndexedArgs does: the element offset reaches the vertex
fetch through the attribute pointers recorded in the vertex array
object instead. base_vertex is therefore only meaningful as an
assertion here, and a non-zero value is reported as false rather
than silently ignored, because silently ignoring it would draw
the wrong mesh with no diagnostic at all.
§Arguments
&WebGl2RenderingContext- The context to draw through.PrimitiveTopology- How the vertices are assembled.&DrawIndexedArgs- The counts and offsets to draw.IndexFormat- The element width of the bound index buffer.
§Returns
bool-truewhen the draw was issued,falsewhenbase_vertexis non-zero and therefore cannot be honored.
Sourcepub fn draw_element_range(
&self,
context: &WebGl2RenderingContext,
topology: PrimitiveTopology,
start: u32,
end: u32,
index_format: IndexFormat,
) -> bool
pub fn draw_element_range( &self, context: &WebGl2RenderingContext, topology: PrimitiveTopology, start: u32, end: u32, index_format: IndexFormat, ) -> bool
Draws a contiguous index range without disturbing the rest of the index buffer.
§Arguments
&WebGl2RenderingContext- The context to draw through.PrimitiveTopology- How the vertices are assembled.u32- The first index to read.u32- The last index to read, inclusive.IndexFormat- The element width of the bound index buffer.
§Returns
bool-truewhen the range was non-empty and was drawn.
Sourcepub fn draw_arrays_instanced(
&self,
context: &WebGl2RenderingContext,
topology: PrimitiveTopology,
args: &DrawArgs,
)
pub fn draw_arrays_instanced( &self, context: &WebGl2RenderingContext, topology: PrimitiveTopology, args: &DrawArgs, )
Draws an instanced vertex range, for geometry generated entirely on the GPU.
§Arguments
&WebGl2RenderingContext- The context to draw through.PrimitiveTopology- How the vertices are assembled.&DrawArgs- The counts and offsets to draw.
Sourcepub fn draw_elements_instanced(
&self,
context: &WebGl2RenderingContext,
topology: PrimitiveTopology,
args: &DrawIndexedArgs,
index_format: IndexFormat,
) -> bool
pub fn draw_elements_instanced( &self, context: &WebGl2RenderingContext, topology: PrimitiveTopology, args: &DrawIndexedArgs, index_format: IndexFormat, ) -> bool
Draws an instanced index range, the form a mesh rendered many times per frame takes.
§Arguments
&WebGl2RenderingContext- The context to draw through.PrimitiveTopology- How the vertices are assembled.&DrawIndexedArgs- The counts and offsets to draw.IndexFormat- The element width of the bound index buffer.
§Returns
bool-truewhen the draw was issued,falsewhenbase_vertexis non-zero and therefore cannot be honored.
Sourcepub fn read_pixels(
&mut self,
context: &WebGl2RenderingContext,
x: i32,
y: i32,
width: u32,
height: u32,
) -> Vec<u8> ⓘ
pub fn read_pixels( &mut self, context: &WebGl2RenderingContext, x: i32, y: i32, width: u32, height: u32, ) -> Vec<u8> ⓘ
Reads a rectangle of the bound framebuffer back into Rust memory.
The destination buffer is owned by the backend and reused across
calls, so a steady-state readback, a picking query or a screenshot
say, allocates nothing after the first time it runs at that size.
The result is returned as an owned Vec: a caller that needs the
bytes past the next readback keeps its own copy, which is one
explicit clone rather than a borrow held across a driver call.
Reading from the default framebuffer is legal but slow on most
drivers, because the contents may already have been discarded by
the compositor; reading from a bound
GlFramebuffer is the supported path.
§Arguments
&WebGl2RenderingContext- The context to read through.i32- The left edge in pixels.i32- The bottom edge in pixels.u32- The width in pixels.u32- The height in pixels.
§Returns
Vec<u8>- The tightly packed RGBA bytes, row-major from the bottom edge, or empty when the rectangle has no area.