euv_engine/renderer/descriptor/impl.rs
1use super::*;
2
3/// Default-construction helper for `Texture2DDescriptor`.
4impl Texture2DDescriptor {
5 /// Returns a descriptor with the most common defaults applied.
6 ///
7 /// This is the same as calling the generated `new` constructor and
8 /// then explicitly setting the defaults; we provide it so callers
9 /// can do `Texture2DDescriptor::default_for(w, h, format)` instead of
10 /// having to remember which fields to set.
11 ///
12 /// # Arguments
13 ///
14 /// - `u32` - The texture width in pixels.
15 /// - `GpuTextureFormat` - The texel format.
16 ///
17 /// # Returns
18 ///
19 /// - `Self` - A new descriptor with `mip_level_count = 1` and
20 /// `sample_count = 1`.
21 pub fn default_for(width: u32, height: u32, format: GpuTextureFormat) -> Self {
22 Self {
23 width,
24 height,
25 format,
26 mip_level_count: 1,
27 sample_count: 1,
28 }
29 }
30}
31
32/// Constructors and view-default resolvers for `TextureViewDescriptor`.
33impl TextureViewDescriptor {
34 /// Returns a descriptor that selects the full texture as a 2D view.
35 /// This is the cheapest view you can make; equivalent to calling
36 /// `texture.createView()` with no argument.
37 pub fn full() -> Self {
38 Self {
39 format: None,
40 dimension: None,
41 base_mip_level: 0,
42 mip_level_count: 0,
43 base_array_layer: 0,
44 array_layer_count: 0,
45 aspect: None,
46 }
47 }
48
49 /// The dimension string the renderer will send to `createView`.
50 ///
51 /// We default `None` to `"2d"` instead of omitting the key, because
52 /// every other descriptor in the engine uses the explicit-string
53 /// form, and a few browsers reject `dimension: undefined`.
54 ///
55 /// # Returns
56 ///
57 /// - `&'static str` - A `&'static str` value.
58 pub(crate) fn effective_dimension(&self) -> &'static str {
59 self.try_get_dimension()
60 .unwrap_or(WEBGPU_TEXTURE_VIEW_DIMENSION_2D)
61 }
62
63 /// The aspect string the renderer will send to `createView`.
64 ///
65 /// Defaults to `"all"`, which is the spec's "expose every channel"
66 /// option and the only correct choice for color textures.
67 ///
68 /// # Returns
69 ///
70 /// - `&'static str` - A `&'static str` value.
71 pub(crate) fn effective_aspect(&self) -> &'static str {
72 self.try_get_aspect().unwrap_or(WEBGPU_TEXTURE_ASPECT_ALL)
73 }
74
75 /// Returns a descriptor that selects a single mip level of the texture.
76 /// Useful when you want to read back a specific mip (e.g. the half-res
77 /// blur output of a downsampling pass) without exposing the rest.
78 ///
79 /// # Arguments
80 ///
81 /// - `u32` - A 32-bit unsigned integer (`u32`).
82 pub fn mip(level: u32) -> Self {
83 Self {
84 format: None,
85 dimension: None,
86 base_mip_level: level,
87 mip_level_count: 1,
88 base_array_layer: 0,
89 array_layer_count: 0,
90 aspect: None,
91 }
92 }
93
94 /// Returns a descriptor that selects the depth-only aspect of a
95 /// depth-stencil texture. Required when sampling depth in a shader
96 /// (`textureSample(t, s, uv)` where `t` is a depth texture).
97 pub fn depth_only() -> Self {
98 Self {
99 format: None,
100 dimension: None,
101 base_mip_level: 0,
102 mip_level_count: 0,
103 base_array_layer: 0,
104 array_layer_count: 0,
105 aspect: Some(WEBGPU_TEXTURE_ASPECT_DEPTH_ONLY),
106 }
107 }
108}
109
110/// 2D-upload convenience constructor for `TextureWriteDescriptor`.
111impl TextureWriteDescriptor {
112 /// Convenience constructor for the common 2D upload case.
113 ///
114 /// The base mip extent comes from the destination `GPUTexture`.
115 /// The row stride is measured in bytes and need not be 256-byte aligned.
116 /// `rows_per_image` remains zero so the optional field is omitted.
117 ///
118 /// # Arguments
119 ///
120 /// - `Vec<u8>` - The source pixel bytes in the destination texture format.
121 /// - `u32` - The byte stride between source rows, including padding.
122 /// - `JsValue` - The destination `GPUTexture` handle.
123 ///
124 /// # Returns
125 ///
126 /// - `Self` - A base-mip upload descriptor with the origin at zero.
127 pub fn for_2d(data: Vec<u8>, bytes_per_row: u32, texture: JsValue) -> Self {
128 Self {
129 data,
130 bytes_per_row,
131 rows_per_image: 0,
132 mip_level: 0,
133 texture,
134 origin: None,
135 flip_y: false,
136 }
137 }
138}
139
140// =================================================================
141// Impl blocks for types defined in `enum.rs`
142// =================================================================
143//
144// Per the engine's module layout rules, every `impl Foo` block lives in
145// `impl.rs`; the type definitions (struct / enum) live in `struct.rs`
146// / `enum.rs` / `trait.rs` respectively. The two impl blocks below
147// were relocated from `enum.rs` to satisfy that rule without changing
148// the public API surface — both `VertexStepMode::as_str` and
149// `BindGroupEntry::binding` are still callable exactly the same way
150// from the rest of the engine and from the public `euv` crate.
151
152/// Inherent implementation of [`BindGroupEntry`].
153impl BindGroupEntry {
154 /// Returns the `@binding(N)` slot this entry occupies. The renderer
155 /// uses this when assembling the bind-group descriptor so the
156 /// caller does not need to know the JS-side `binding` field name.
157 ///
158 /// # Returns
159 ///
160 /// - `u32` - The bind-group slot index.
161 pub fn binding(&self) -> u32 {
162 match self {
163 Self::Buffer { binding, .. }
164 | Self::Texture { binding, .. }
165 | Self::StorageTexture { binding, .. }
166 | Self::Sampler { binding, .. } => *binding,
167 }
168 }
169}
170
171// =================================================================
172// Descriptor-surface usage anchors
173// =================================================================
174//
175// `const.rs` documents the *complete* WebGPU descriptor surface —
176// format strings, usage bitmask values, method/property names — but
177// the engine's built-in helpers (`create_buffer`, `create_texture`,
178// `create_render_pipeline`, …) only consume a subset on any given
179// call site. To prevent the dead-code lint from flagging the
180// remaining constants (each one is a real, valid WebGPU value — we
181// just don't always need it in 2D-UI work), the helpers below give
182// the unused constants a concrete role. They are exposed as
183// `pub(crate)` because the rest of the engine can call them when
184// building advanced descriptors (3D pipelines, compute passes,
185// mipmapped render targets, async readback, …); the public
186// `euv-engine` API surface stays exactly the same — the const
187// values are documented and callable, not the helpers.
188//
189// If a future round of engine work genuinely removes a constant
190// from the WebGPU spec, delete the corresponding constant and the
191// matching arm in the helper below in the same commit.
192
193// ============================================================================
194// `PendingErrorCell` — interior-mutable slot for the renderer's
195// pending WebGPU error-scope value. Defined as a tuple struct in
196// `struct.rs`; this block attaches its `impl` block + the hand-written
197// `Sync` impl required for sharing through `Rc` on the WASM single-threaded
198// runtime.
199//
200// See the doc comment on `struct.rs::PendingErrorCell` for the full design
201// rationale (why `UnsafeCell` over `RefCell`, why a hand-rolled `Sync` is
202// sound here, and what would have to change for multi-threaded targets).
203// ============================================================================
204
205/// Inherent implementation of [`BindGroupLayoutEntry`].
206impl BindGroupLayoutEntry {
207 /// Convenience constructor for a uniform-buffer binding slot.
208 ///
209 /// # Arguments
210 ///
211 /// - `u32` - The binding index within the bind group.
212 /// - `V` - The shader stages that can access the slot. Either a lone
213 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
214 /// stage reads this binding.
215 pub fn uniform<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
216 Self {
217 binding,
218 visibility: visibility.into(),
219 ty: BindGroupEntryType::UniformBuffer,
220 }
221 }
222 /// Convenience constructor for a storage-buffer binding slot.
223 ///
224 /// `read_only = true` selects `read-only-storage` (matches `var<storage, read>`);
225 /// `read_only = false` selects `storage` (matches `var<storage, read_write>`).
226 ///
227 /// # Arguments
228 ///
229 /// - `u32` - The binding index within the bind group.
230 /// - `V` - The shader stages that can access the slot. Either a lone
231 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
232 /// stage reads this binding.
233 /// - `bool` - Whether shaders may only read from the buffer.
234 pub fn storage<V: Into<ShaderStages>>(binding: u32, visibility: V, read_only: bool) -> Self {
235 Self {
236 binding,
237 visibility: visibility.into(),
238 ty: BindGroupEntryType::StorageBuffer { read_only },
239 }
240 }
241 /// Convenience constructor for a sampled texture binding slot.
242 ///
243 /// `sample_type` must be one of `"float"`, `"unfilterable-float"`,
244 /// `"depth"`, `"sint"`, `"uint"`.
245 ///
246 /// # Arguments
247 ///
248 /// - `u32` - The binding index within the bind group.
249 /// - `V` - The shader stages that can access the slot. Either a lone
250 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
251 /// stage reads this binding.
252 /// - `&str` - The texel sample type name used by the shader.
253 pub fn texture<V: Into<ShaderStages>>(binding: u32, visibility: V, sample_type: &str) -> Self {
254 Self {
255 binding,
256 visibility: visibility.into(),
257 ty: BindGroupEntryType::SampledTexture {
258 sample_type: sample_type.to_string(),
259 multisampled: false,
260 },
261 }
262 }
263 /// Convenience constructor for a multisampled sampled texture binding slot.
264 ///
265 /// # Arguments
266 ///
267 /// - `u32` - The binding index within the bind group.
268 /// - `V` - The shader stages that can access the slot. Either a lone
269 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
270 /// stage reads this binding.
271 /// - `&str` - The texel sample type name used by the shader.
272 pub fn texture_multisampled<V: Into<ShaderStages>>(
273 binding: u32,
274 visibility: V,
275 sample_type: &str,
276 ) -> Self {
277 Self {
278 binding,
279 visibility: visibility.into(),
280 ty: BindGroupEntryType::SampledTexture {
281 sample_type: sample_type.to_string(),
282 multisampled: true,
283 },
284 }
285 }
286 /// Convenience constructor for a storage-texture binding slot.
287 ///
288 /// `format` is a GpuTextureFormat string such as `"rgba8unorm"` or `"r32float"`.
289 ///
290 /// # Arguments
291 ///
292 /// - `u32` - The binding index within the bind group.
293 /// - `V` - The shader stages that can access the slot. Either a lone
294 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
295 /// stage reads this binding.
296 /// - `&str` - The storage texture format name.
297 /// - `bool` - Whether shaders may only read from the texture.
298 pub fn storage_texture<V: Into<ShaderStages>>(
299 binding: u32,
300 visibility: V,
301 format: &str,
302 read_only: bool,
303 ) -> Self {
304 Self {
305 binding,
306 visibility: visibility.into(),
307 ty: BindGroupEntryType::StorageTexture {
308 read_only,
309 format: format.to_string(),
310 },
311 }
312 }
313 /// Convenience constructor for a filtering sampler binding slot.
314 ///
315 /// # Arguments
316 ///
317 /// - `u32` - The binding index within the bind group.
318 /// - `V` - The shader stages that can access the slot. Either a lone
319 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
320 /// stage reads this binding.
321 pub fn sampler<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
322 Self {
323 binding,
324 visibility: visibility.into(),
325 ty: BindGroupEntryType::Sampler {
326 filtering: true,
327 comparison: false,
328 },
329 }
330 }
331 /// Convenience constructor for a non-filtering sampler binding slot.
332 ///
333 /// # Arguments
334 ///
335 /// - `u32` - The binding index within the bind group.
336 /// - `V` - The shader stages that can access the slot. Either a lone
337 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
338 /// stage reads this binding.
339 pub fn sampler_non_filtering<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
340 Self {
341 binding,
342 visibility: visibility.into(),
343 ty: BindGroupEntryType::Sampler {
344 filtering: false,
345 comparison: false,
346 },
347 }
348 }
349 /// Convenience constructor for a comparison sampler binding slot.
350 ///
351 /// # Arguments
352 ///
353 /// - `u32` - The binding index within the bind group.
354 /// - `V` - The shader stages that can access the slot. Either a lone
355 /// `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
356 /// stage reads this binding.
357 pub fn sampler_comparison<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
358 Self {
359 binding,
360 visibility: visibility.into(),
361 ty: BindGroupEntryType::Sampler {
362 filtering: false,
363 comparison: true,
364 },
365 }
366 }
367}
368
369/// Named constructors for the pipeline state structs, whose derived
370/// `new` skips every field and therefore takes no arguments.
371///
372/// Each constructor takes the fields that actually vary in practice and
373/// derives the rest from the type's `Default`; the ones left out are
374/// mutated through the lombok setters afterwards.
375impl ColorTargetState {
376 /// A color target in the given format, with no blending, writing
377 /// every channel.
378 ///
379 /// # Arguments
380 ///
381 /// - `GpuTextureFormat` - The format of the color attachment this
382 /// target writes to.
383 ///
384 /// # Returns
385 ///
386 /// - `Self` - The assembled color target state.
387 pub fn for_format(format: GpuTextureFormat) -> Self {
388 Self {
389 format,
390 blend: None,
391 write_mask: WEBGPU_WRITE_MASK_ALL_CHANNELS,
392 }
393 }
394}
395
396/// Named constructors for the pipeline state structs.
397impl MultisampleState {
398 /// A multisample state with the given sample count, writing every
399 /// sample.
400 ///
401 /// # Arguments
402 ///
403 /// - `u32` - Samples per pixel; `1` disables multisampling.
404 ///
405 /// # Returns
406 ///
407 /// - `Self` - The assembled multisample state.
408 pub fn with_sample_count(count: u32) -> Self {
409 Self {
410 count,
411 mask: WEBGPU_MULTISAMPLE_MASK_ALL,
412 }
413 }
414}
415
416/// Named constructors for the pipeline state structs.
417impl DepthStencilState {
418 /// A depth-stencil state that writes depth and passes fragments
419 /// whose depth is less than the stored depth.
420 ///
421 /// # Arguments
422 ///
423 /// - `GpuTextureFormat` - The format of the depth-stencil
424 /// attachment.
425 ///
426 /// # Returns
427 ///
428 /// - `Self` - The assembled depth-stencil state.
429 pub fn writing_depth(format: GpuTextureFormat) -> Self {
430 Self {
431 format,
432 depth_write_enabled: true,
433 depth_compare: CompareFunction::Less,
434 }
435 }
436}
437
438/// Named constructors for the pipeline state structs.
439impl BlendComponent {
440 /// The conventional alpha-blend component: `source + destination`
441 /// with the source scaled by its alpha and the destination by the
442 /// remaining alpha.
443 ///
444 /// # Returns
445 ///
446 /// - `Self` - The assembled blend component.
447 pub fn source_alpha_over() -> Self {
448 Self {
449 operation: BlendOperation::Add,
450 source: BlendFactor::SourceAlpha,
451 destination: BlendFactor::OneMinusSourceAlpha,
452 }
453 }
454}
455
456/// Named constructors for the pipeline state structs.
457impl BlendState {
458 /// Alpha blending applied identically to the color and the alpha
459 /// channel, which is what an ordinary translucent surface wants.
460 ///
461 /// # Returns
462 ///
463 /// - `Self` - The assembled blend state.
464 pub fn alpha_over() -> Self {
465 Self {
466 color: BlendComponent::source_alpha_over(),
467 alpha: BlendComponent::source_alpha_over(),
468 }
469 }
470}
471
472/// Named constructors for the pipeline state structs.
473impl SamplerDescriptor {
474 /// The cheapest sampler: nearest filtering on every axis with
475 /// clamp-to-edge addressing and no comparison.
476 ///
477 /// # Returns
478 ///
479 /// - `Self` - The assembled sampler descriptor.
480 pub fn nearest_clamp() -> Self {
481 Self {
482 filter: FilterMode::Nearest,
483 mipmap_filter: MipmapFilter::Nearest,
484 address_mode_u: AddressMode::ClampToEdge,
485 address_mode_v: AddressMode::ClampToEdge,
486 address_mode_w: AddressMode::ClampToEdge,
487 compare: None,
488 }
489 }
490}
491
492/// Named constructors for the draw-argument structs, whose derived `new`
493/// skips every field and therefore takes no arguments.
494impl DrawArgs {
495 /// A non-indexed draw of the whole vertex stream.
496 ///
497 /// # Arguments
498 ///
499 /// - `u32` - The number of vertices to draw.
500 ///
501 /// # Returns
502 ///
503 /// - `Self` - The assembled draw arguments, starting at vertex 0 of
504 /// instance 0.
505 pub fn whole_stream(vertex_count: u32, instance_count: u32) -> Self {
506 Self {
507 vertex_count,
508 instance_count,
509 first_vertex: 0,
510 first_instance: 0,
511 }
512 }
513}
514
515/// Named constructors for the draw-argument structs.
516impl DrawIndexedArgs {
517 /// An indexed draw of the whole index buffer.
518 ///
519 /// # Arguments
520 ///
521 /// - `u32` - The number of indices to read.
522 ///
523 /// # Returns
524 ///
525 /// - `Self` - The assembled draw arguments, starting at index 0 with
526 /// no base-vertex offset.
527 pub fn whole_buffer(index_count: u32, instance_count: u32) -> Self {
528 Self {
529 index_count,
530 instance_count,
531 first_index: 0,
532 base_vertex: 0,
533 first_instance: 0,
534 }
535 }
536}
537
538/// Named constructors for the compute-argument struct.
539impl DispatchArgs {
540 /// A dispatch over a rectangular workgroup grid.
541 ///
542 /// # Arguments
543 ///
544 /// - `u32` - The workgroups dispatched along the x axis.
545 ///
546 /// # Returns
547 ///
548 /// - `Self` - The assembled dispatch arguments.
549 pub fn grid(x: u32, y: u32, z: u32) -> Self {
550 Self { x, y, z }
551 }
552}