euv_engine/renderer/enum.rs
1use super::*;
2
3/// Defines how new pixels are composited with existing pixels on the canvas.
4///
5/// Maps directly to the CSS `globalCompositeOperation` property.
6#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
7pub enum BlendMode {
8 /// The source is drawn over the destination (default alpha blending).
9 #[default]
10 Normal,
11 /// The source color is multiplied with the destination, producing a darker result.
12 Multiply,
13 /// The source and destination are inverted, multiplied, then inverted again.
14 Screen,
15 /// The source and destination colors are added together, clamped to maximum brightness.
16 Lighter,
17 /// Combines `Multiply` and `Screen` based on the destination color.
18 Overlay,
19 /// Keeps the darker of the source and destination per channel.
20 Darken,
21 /// Keeps the lighter of the source and destination per channel.
22 Lighten,
23 /// Dodges the destination color brightening it based on the source.
24 ColorDodge,
25 /// Burns the destination color darkening it based on the source.
26 ColorBurn,
27 /// A harsher version of `Overlay` using the source color as the filter.
28 HardLight,
29 /// A softer version of `Overlay` using the source color as the filter.
30 SoftLight,
31 /// Subtracts the darker color from the lighter color per channel.
32 Difference,
33 /// Similar to `Difference` but with lower contrast.
34 Exclusion,
35 /// Uses the hue of the source with the saturation and luminosity of the destination.
36 Hue,
37 /// Uses the saturation of the source with the hue and luminosity of the destination.
38 Saturation,
39 /// Uses the hue and saturation of the source with the luminosity of the destination.
40 Color,
41 /// Uses the luminosity of the source with the hue and saturation of the destination.
42 Luminosity,
43}
44
45/// A single deferred draw operation recorded into a `DrawList`.
46///
47/// Commands carry the resolved style for the operation (fill/stroke color, line
48/// width) so that replay can group consecutive same-style shapes into a single
49/// path and skip redundant canvas state changes. Colors are stored as `Color`
50/// and converted to CSS strings only at replay time.
51#[derive(Clone, Debug, PartialEq)]
52pub enum DrawCommand {
53 /// Fills a rectangle. Carries the fill color.
54 FillRect {
55 /// The top-left position in world space.
56 position: Vector2D,
57 /// The width in pixels.
58 width: f64,
59 /// The height in pixels.
60 height: f64,
61 /// The fill color.
62 color: Color,
63 },
64 /// Strokes the outline of a rectangle. Carries stroke color and line width.
65 StrokeRect {
66 /// The top-left position in world space.
67 position: Vector2D,
68 /// The width in pixels.
69 width: f64,
70 /// The height in pixels.
71 height: f64,
72 /// The stroke color.
73 color: Color,
74 /// The stroke line width in pixels.
75 line_width: f64,
76 },
77 /// Fills a circle. Carries the fill color.
78 FillCircle {
79 /// The center in world space.
80 center: Vector2D,
81 /// The radius in pixels.
82 radius: f64,
83 /// The fill color.
84 color: Color,
85 },
86 /// Strokes the outline of a circle. Carries stroke color and line width.
87 StrokeCircle {
88 /// The center in world space.
89 center: Vector2D,
90 /// The radius in pixels.
91 radius: f64,
92 /// The stroke color.
93 color: Color,
94 /// The stroke line width in pixels.
95 line_width: f64,
96 },
97 /// Draws a line segment. Carries stroke color and line width.
98 Line {
99 /// The start point in world space.
100 start: Vector2D,
101 /// The end point in world space.
102 end: Vector2D,
103 /// The stroke color.
104 color: Color,
105 /// The stroke line width in pixels.
106 line_width: f64,
107 },
108 /// Fills text at a position. Carries the fill color and font.
109 FillText {
110 /// The text to draw.
111 text: String,
112 /// The position in world space.
113 position: Vector2D,
114 /// The fill color.
115 color: Color,
116 /// The CSS font string.
117 font: String,
118 },
119 /// Draws a transformed sprite sub-region (image, source rect, TRS transform).
120 DrawSprite {
121 /// The image to draw.
122 image: HtmlImageElement,
123 /// The source rectangle within the image.
124 source: Rect,
125 /// The world-space transform (position, rotation, scale). Scale signs flip.
126 transform: Transform2D,
127 },
128 /// Draws an image sub-region at a destination rect (no rotation).
129 DrawImageRect {
130 /// The image to draw.
131 image: HtmlImageElement,
132 /// The source rectangle within the image.
133 source: Rect,
134 /// The destination top-left position in world space.
135 dest_position: Vector2D,
136 /// The destination width in pixels.
137 dest_width: f64,
138 /// The destination height in pixels.
139 dest_height: f64,
140 },
141 /// Applies a global alpha to all subsequent commands until changed.
142 SetGlobalAlpha {
143 /// The alpha value in the range 0.0 to 1.0.
144 alpha: f64,
145 },
146 /// Applies a blend mode to all subsequent commands until changed.
147 SetBlendMode {
148 /// The blend mode to apply.
149 mode: BlendMode,
150 },
151 /// Draws a texture as a nine-slice, keeping the corners fixed while
152 /// the edges and centre stretch to the destination rectangle.
153 DrawNineSlice {
154 /// The source texture to slice.
155 image: web_sys::HtmlImageElement,
156 /// The border insets splitting the texture into nine regions.
157 insets: NineSliceInsets,
158 /// The top-left position in world space.
159 dest_position: Vector2D,
160 /// The destination width in pixels.
161 dest_width: f64,
162 /// The destination height in pixels.
163 dest_height: f64,
164 },
165 /// Draws one named region of a sprite atlas texture.
166 DrawAtlasRegion {
167 /// The atlas texture holding the region.
168 image: web_sys::HtmlImageElement,
169 /// The region within the atlas, in source pixels.
170 source: Rect,
171 /// The top-left position in world space.
172 dest_position: Vector2D,
173 /// The destination width in pixels.
174 dest_width: f64,
175 /// The destination height in pixels.
176 dest_height: f64,
177 },
178}
179
180/// Rendering quality preset controlling anti-aliasing smoothing strategy.
181///
182/// Maps to the canvas `imageSmoothingQuality` value plus an explicit
183/// `imageSmoothingEnabled` toggle. Combined with a CSS `image-rendering:
184/// pixelated` rule on the consumer side, `Low` produces crisp pixel-art
185/// rendering while `High` produces smooth vector-style rendering.
186#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
187pub enum RenderQuality {
188 /// Fastest rendering, pixelated scaling.
189 ///
190 /// Disables `imageSmoothingEnabled` on the canvas context and sets
191 /// `imageSmoothingQuality = "low"`. Pair with CSS `image-rendering:
192 /// pixelated` for sharp nearest-neighbour scaling.
193 Low,
194 /// Balanced rendering with default smoothing quality.
195 ///
196 /// Sets `imageSmoothingQuality = "medium"`.
197 Medium,
198 /// Highest fidelity rendering with smooth edges and high-quality scaling.
199 ///
200 /// Sets `imageSmoothingQuality = "high"`. Best for vector-style content
201 /// on HiDPI displays. This is the default — when no explicit quality is
202 /// requested, the engine errs on the side of visual fidelity rather than
203 /// performance, since users typically notice aliasing artifacts before
204 /// they notice a few extra milliseconds of GPU time.
205 #[default]
206 High,
207}
208
209/// Errors that can occur while asynchronously initializing a `WebGpuRenderer`.
210///
211/// Each variant maps to one specific failure mode that the WebGPU init
212/// pipeline can encounter when calling into the browser's GPU API. The
213/// underlying JS error (when available) is carried as a `JsValue` so callers
214/// can surface the exact diagnostic string without losing fidelity.
215///
216/// Instead of logging diagnostics inside the engine, `WebGpuRenderer::init`
217/// returns `Result<WebGpuRenderer, WebGpuInitError>` and lets the caller
218/// decide how to react — typically via `Console::error` on the example side
219/// or by falling back to the Canvas 2D backend.
220#[derive(Clone, Debug)]
221pub enum WebGpuInitError {
222 /// `Reflect::get(navigator, "webgpu")` threw an exception.
223 ///
224 /// Surfaced when the JavaScript binding lookup itself fails rather than
225 /// simply returning `undefined`/`null`. Carries the original JS error.
226 NavigatorLookup(JsValue),
227 /// `navigator.gpu` is `undefined` or `null`.
228 ///
229 /// The browser does not expose WebGPU on the current origin. The most
230 /// common causes are serving over an insecure origin (must be HTTPS or
231 /// `localhost`) or running in a browser that lacks the WebGPU feature.
232 NavigatorGpuMissing,
233 /// `Reflect::get(gpu, "requestAdapter")` threw an exception.
234 ///
235 /// Carries the original JS error returned by the reflect call.
236 RequestAdapterLookup(JsValue),
237 /// `gpu.requestAdapter()` threw an exception synchronously.
238 ///
239 /// Carries the thrown JS error or value.
240 RequestAdapterCall(JsValue),
241 /// The adapter promise rejected, or the `INIT_PROMISE_TIMEOUT_MILLIS`
242 /// race timer fired before the adapter was produced.
243 ///
244 /// Carries the rejection value, which may be a string, an error object,
245 /// or `undefined` when the timeout won the race.
246 AdapterPromise(JsValue),
247 /// `requestAdapter()` resolved to `null` or `undefined`.
248 ///
249 /// No compatible GPU adapter exists for the requested `powerPreference`.
250 AdapterUnavailable,
251 /// `Reflect::get(adapter, "requestDevice")` threw an exception.
252 RequestDeviceLookup(JsValue),
253 /// `adapter.requestDevice()` threw an exception synchronously.
254 RequestDeviceCall(JsValue),
255 /// The device promise rejected, or the `INIT_PROMISE_TIMEOUT_MILLIS`
256 /// race timer fired before the device was produced.
257 DevicePromise(JsValue),
258 /// `requestDevice()` resolved to `null` or `undefined`.
259 ///
260 /// The adapter could not allocate a device, typically because the
261 /// adapter is in a `device-lost` state.
262 DeviceUnavailable,
263 /// `document.querySelector(canvas_selector)` returned `None`.
264 ///
265 /// The canvas element is not in the DOM yet (or its selector is wrong).
266 /// Carries the selector string that was queried.
267 CanvasNotFound(String),
268 /// `document.querySelector(canvas_selector)` threw an exception.
269 CanvasQuery(JsValue),
270 /// `canvas.get_context("webgpu")` returned `None`.
271 ///
272 /// The canvas is already using a different context type, or WebGPU is
273 /// disabled for this canvas.
274 CanvasContextUnavailable,
275 /// `Reflect::get(gpu, "getPreferredCanvasFormat")` threw an exception.
276 PreferredFormatLookup(JsValue),
277 /// `gpu.getPreferredCanvasFormat()` threw an exception synchronously.
278 PreferredFormatCall(JsValue),
279 /// `getPreferredCanvasFormat()` resolved to a value that is not a string.
280 ///
281 /// Carries the offending JS value so callers can log its type/name.
282 PreferredFormatType(JsValue),
283 /// `Reflect::get(context, "configure")` threw an exception.
284 ConfigureLookup(JsValue),
285 /// `Reflect::get(device, "queue")` threw an exception.
286 QueueLookup(JsValue),
287}
288
289/// Errors that can occur while initializing a `WebGlRenderer`.
290///
291/// WebGL context acquisition is synchronous, so the failure modes are far
292/// fewer than `WebGpuInitError` - the canvas must resolve and the browser
293/// must hand back a `WebGl2RenderingContext`. Each variant maps to one
294/// specific failure mode; the caller decides how to surface it (typically
295/// via `Console::error` on the example side).
296#[derive(Clone, Debug)]
297pub enum WebGlInitError {
298 /// `document.querySelector(canvas_selector)` returned `None`.
299 ///
300 /// The canvas element is not in the DOM yet (or its selector is wrong).
301 /// Carries the selector string that was queried.
302 CanvasNotFound(String),
303 /// `document.querySelector(canvas_selector)` threw an exception.
304 CanvasQuery(JsValue),
305 /// `canvas.get_context("webgl2")` returned `None`.
306 ///
307 /// The browser does not support WebGL 2, or the canvas is already bound
308 /// to a different context type.
309 ContextUnavailable,
310 /// `canvas.get_context("webgl2")` threw an exception.
311 ContextLookup(JsValue),
312 /// The object returned by `canvas.get_context("webgl2")` could not be
313 /// cast to `WebGl2RenderingContext`.
314 ContextCast,
315}
316
317/// Errors that can occur while building a WebGL shader program.
318///
319/// Each variant carries the browser-provided info log so the caller can
320/// surface the exact GLSL diagnostic without losing fidelity.
321#[derive(Clone, Debug)]
322pub enum WebGlProgramError {
323 /// Vertex or fragment shader compilation failed.
324 ///
325 /// Carries the shader info log returned by `getShaderInfoLog`.
326 ShaderCompile(String),
327 /// Program linking failed (or `createProgram` returned `None`).
328 ///
329 /// Carries the program info log returned by `getProgramInfoLog`.
330 ProgramLink(String),
331}
332
333/// Whether a vertex buffer is consumed per-vertex or per-instance.
334#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
335pub enum VertexStepMode {
336 /// Advance the buffer one vertex at a time.
337 #[default]
338 Vertex,
339 /// Advance the buffer one entry at a time, for all vertices of an
340 /// instance.
341 Instance,
342}
343
344/// A single binding entry inside a `BindGroupDescriptor`.
345#[derive(Clone, Debug)]
346pub enum BindGroupEntry {
347 /// A uniform / storage buffer binding.
348 ///
349 /// In WGSL terms, the buffer's `usage` must include `UNIFORM` for
350 /// `var<uniform>` bindings and `STORAGE` for `var<storage>` bindings.
351 Buffer {
352 /// The binding slot (matches `@binding(N)` in the shader).
353 binding: u32,
354 /// The `GpuBuffer` handle.
355 buffer: JsValue,
356 /// The byte offset into the buffer where the binding starts.
357 offset: u64,
358 /// The size in bytes of the binding. `None` means "until the end
359 /// of the buffer".
360 size: Option<u64>,
361 },
362 /// A read-write storage texture binding.
363 ///
364 /// The `GpuTexture` must have been created with `STORAGE_BINDING`
365 /// in its `usage` flag. Combine with `view` (a `GpuTextureView`)
366 /// obtained from `GpuTexture.createView()`.
367 StorageTexture {
368 /// The binding slot.
369 binding: u32,
370 /// The `GpuTextureView` handle.
371 view: JsValue,
372 /// `true` for `texture_storage_2d<format, read>` bindings,
373 /// `false` for `texture_storage_2d<format, read_write>` bindings.
374 read_only: bool,
375 },
376 /// A sampled texture binding.
377 Texture {
378 /// The binding slot.
379 binding: u32,
380 /// The `GpuTextureView` handle.
381 view: JsValue,
382 },
383 /// A sampler binding.
384 Sampler {
385 /// The binding slot.
386 binding: u32,
387 /// The `GpuSampler` handle.
388 sampler: JsValue,
389 },
390}
391
392/// The resource kind bound at a single slot of a `BindGroupLayoutEntry`.
393#[derive(Clone, Debug)]
394pub enum BindGroupEntryType {
395 /// `GpuBufferBindingLayout { type: "uniform" }`.
396 UniformBuffer,
397 /// `GpuBufferBindingLayout { type: "storage" | "read-only-storage" }`.
398 StorageBuffer {
399 /// `true` → `"read-only-storage"`, `false` → `"storage"`.
400 read_only: bool,
401 },
402 /// `GpuTextureBindingLayout`.
403 SampledTexture {
404 /// One of `"float"`, `"unfilterable-float"`, `"depth"`, `"sint"`, `"uint"`.
405 sample_type: String,
406 /// `true` if the bound texture is multisampled (matches MSAA render-target sampling).
407 multisampled: bool,
408 },
409 /// `GpuStorageTextureBindingLayout`.
410 StorageTexture {
411 /// `true` → `"read-only"`, `false` → `"read-write"`.
412 read_only: bool,
413 /// Texture format string (e.g. `"rgba8unorm"`, `"r32float"`).
414 format: String,
415 },
416 /// `GpuSamplerBindingLayout`.
417 Sampler {
418 /// `true` for filtering samplers (linear interpolation).
419 filtering: bool,
420 /// `true` for comparison samplers (depth-texture sampling).
421 comparison: bool,
422 },
423}
424
425/// WebGPU receiver-class discriminator for the `cached_method` cache.
426///
427/// JS class methods live on the prototype, so the same `Function` instance
428/// is returned for every receiver of a given class — the `(class, method)`
429/// pair uniquely identifies the cached `Function` and no receiver identity
430/// is needed. This replaces the previous stack-address keying: per-frame
431/// temporary `JsValue`s (pass encoders, command encoders) reuse stack
432/// slots across frames, so an address-keyed cache could return the wrong
433/// class's `Function` when a `GPUComputePassEncoder` landed on a slot that
434/// previously held a `GPURenderPassEncoder` (both share `setPipeline` /
435/// `setBindGroup` / `end`), producing a swallowed TypeError and a silently
436/// skipped GPU call.
437#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
438pub enum GpuReceiverClass {
439 /// `GPUDevice` (immortal, renderer-owned).
440 Device,
441 /// `GPUQueue` (immortal, renderer-owned).
442 Queue,
443 /// `GPUCanvasContext` (immortal, renderer-owned).
444 Context,
445 /// `GPUTexture` (per-call temporary).
446 Texture,
447 /// `GPUCommandEncoder` (per-frame temporary).
448 CommandEncoder,
449 /// `GPURenderPassEncoder` (per-pass temporary).
450 RenderPass,
451 /// `GPUComputePassEncoder` (per-pass temporary).
452 ComputePass,
453}