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}