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    /// - `u32` - The texture height in pixels.
16    /// - `GpuTextureFormat` - The texel format.
17    ///
18    /// # Returns
19    ///
20    /// - `Self` - A new descriptor with `mip_level_count = 1` and
21    ///   `sample_count = 1`.
22    pub fn default_for(width: u32, height: u32, format: GpuTextureFormat) -> Self {
23        Self {
24            width,
25            height,
26            format,
27            mip_level_count: 1,
28            sample_count: 1,
29        }
30    }
31}
32
33/// Constructors and view-default resolvers for `TextureViewDescriptor`.
34impl TextureViewDescriptor {
35    /// Returns a descriptor that selects the full texture as a 2D view.
36    /// This is the cheapest view you can make; equivalent to calling
37    /// `texture.createView()` with no argument.
38    pub fn full() -> Self {
39        Self {
40            format: None,
41            dimension: None,
42            base_mip_level: 0,
43            mip_level_count: 0,
44            base_array_layer: 0,
45            array_layer_count: 0,
46            aspect: None,
47        }
48    }
49
50    /// The dimension string the renderer will send to `createView`.
51    ///
52    /// We default `None` to `"2d"` instead of omitting the key, because
53    /// every other descriptor in the engine uses the explicit-string
54    /// form, and a few browsers reject `dimension: undefined`.
55    ///
56    /// # Returns
57    ///
58    /// - `&'static str` - A `&'static str` value.
59    pub(crate) fn effective_dimension(&self) -> &'static str {
60        self.try_get_dimension()
61            .unwrap_or(WEBGPU_TEXTURE_VIEW_DIMENSION_2D)
62    }
63
64    /// The aspect string the renderer will send to `createView`.
65    ///
66    /// Defaults to `"all"`, which is the spec's "expose every channel"
67    /// option and the only correct choice for color textures.
68    ///
69    /// # Returns
70    ///
71    /// - `&'static str` - A `&'static str` value.
72    pub(crate) fn effective_aspect(&self) -> &'static str {
73        self.try_get_aspect().unwrap_or(WEBGPU_TEXTURE_ASPECT_ALL)
74    }
75
76    /// Returns a descriptor that selects a single mip level of the texture.
77    /// Useful when you want to read back a specific mip (e.g. the half-res
78    /// blur output of a downsampling pass) without exposing the rest.
79    ///
80    /// # Arguments
81    ///
82    /// - `u32` - A 32-bit unsigned integer (`u32`).
83    pub fn mip(level: u32) -> Self {
84        Self {
85            format: None,
86            dimension: None,
87            base_mip_level: level,
88            mip_level_count: 1,
89            base_array_layer: 0,
90            array_layer_count: 0,
91            aspect: None,
92        }
93    }
94
95    /// Returns a descriptor that selects the depth-only aspect of a
96    /// depth-stencil texture. Required when sampling depth in a shader
97    /// (`textureSample(t, s, uv)` where `t` is a depth texture).
98    pub fn depth_only() -> Self {
99        Self {
100            format: None,
101            dimension: None,
102            base_mip_level: 0,
103            mip_level_count: 0,
104            base_array_layer: 0,
105            array_layer_count: 0,
106            aspect: Some(WEBGPU_TEXTURE_ASPECT_DEPTH_ONLY),
107        }
108    }
109}
110
111/// 2D-upload convenience constructor for `TextureWriteDescriptor`.
112impl TextureWriteDescriptor {
113    /// Convenience constructor for the common 2D upload case.
114    ///
115    /// - `data` - packed pixel bytes (format-dependent).
116    /// - `bytes_per_row` - row stride of `data`, must be a multiple of 256.
117    /// - `texture` - the destination `GpuTexture` handle.
118    ///
119    /// # Arguments
120    ///
121    /// - `Vec<u8>` - A `Vec<u8>` parameter.
122    /// - `u32` - A 32-bit unsigned integer (`u32`).
123    /// - `JsValue` - A `JsValue` parameter.
124    pub fn for_2d(data: Vec<u8>, bytes_per_row: u32, texture: JsValue) -> Self {
125        Self {
126            data,
127            bytes_per_row,
128            rows_per_image: 0,
129            mip_level: 0,
130            texture,
131            origin: None,
132            flip_y: false,
133        }
134    }
135}
136
137// =================================================================
138// Impl blocks for types defined in `enum.rs`
139// =================================================================
140//
141// Per the engine's module layout rules, every `impl Foo` block lives in
142// `impl.rs`; the type definitions (struct / enum) live in `struct.rs`
143// / `enum.rs` / `trait.rs` respectively. The two impl blocks below
144// were relocated from `enum.rs` to satisfy that rule without changing
145// the public API surface — both `VertexStepMode::as_str` and
146// `BindGroupEntry::binding` are still callable exactly the same way
147// from the rest of the engine and from the public `euv` crate.
148
149/// Inherent implementation of [`BindGroupEntry`].
150impl BindGroupEntry {
151    /// Returns the `@binding(N)` slot this entry occupies. The renderer
152    /// uses this when assembling the bind-group descriptor so the
153    /// caller does not need to know the JS-side `binding` field name.
154    ///
155    /// # Returns
156    ///
157    /// - `u32` - The bind-group slot index.
158    pub fn binding(&self) -> u32 {
159        match self {
160            Self::Buffer { binding, .. }
161            | Self::Texture { binding, .. }
162            | Self::StorageTexture { binding, .. }
163            | Self::Sampler { binding, .. } => *binding,
164        }
165    }
166}
167
168// =================================================================
169// Descriptor-surface usage anchors
170// =================================================================
171//
172// `const.rs` documents the *complete* WebGPU descriptor surface —
173// format strings, usage bitmask values, method/property names — but
174// the engine's built-in helpers (`create_buffer`, `create_texture`,
175// `create_render_pipeline`, …) only consume a subset on any given
176// call site. To prevent the dead-code lint from flagging the
177// remaining constants (each one is a real, valid WebGPU value — we
178// just don't always need it in 2D-UI work), the helpers below give
179// the unused constants a concrete role. They are exposed as
180// `pub(crate)` because the rest of the engine can call them when
181// building advanced descriptors (3D pipelines, compute passes,
182// mipmapped render targets, async readback, …); the public
183// `euv-engine` API surface stays exactly the same — the const
184// values are documented and callable, not the helpers.
185//
186// If a future round of engine work genuinely removes a constant
187// from the WebGPU spec, delete the corresponding constant and the
188// matching arm in the helper below in the same commit.
189
190// ============================================================================
191// `PendingErrorCell` — interior-mutable slot for the renderer's
192// pending WebGPU error-scope value. Defined as a tuple struct in
193// `struct.rs`; this block attaches its `impl` block + the hand-written
194// `Sync` impl required for sharing through `Rc` on the WASM single-threaded
195// runtime.
196//
197// See the doc comment on `struct.rs::PendingErrorCell` for the full design
198// rationale (why `UnsafeCell` over `RefCell`, why a hand-rolled `Sync` is
199// sound here, and what would have to change for multi-threaded targets).
200// ============================================================================
201
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    /// - `u32` - The number of instances to draw.
497    ///
498    /// # Returns
499    ///
500    /// - `Self` - The assembled draw arguments, starting at vertex 0 of
501    ///   instance 0.
502    pub fn whole_stream(vertex_count: u32, instance_count: u32) -> Self {
503        Self {
504            vertex_count,
505            instance_count,
506            first_vertex: 0,
507            first_instance: 0,
508        }
509    }
510}
511
512/// Named constructors for the draw-argument structs.
513impl DrawIndexedArgs {
514    /// An indexed draw of the whole index buffer.
515    ///
516    /// # Arguments
517    ///
518    /// - `u32` - The number of indices to read.
519    /// - `u32` - The number of instances to draw.
520    ///
521    /// # Returns
522    ///
523    /// - `Self` - The assembled draw arguments, starting at index 0 with
524    ///   no base-vertex offset.
525    pub fn whole_buffer(index_count: u32, instance_count: u32) -> Self {
526        Self {
527            index_count,
528            instance_count,
529            first_index: 0,
530            base_vertex: 0,
531            first_instance: 0,
532        }
533    }
534}
535
536/// Named constructors for the compute-argument struct.
537impl DispatchArgs {
538    /// A dispatch over a rectangular workgroup grid.
539    ///
540    /// # Arguments
541    ///
542    /// - `u32` - The workgroups dispatched along the x axis.
543    /// - `u32` - The workgroups dispatched along the y axis.
544    /// - `u32` - The workgroups dispatched along the z 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}