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    /// - `ShaderStage` - The shader stages that can access the slot.
209    pub fn uniform(binding: u32, visibility: ShaderStage) -> Self {
210        Self {
211            binding,
212            visibility,
213            ty: BindGroupEntryType::UniformBuffer,
214        }
215    }
216    /// Convenience constructor for a storage-buffer binding slot.
217    ///
218    /// `read_only = true` selects `read-only-storage` (matches `var<storage, read>`);
219    /// `read_only = false` selects `storage` (matches `var<storage, read_write>`).
220    ///
221    /// # Arguments
222    ///
223    /// - `u32` - The binding index within the bind group.
224    /// - `ShaderStage` - The shader stages that can access the slot.
225    /// - `bool` - Whether shaders may only read from the buffer.
226    pub fn storage(binding: u32, visibility: ShaderStage, read_only: bool) -> Self {
227        Self {
228            binding,
229            visibility,
230            ty: BindGroupEntryType::StorageBuffer { read_only },
231        }
232    }
233    /// Convenience constructor for a sampled texture binding slot.
234    ///
235    /// `sample_type` must be one of `"float"`, `"unfilterable-float"`,
236    /// `"depth"`, `"sint"`, `"uint"`.
237    ///
238    /// # Arguments
239    ///
240    /// - `u32` - The binding index within the bind group.
241    /// - `ShaderStage` - The shader stages that can access the slot.
242    /// - `&str` - The texel sample type name used by the shader.
243    pub fn texture(binding: u32, visibility: ShaderStage, sample_type: &str) -> Self {
244        Self {
245            binding,
246            visibility,
247            ty: BindGroupEntryType::SampledTexture {
248                sample_type: sample_type.to_string(),
249                multisampled: false,
250            },
251        }
252    }
253    /// Convenience constructor for a multisampled sampled texture binding slot.
254    ///
255    /// # Arguments
256    ///
257    /// - `u32` - The binding index within the bind group.
258    /// - `ShaderStage` - The shader stages that can access the slot.
259    /// - `&str` - The texel sample type name used by the shader.
260    pub fn texture_multisampled(binding: u32, visibility: ShaderStage, sample_type: &str) -> Self {
261        Self {
262            binding,
263            visibility,
264            ty: BindGroupEntryType::SampledTexture {
265                sample_type: sample_type.to_string(),
266                multisampled: true,
267            },
268        }
269    }
270    /// Convenience constructor for a storage-texture binding slot.
271    ///
272    /// `format` is a GpuTextureFormat string such as `"rgba8unorm"` or `"r32float"`.
273    ///
274    /// # Arguments
275    ///
276    /// - `u32` - The binding index within the bind group.
277    /// - `ShaderStage` - The shader stages that can access the slot.
278    /// - `&str` - The storage texture format name.
279    /// - `bool` - Whether shaders may only read from the texture.
280    pub fn storage_texture(
281        binding: u32,
282        visibility: ShaderStage,
283        format: &str,
284        read_only: bool,
285    ) -> Self {
286        Self {
287            binding,
288            visibility,
289            ty: BindGroupEntryType::StorageTexture {
290                read_only,
291                format: format.to_string(),
292            },
293        }
294    }
295    /// Convenience constructor for a filtering sampler binding slot.
296    ///
297    /// # Arguments
298    ///
299    /// - `u32` - The binding index within the bind group.
300    /// - `ShaderStage` - The shader stages that can access the slot.
301    pub fn sampler(binding: u32, visibility: ShaderStage) -> Self {
302        Self {
303            binding,
304            visibility,
305            ty: BindGroupEntryType::Sampler {
306                filtering: true,
307                comparison: false,
308            },
309        }
310    }
311    /// Convenience constructor for a non-filtering sampler binding slot.
312    ///
313    /// # Arguments
314    ///
315    /// - `u32` - The binding index within the bind group.
316    /// - `ShaderStage` - The shader stages that can access the slot.
317    pub fn sampler_non_filtering(binding: u32, visibility: ShaderStage) -> Self {
318        Self {
319            binding,
320            visibility,
321            ty: BindGroupEntryType::Sampler {
322                filtering: false,
323                comparison: false,
324            },
325        }
326    }
327    /// Convenience constructor for a comparison sampler binding slot.
328    ///
329    /// # Arguments
330    ///
331    /// - `u32` - The binding index within the bind group.
332    /// - `ShaderStage` - The shader stages that can access the slot.
333    pub fn sampler_comparison(binding: u32, visibility: ShaderStage) -> Self {
334        Self {
335            binding,
336            visibility,
337            ty: BindGroupEntryType::Sampler {
338                filtering: false,
339                comparison: true,
340            },
341        }
342    }
343}
344
345/// Named constructors for the pipeline state structs, whose derived
346/// `new` skips every field and therefore takes no arguments.
347///
348/// Each constructor takes the fields that actually vary in practice and
349/// derives the rest from the type's `Default`; the ones left out are
350/// mutated through the lombok setters afterwards.
351impl ColorTargetState {
352    /// A color target in the given format, with no blending, writing
353    /// every channel.
354    ///
355    /// # Arguments
356    ///
357    /// - `GpuTextureFormat` - The format of the color attachment this
358    ///   target writes to.
359    ///
360    /// # Returns
361    ///
362    /// - `Self` - The assembled color target state.
363    pub fn for_format(format: GpuTextureFormat) -> Self {
364        Self {
365            format,
366            blend: None,
367            write_mask: WEBGPU_WRITE_MASK_ALL_CHANNELS,
368        }
369    }
370}
371
372/// Named constructors for the pipeline state structs.
373impl MultisampleState {
374    /// A multisample state with the given sample count, writing every
375    /// sample.
376    ///
377    /// # Arguments
378    ///
379    /// - `u32` - Samples per pixel; `1` disables multisampling.
380    ///
381    /// # Returns
382    ///
383    /// - `Self` - The assembled multisample state.
384    pub fn with_sample_count(count: u32) -> Self {
385        Self {
386            count,
387            mask: WEBGPU_MULTISAMPLE_MASK_ALL,
388        }
389    }
390}
391
392/// Named constructors for the pipeline state structs.
393impl DepthStencilState {
394    /// A depth-stencil state that writes depth and passes fragments
395    /// whose depth is less than the stored depth.
396    ///
397    /// # Arguments
398    ///
399    /// - `GpuTextureFormat` - The format of the depth-stencil
400    ///   attachment.
401    ///
402    /// # Returns
403    ///
404    /// - `Self` - The assembled depth-stencil state.
405    pub fn writing_depth(format: GpuTextureFormat) -> Self {
406        Self {
407            format,
408            depth_write_enabled: true,
409            depth_compare: CompareFunction::Less,
410        }
411    }
412}
413
414/// Named constructors for the pipeline state structs.
415impl BlendComponent {
416    /// The conventional alpha-blend component: `source + destination`
417    /// with the source scaled by its alpha and the destination by the
418    /// remaining alpha.
419    ///
420    /// # Returns
421    ///
422    /// - `Self` - The assembled blend component.
423    pub fn source_alpha_over() -> Self {
424        Self {
425            operation: BlendOperation::Add,
426            source: BlendFactor::SourceAlpha,
427            destination: BlendFactor::OneMinusSourceAlpha,
428        }
429    }
430}
431
432/// Named constructors for the pipeline state structs.
433impl BlendState {
434    /// Alpha blending applied identically to the color and the alpha
435    /// channel, which is what an ordinary translucent surface wants.
436    ///
437    /// # Returns
438    ///
439    /// - `Self` - The assembled blend state.
440    pub fn alpha_over() -> Self {
441        Self {
442            color: BlendComponent::source_alpha_over(),
443            alpha: BlendComponent::source_alpha_over(),
444        }
445    }
446}
447
448/// Named constructors for the pipeline state structs.
449impl SamplerDescriptor {
450    /// The cheapest sampler: nearest filtering on every axis with
451    /// clamp-to-edge addressing and no comparison.
452    ///
453    /// # Returns
454    ///
455    /// - `Self` - The assembled sampler descriptor.
456    pub fn nearest_clamp() -> Self {
457        Self {
458            filter: FilterMode::Nearest,
459            mipmap_filter: MipmapFilter::Nearest,
460            address_mode_u: AddressMode::ClampToEdge,
461            address_mode_v: AddressMode::ClampToEdge,
462            address_mode_w: AddressMode::ClampToEdge,
463            compare: None,
464        }
465    }
466}
467
468/// Named constructors for the draw-argument structs, whose derived `new`
469/// skips every field and therefore takes no arguments.
470impl DrawArgs {
471    /// A non-indexed draw of the whole vertex stream.
472    ///
473    /// # Arguments
474    ///
475    /// - `u32` - The number of vertices to draw.
476    /// - `u32` - The number of instances to draw.
477    ///
478    /// # Returns
479    ///
480    /// - `Self` - The assembled draw arguments, starting at vertex 0 of
481    ///   instance 0.
482    pub fn whole_stream(vertex_count: u32, instance_count: u32) -> Self {
483        Self {
484            vertex_count,
485            instance_count,
486            first_vertex: 0,
487            first_instance: 0,
488        }
489    }
490}
491
492/// Named constructors for the draw-argument structs.
493impl DrawIndexedArgs {
494    /// An indexed draw of the whole index buffer.
495    ///
496    /// # Arguments
497    ///
498    /// - `u32` - The number of indices to read.
499    /// - `u32` - The number of instances to draw.
500    ///
501    /// # Returns
502    ///
503    /// - `Self` - The assembled draw arguments, starting at index 0 with
504    ///   no base-vertex offset.
505    pub fn whole_buffer(index_count: u32, instance_count: u32) -> Self {
506        Self {
507            index_count,
508            instance_count,
509            first_index: 0,
510            base_vertex: 0,
511            first_instance: 0,
512        }
513    }
514}
515
516/// Named constructors for the compute-argument struct.
517impl DispatchArgs {
518    /// A dispatch over a rectangular workgroup grid.
519    ///
520    /// # Arguments
521    ///
522    /// - `u32` - The workgroups dispatched along the x axis.
523    /// - `u32` - The workgroups dispatched along the y axis.
524    /// - `u32` - The workgroups dispatched along the z axis.
525    ///
526    /// # Returns
527    ///
528    /// - `Self` - The assembled dispatch arguments.
529    pub fn grid(x: u32, y: u32, z: u32) -> Self {
530        Self { x, y, z }
531    }
532}