Skip to main content

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}