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    /// - `data` - packed pixel bytes (format-dependent).
115    /// - `bytes_per_row` - row stride of `data`, must be a multiple of 256.
116    /// - `texture` - the destination `GpuTexture` handle.
117    ///
118    /// # Arguments
119    ///
120    /// - `Vec<u8>` - A `Vec<u8>` parameter.
121    /// - `u32` - A 32-bit unsigned integer (`u32`).
122    /// - `JsValue` - A `JsValue` parameter.
123    pub fn for_2d(data: Vec<u8>, bytes_per_row: u32, texture: JsValue) -> Self {
124        Self {
125            data,
126            bytes_per_row,
127            rows_per_image: 0,
128            mip_level: 0,
129            texture,
130            origin: None,
131            flip_y: false,
132        }
133    }
134}
135
136// =================================================================
137// Impl blocks for types defined in `enum.rs`
138// =================================================================
139//
140// Per the engine's module layout rules, every `impl Foo` block lives in
141// `impl.rs`; the type definitions (struct / enum) live in `struct.rs`
142// / `enum.rs` / `trait.rs` respectively. The two impl blocks below
143// were relocated from `enum.rs` to satisfy that rule without changing
144// the public API surface — both `VertexStepMode::as_str` and
145// `BindGroupEntry::binding` are still callable exactly the same way
146// from the rest of the engine and from the public `euv` crate.
147
148/// Inherent implementation of [`BindGroupEntry`].
149impl BindGroupEntry {
150    /// Returns the `@binding(N)` slot this entry occupies. The renderer
151    /// uses this when assembling the bind-group descriptor so the
152    /// caller does not need to know the JS-side `binding` field name.
153    ///
154    /// # Returns
155    ///
156    /// - `u32` - The bind-group slot index.
157    pub fn binding(&self) -> u32 {
158        match self {
159            Self::Buffer { binding, .. }
160            | Self::Texture { binding, .. }
161            | Self::StorageTexture { binding, .. }
162            | Self::Sampler { binding, .. } => *binding,
163        }
164    }
165}
166
167// =================================================================
168// Descriptor-surface usage anchors
169// =================================================================
170//
171// `const.rs` documents the *complete* WebGPU descriptor surface —
172// format strings, usage bitmask values, method/property names — but
173// the engine's built-in helpers (`create_buffer`, `create_texture`,
174// `create_render_pipeline`, …) only consume a subset on any given
175// call site. To prevent the dead-code lint from flagging the
176// remaining constants (each one is a real, valid WebGPU value — we
177// just don't always need it in 2D-UI work), the helpers below give
178// the unused constants a concrete role. They are exposed as
179// `pub(crate)` because the rest of the engine can call them when
180// building advanced descriptors (3D pipelines, compute passes,
181// mipmapped render targets, async readback, …); the public
182// `euv-engine` API surface stays exactly the same — the const
183// values are documented and callable, not the helpers.
184//
185// If a future round of engine work genuinely removes a constant
186// from the WebGPU spec, delete the corresponding constant and the
187// matching arm in the helper below in the same commit.
188
189// ============================================================================
190// `PendingErrorCell` — interior-mutable slot for the renderer's
191// pending WebGPU error-scope value. Defined as a tuple struct in
192// `struct.rs`; this block attaches its `impl` block + the hand-written
193// `Sync` impl required for sharing through `Rc` on the WASM single-threaded
194// runtime.
195//
196// See the doc comment on `struct.rs::PendingErrorCell` for the full design
197// rationale (why `UnsafeCell` over `RefCell`, why a hand-rolled `Sync` is
198// sound here, and what would have to change for multi-threaded targets).
199// ============================================================================
200
201/// Inherent implementation of [`BindGroupLayoutEntry`].
202impl BindGroupLayoutEntry {
203    /// Convenience constructor for a uniform-buffer binding slot.
204    ///
205    /// # Arguments
206    ///
207    /// - `u32` - The binding index within the bind group.
208    /// - `V` - The shader stages that can access the slot. Either a lone
209    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
210    ///   stage reads this binding.
211    pub fn uniform<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
212        Self {
213            binding,
214            visibility: visibility.into(),
215            ty: BindGroupEntryType::UniformBuffer,
216        }
217    }
218    /// Convenience constructor for a storage-buffer binding slot.
219    ///
220    /// `read_only = true` selects `read-only-storage` (matches `var<storage, read>`);
221    /// `read_only = false` selects `storage` (matches `var<storage, read_write>`).
222    ///
223    /// # Arguments
224    ///
225    /// - `u32` - The binding index within the bind group.
226    /// - `V` - The shader stages that can access the slot. Either a lone
227    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
228    ///   stage reads this binding.
229    /// - `bool` - Whether shaders may only read from the buffer.
230    pub fn storage<V: Into<ShaderStages>>(binding: u32, visibility: V, read_only: bool) -> Self {
231        Self {
232            binding,
233            visibility: visibility.into(),
234            ty: BindGroupEntryType::StorageBuffer { read_only },
235        }
236    }
237    /// Convenience constructor for a sampled texture binding slot.
238    ///
239    /// `sample_type` must be one of `"float"`, `"unfilterable-float"`,
240    /// `"depth"`, `"sint"`, `"uint"`.
241    ///
242    /// # Arguments
243    ///
244    /// - `u32` - The binding index within the bind group.
245    /// - `V` - The shader stages that can access the slot. Either a lone
246    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
247    ///   stage reads this binding.
248    /// - `&str` - The texel sample type name used by the shader.
249    pub fn texture<V: Into<ShaderStages>>(binding: u32, visibility: V, sample_type: &str) -> Self {
250        Self {
251            binding,
252            visibility: visibility.into(),
253            ty: BindGroupEntryType::SampledTexture {
254                sample_type: sample_type.to_string(),
255                multisampled: false,
256            },
257        }
258    }
259    /// Convenience constructor for a multisampled sampled texture binding slot.
260    ///
261    /// # Arguments
262    ///
263    /// - `u32` - The binding index within the bind group.
264    /// - `V` - The shader stages that can access the slot. Either a lone
265    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
266    ///   stage reads this binding.
267    /// - `&str` - The texel sample type name used by the shader.
268    pub fn texture_multisampled<V: Into<ShaderStages>>(
269        binding: u32,
270        visibility: V,
271        sample_type: &str,
272    ) -> Self {
273        Self {
274            binding,
275            visibility: visibility.into(),
276            ty: BindGroupEntryType::SampledTexture {
277                sample_type: sample_type.to_string(),
278                multisampled: true,
279            },
280        }
281    }
282    /// Convenience constructor for a storage-texture binding slot.
283    ///
284    /// `format` is a GpuTextureFormat string such as `"rgba8unorm"` or `"r32float"`.
285    ///
286    /// # Arguments
287    ///
288    /// - `u32` - The binding index within the bind group.
289    /// - `V` - The shader stages that can access the slot. Either a lone
290    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
291    ///   stage reads this binding.
292    /// - `&str` - The storage texture format name.
293    /// - `bool` - Whether shaders may only read from the texture.
294    pub fn storage_texture<V: Into<ShaderStages>>(
295        binding: u32,
296        visibility: V,
297        format: &str,
298        read_only: bool,
299    ) -> Self {
300        Self {
301            binding,
302            visibility: visibility.into(),
303            ty: BindGroupEntryType::StorageTexture {
304                read_only,
305                format: format.to_string(),
306            },
307        }
308    }
309    /// Convenience constructor for a filtering sampler binding slot.
310    ///
311    /// # Arguments
312    ///
313    /// - `u32` - The binding index within the bind group.
314    /// - `V` - The shader stages that can access the slot. Either a lone
315    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
316    ///   stage reads this binding.
317    pub fn sampler<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
318        Self {
319            binding,
320            visibility: visibility.into(),
321            ty: BindGroupEntryType::Sampler {
322                filtering: true,
323                comparison: false,
324            },
325        }
326    }
327    /// Convenience constructor for a non-filtering sampler binding slot.
328    ///
329    /// # Arguments
330    ///
331    /// - `u32` - The binding index within the bind group.
332    /// - `V` - The shader stages that can access the slot. Either a lone
333    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
334    ///   stage reads this binding.
335    pub fn sampler_non_filtering<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
336        Self {
337            binding,
338            visibility: visibility.into(),
339            ty: BindGroupEntryType::Sampler {
340                filtering: false,
341                comparison: false,
342            },
343        }
344    }
345    /// Convenience constructor for a comparison sampler binding slot.
346    ///
347    /// # Arguments
348    ///
349    /// - `u32` - The binding index within the bind group.
350    /// - `V` - The shader stages that can access the slot. Either a lone
351    ///   `ShaderStage`, or a `ShaderStages` mask built with `|` when more than one
352    ///   stage reads this binding.
353    pub fn sampler_comparison<V: Into<ShaderStages>>(binding: u32, visibility: V) -> Self {
354        Self {
355            binding,
356            visibility: visibility.into(),
357            ty: BindGroupEntryType::Sampler {
358                filtering: false,
359                comparison: true,
360            },
361        }
362    }
363}
364
365/// Named constructors for the pipeline state structs, whose derived
366/// `new` skips every field and therefore takes no arguments.
367///
368/// Each constructor takes the fields that actually vary in practice and
369/// derives the rest from the type's `Default`; the ones left out are
370/// mutated through the lombok setters afterwards.
371impl ColorTargetState {
372    /// A color target in the given format, with no blending, writing
373    /// every channel.
374    ///
375    /// # Arguments
376    ///
377    /// - `GpuTextureFormat` - The format of the color attachment this
378    ///   target writes to.
379    ///
380    /// # Returns
381    ///
382    /// - `Self` - The assembled color target state.
383    pub fn for_format(format: GpuTextureFormat) -> Self {
384        Self {
385            format,
386            blend: None,
387            write_mask: WEBGPU_WRITE_MASK_ALL_CHANNELS,
388        }
389    }
390}
391
392/// Named constructors for the pipeline state structs.
393impl MultisampleState {
394    /// A multisample state with the given sample count, writing every
395    /// sample.
396    ///
397    /// # Arguments
398    ///
399    /// - `u32` - Samples per pixel; `1` disables multisampling.
400    ///
401    /// # Returns
402    ///
403    /// - `Self` - The assembled multisample state.
404    pub fn with_sample_count(count: u32) -> Self {
405        Self {
406            count,
407            mask: WEBGPU_MULTISAMPLE_MASK_ALL,
408        }
409    }
410}
411
412/// Named constructors for the pipeline state structs.
413impl DepthStencilState {
414    /// A depth-stencil state that writes depth and passes fragments
415    /// whose depth is less than the stored depth.
416    ///
417    /// # Arguments
418    ///
419    /// - `GpuTextureFormat` - The format of the depth-stencil
420    ///   attachment.
421    ///
422    /// # Returns
423    ///
424    /// - `Self` - The assembled depth-stencil state.
425    pub fn writing_depth(format: GpuTextureFormat) -> Self {
426        Self {
427            format,
428            depth_write_enabled: true,
429            depth_compare: CompareFunction::Less,
430        }
431    }
432}
433
434/// Named constructors for the pipeline state structs.
435impl BlendComponent {
436    /// The conventional alpha-blend component: `source + destination`
437    /// with the source scaled by its alpha and the destination by the
438    /// remaining alpha.
439    ///
440    /// # Returns
441    ///
442    /// - `Self` - The assembled blend component.
443    pub fn source_alpha_over() -> Self {
444        Self {
445            operation: BlendOperation::Add,
446            source: BlendFactor::SourceAlpha,
447            destination: BlendFactor::OneMinusSourceAlpha,
448        }
449    }
450}
451
452/// Named constructors for the pipeline state structs.
453impl BlendState {
454    /// Alpha blending applied identically to the color and the alpha
455    /// channel, which is what an ordinary translucent surface wants.
456    ///
457    /// # Returns
458    ///
459    /// - `Self` - The assembled blend state.
460    pub fn alpha_over() -> Self {
461        Self {
462            color: BlendComponent::source_alpha_over(),
463            alpha: BlendComponent::source_alpha_over(),
464        }
465    }
466}
467
468/// Named constructors for the pipeline state structs.
469impl SamplerDescriptor {
470    /// The cheapest sampler: nearest filtering on every axis with
471    /// clamp-to-edge addressing and no comparison.
472    ///
473    /// # Returns
474    ///
475    /// - `Self` - The assembled sampler descriptor.
476    pub fn nearest_clamp() -> Self {
477        Self {
478            filter: FilterMode::Nearest,
479            mipmap_filter: MipmapFilter::Nearest,
480            address_mode_u: AddressMode::ClampToEdge,
481            address_mode_v: AddressMode::ClampToEdge,
482            address_mode_w: AddressMode::ClampToEdge,
483            compare: None,
484        }
485    }
486}
487
488/// Named constructors for the draw-argument structs, whose derived `new`
489/// skips every field and therefore takes no arguments.
490impl DrawArgs {
491    /// A non-indexed draw of the whole vertex stream.
492    ///
493    /// # Arguments
494    ///
495    /// - `u32` - The number of vertices to draw.
496    ///
497    /// # Returns
498    ///
499    /// - `Self` - The assembled draw arguments, starting at vertex 0 of
500    ///   instance 0.
501    pub fn whole_stream(vertex_count: u32, instance_count: u32) -> Self {
502        Self {
503            vertex_count,
504            instance_count,
505            first_vertex: 0,
506            first_instance: 0,
507        }
508    }
509}
510
511/// Named constructors for the draw-argument structs.
512impl DrawIndexedArgs {
513    /// An indexed draw of the whole index buffer.
514    ///
515    /// # Arguments
516    ///
517    /// - `u32` - The number of indices to read.
518    ///
519    /// # Returns
520    ///
521    /// - `Self` - The assembled draw arguments, starting at index 0 with
522    ///   no base-vertex offset.
523    pub fn whole_buffer(index_count: u32, instance_count: u32) -> Self {
524        Self {
525            index_count,
526            instance_count,
527            first_index: 0,
528            base_vertex: 0,
529            first_instance: 0,
530        }
531    }
532}
533
534/// Named constructors for the compute-argument struct.
535impl DispatchArgs {
536    /// A dispatch over a rectangular workgroup grid.
537    ///
538    /// # Arguments
539    ///
540    /// - `u32` - The workgroups dispatched along the x axis.
541    ///
542    /// # Returns
543    ///
544    /// - `Self` - The assembled dispatch arguments.
545    pub fn grid(x: u32, y: u32, z: u32) -> Self {
546        Self { x, y, z }
547    }
548}