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}