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}