1use alloc::boxed::Box;
2use alloc::string::String;
3use core::{mem, ops::Range};
4
5use arrayvec::ArrayVec;
6
7use super::{conv, Command as C};
8
9#[derive(Clone, Copy, Debug, Default)]
10struct TextureSlotDesc {
11 tex_target: super::BindTarget,
12 sampler_index: Option<u8>,
13}
14
15pub(super) struct State {
16 topology: u32,
17 primitive: super::PrimitiveState,
18 index_format: wgt::IndexFormat,
19 index_offset: wgt::BufferAddress,
20 vertex_buffers: [(
21 Option<super::VertexBufferDesc>,
22 Option<super::BufferBinding>,
23 ); crate::MAX_VERTEX_BUFFERS],
24 vertex_attributes: ArrayVec<super::AttributeDesc, { super::MAX_VERTEX_ATTRIBUTES }>,
25 color_targets: ArrayVec<super::ColorTargetDesc, { crate::MAX_COLOR_ATTACHMENTS }>,
26 stencil: super::StencilState,
27 depth_bias: wgt::DepthBiasState,
28 alpha_to_coverage_enabled: bool,
29 samplers: [Option<glow::Sampler>; super::MAX_SAMPLERS],
30 texture_slots: [TextureSlotDesc; super::MAX_TEXTURE_SLOTS],
31 render_size: wgt::Extent3d,
32 resolve_attachments: ArrayVec<(u32, super::TextureView), { crate::MAX_COLOR_ATTACHMENTS }>,
33 invalidate_attachments: ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>,
34 has_pass_label: bool,
35 instance_vbuf_mask: usize,
36 dirty_vbuf_mask: usize,
37 active_first_instance: u32,
38 first_instance_location: Option<glow::UniformLocation>,
39 immediates_descs: ArrayVec<super::ImmediateDesc, { super::MAX_IMMEDIATES_COMMANDS }>,
40 current_immediates_data: [u32; super::MAX_IMMEDIATES],
42 end_of_pass_timestamp: Option<glow::Query>,
43 clip_distance_count: u32,
44}
45
46impl Default for State {
47 fn default() -> Self {
48 Self {
49 topology: Default::default(),
50 primitive: Default::default(),
51 index_format: Default::default(),
52 index_offset: Default::default(),
53 vertex_buffers: Default::default(),
54 vertex_attributes: Default::default(),
55 color_targets: Default::default(),
56 stencil: Default::default(),
57 depth_bias: Default::default(),
58 alpha_to_coverage_enabled: Default::default(),
59 samplers: Default::default(),
60 texture_slots: Default::default(),
61 render_size: Default::default(),
62 resolve_attachments: Default::default(),
63 invalidate_attachments: Default::default(),
64 has_pass_label: Default::default(),
65 instance_vbuf_mask: Default::default(),
66 dirty_vbuf_mask: Default::default(),
67 active_first_instance: Default::default(),
68 first_instance_location: Default::default(),
69 immediates_descs: Default::default(),
70 current_immediates_data: [0; super::MAX_IMMEDIATES],
71 end_of_pass_timestamp: Default::default(),
72 clip_distance_count: Default::default(),
73 }
74 }
75}
76
77impl super::CommandBuffer {
78 fn clear(&mut self) {
79 self.label = None;
80 self.commands.clear();
81 self.data_bytes.clear();
82 self.queries.clear();
83 }
84
85 fn add_marker(&mut self, marker: &str) -> Range<u32> {
86 let start = self.data_bytes.len() as u32;
87 self.data_bytes.extend(marker.as_bytes());
88 start..self.data_bytes.len() as u32
89 }
90
91 fn add_immediates_data(&mut self, data: &[u32]) -> Range<u32> {
92 let data_raw = bytemuck::cast_slice(data);
93 let start = self.data_bytes.len();
94 assert!(start < u32::MAX as usize);
95 self.data_bytes.extend_from_slice(data_raw);
96 let end = self.data_bytes.len();
97 assert!(end < u32::MAX as usize);
98 (start as u32)..(end as u32)
99 }
100}
101
102impl Drop for super::CommandEncoder {
103 fn drop(&mut self) {
104 use crate::CommandEncoder;
105 unsafe { self.discard_encoding() }
106 self.counters.command_encoders.sub(1);
107 }
108}
109
110impl super::CommandEncoder {
111 fn rebind_stencil_func(&mut self) {
112 fn make(s: &super::StencilSide, face: u32) -> C {
113 C::SetStencilFunc {
114 face,
115 function: s.function,
116 reference: s.reference,
117 read_mask: s.mask_read,
118 }
119 }
120
121 let s = &self.state.stencil;
122 if s.front.function == s.back.function
123 && s.front.mask_read == s.back.mask_read
124 && s.front.reference == s.back.reference
125 {
126 self.cmd_buffer
127 .commands
128 .push(make(&s.front, glow::FRONT_AND_BACK));
129 } else {
130 self.cmd_buffer.commands.push(make(&s.front, glow::FRONT));
131 self.cmd_buffer.commands.push(make(&s.back, glow::BACK));
132 }
133 }
134
135 fn rebind_vertex_data(&mut self, first_instance: u32) {
136 if self
137 .private_caps
138 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
139 {
140 for (index, pair) in self.state.vertex_buffers.iter().enumerate() {
141 if self.state.dirty_vbuf_mask & (1 << index) == 0 {
142 continue;
143 }
144 let (buffer_desc, vb) = match *pair {
145 (Some(ref vb_desc), Some(ref vb)) => (vb_desc.clone(), vb),
146 (_, _) => continue,
148 };
149 let instance_offset = match buffer_desc.step {
150 wgt::VertexStepMode::Vertex => 0,
151 wgt::VertexStepMode::Instance => first_instance * buffer_desc.stride,
152 };
153
154 self.cmd_buffer.commands.push(C::SetVertexBuffer {
155 index: index as u32,
156 buffer: super::BufferBinding {
157 raw: vb.raw,
158 offset: vb.offset + instance_offset as wgt::BufferAddress,
159 },
160 buffer_desc,
161 });
162 self.state.dirty_vbuf_mask ^= 1 << index;
163 }
164 } else {
165 let mut vbuf_mask = 0;
166 for attribute in self.state.vertex_attributes.iter() {
167 if self.state.dirty_vbuf_mask & (1 << attribute.buffer_index) == 0 {
168 continue;
169 }
170 let (buffer_desc, vb) =
171 match self.state.vertex_buffers[attribute.buffer_index as usize] {
172 (Some(ref vb_desc), Some(ref vb)) => (vb_desc.clone(), vb),
173 (_, _) => continue,
175 };
176
177 let mut attribute_desc = attribute.clone();
178 attribute_desc.offset += vb.offset as u32;
179 if buffer_desc.step == wgt::VertexStepMode::Instance {
180 attribute_desc.offset += buffer_desc.stride * first_instance;
181 }
182
183 self.cmd_buffer.commands.push(C::SetVertexAttribute {
184 buffer: Some(vb.raw),
185 buffer_desc,
186 attribute_desc,
187 });
188 vbuf_mask |= 1 << attribute.buffer_index;
189 }
190 self.state.dirty_vbuf_mask ^= vbuf_mask;
191 }
192 }
193
194 fn rebind_sampler_states(&mut self, dirty_textures: u32, dirty_samplers: u32) {
195 for (texture_index, slot) in self.state.texture_slots.iter().enumerate() {
196 if dirty_textures & (1 << texture_index) != 0
197 || slot
198 .sampler_index
199 .is_some_and(|si| dirty_samplers & (1 << si) != 0)
200 {
201 let sampler = slot
202 .sampler_index
203 .and_then(|si| self.state.samplers[si as usize]);
204 self.cmd_buffer
205 .commands
206 .push(C::BindSampler(texture_index as u32, sampler));
207 }
208 }
209 }
210
211 fn prepare_draw(&mut self, first_instance: u32) {
212 let emulated_first_instance_value = if self
215 .private_caps
216 .contains(super::PrivateCapabilities::FULLY_FEATURED_INSTANCING)
217 {
218 0
219 } else {
220 first_instance
221 };
222
223 if emulated_first_instance_value != self.state.active_first_instance {
224 self.state.dirty_vbuf_mask |= self.state.instance_vbuf_mask;
226 self.state.active_first_instance = emulated_first_instance_value;
227 }
228 if self.state.dirty_vbuf_mask != 0 {
229 self.rebind_vertex_data(emulated_first_instance_value);
230 }
231 }
232
233 fn set_pipeline_inner(&mut self, inner: &super::PipelineInner) {
234 self.cmd_buffer.commands.push(C::SetProgram(inner.program));
235
236 self.state
237 .first_instance_location
238 .clone_from(&inner.first_instance_location);
239 self.state
240 .immediates_descs
241 .clone_from(&inner.immediates_descs);
242
243 let mut dirty_textures = 0u32;
245 for (texture_index, (slot, &sampler_index)) in self
246 .state
247 .texture_slots
248 .iter_mut()
249 .zip(inner.sampler_map.iter())
250 .enumerate()
251 {
252 if slot.sampler_index != sampler_index {
253 slot.sampler_index = sampler_index;
254 dirty_textures |= 1 << texture_index;
255 }
256 }
257 if dirty_textures != 0 {
258 self.rebind_sampler_states(dirty_textures, 0);
259 }
260 }
261}
262
263impl crate::CommandEncoder for super::CommandEncoder {
264 type A = super::Api;
265
266 unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> {
267 self.state = State::default();
268 self.cmd_buffer.label = label.map(String::from);
269 Ok(())
270 }
271 unsafe fn discard_encoding(&mut self) {
272 self.cmd_buffer.clear();
273 }
274 unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
275 Ok(mem::take(&mut self.cmd_buffer))
276 }
277 unsafe fn reset_all<I>(&mut self, _command_buffers: I) {
278 }
280
281 unsafe fn transition_buffers<'a, T>(&mut self, barriers: T)
282 where
283 T: Iterator<Item = crate::BufferBarrier<'a, super::Buffer>>,
284 {
285 if !self
286 .private_caps
287 .contains(super::PrivateCapabilities::MEMORY_BARRIERS)
288 {
289 return;
290 }
291 for bar in barriers {
292 if !bar.usage.from.contains(wgt::BufferUses::STORAGE_READ_WRITE) {
294 continue;
295 }
296 self.cmd_buffer
297 .commands
298 .push(C::BufferBarrier(bar.buffer.raw.unwrap(), bar.usage.to));
299 }
300 }
301
302 unsafe fn transition_textures<'a, T>(&mut self, barriers: T)
303 where
304 T: Iterator<Item = crate::TextureBarrier<'a, super::Texture>>,
305 {
306 if !self
307 .private_caps
308 .contains(super::PrivateCapabilities::MEMORY_BARRIERS)
309 {
310 return;
311 }
312
313 let mut combined_usage = wgt::TextureUses::empty();
314 for bar in barriers {
315 if !bar.usage.from.intersects(
318 wgt::TextureUses::STORAGE_READ_WRITE | wgt::TextureUses::STORAGE_WRITE_ONLY,
319 ) {
320 continue;
321 }
322 combined_usage |= bar.usage.to;
325 }
326
327 if !combined_usage.is_empty() {
328 self.cmd_buffer
329 .commands
330 .push(C::TextureBarrier(combined_usage));
331 }
332 }
333
334 unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
335 self.cmd_buffer
336 .commands
337 .push(C::ClearBuffer(Box::new(super::ClearBufferOp {
338 dst: buffer.clone(),
339 dst_target: buffer.target,
340 range,
341 })));
342 }
343
344 unsafe fn copy_buffer_to_buffer<T>(
345 &mut self,
346 src: &super::Buffer,
347 dst: &super::Buffer,
348 regions: T,
349 ) where
350 T: Iterator<Item = crate::BufferCopy>,
351 {
352 let (src_target, dst_target) = if src.target == dst.target {
353 (glow::COPY_READ_BUFFER, glow::COPY_WRITE_BUFFER)
354 } else {
355 (src.target, dst.target)
356 };
357 for copy in regions {
358 self.cmd_buffer
359 .commands
360 .push(C::CopyBufferToBuffer(Box::new(super::BufferToBufferCopy {
361 src: src.clone(),
362 src_target,
363 dst: dst.clone(),
364 dst_target,
365 copy,
366 })))
367 }
368 }
369
370 #[cfg(webgl)]
371 unsafe fn copy_external_image_to_texture<T>(
372 &mut self,
373 src: &wgt::CopyExternalImageSourceInfo,
374 dst: &super::Texture,
375 dst_premultiplication: bool,
376 regions: T,
377 ) where
378 T: Iterator<Item = crate::TextureCopy>,
379 {
380 let (dst_raw, dst_target) = dst.inner.as_native();
381 for copy in regions {
382 self.cmd_buffer
383 .commands
384 .push(C::CopyExternalImageToTexture(Box::new(
385 super::ExternalImageToTextureCopy {
386 src: src.clone(),
387 dst: dst_raw,
388 dst_target,
389 dst_format: dst.format,
390 dst_premultiplication,
391 copy,
392 },
393 )))
394 }
395 }
396
397 unsafe fn copy_texture_to_texture<T>(
398 &mut self,
399 src: &super::Texture,
400 _src_usage: wgt::TextureUses,
401 dst: &super::Texture,
402 regions: T,
403 ) where
404 T: Iterator<Item = crate::TextureCopy>,
405 {
406 let (src_raw, src_target) = src.inner.as_native();
407 let (dst_raw, dst_target) = dst.inner.as_native();
408 for mut copy in regions {
409 copy.clamp_size_to_virtual(&src.copy_size, &dst.copy_size);
410 self.cmd_buffer
411 .commands
412 .push(C::CopyTextureToTexture(Box::new(
413 super::TextureToTextureCopy {
414 src: src_raw,
415 src_target,
416 dst: dst_raw,
417 dst_target,
418 copy,
419 },
420 )))
421 }
422 }
423
424 unsafe fn copy_buffer_to_texture<T>(
425 &mut self,
426 src: &super::Buffer,
427 dst: &super::Texture,
428 regions: T,
429 ) where
430 T: Iterator<Item = crate::BufferTextureCopy>,
431 {
432 let (dst_raw, dst_target) = dst.inner.as_native();
433
434 for mut copy in regions {
435 copy.clamp_size_to_virtual(&dst.copy_size);
436 self.cmd_buffer
437 .commands
438 .push(C::CopyBufferToTexture(Box::new(
439 super::BufferToTextureCopy {
440 src: src.clone(),
441 src_target: src.target,
442 dst: dst_raw,
443 dst_target,
444 dst_format: dst.format,
445 copy,
446 },
447 )))
448 }
449 }
450
451 unsafe fn copy_texture_to_buffer<T>(
452 &mut self,
453 src: &super::Texture,
454 _src_usage: wgt::TextureUses,
455 dst: &super::Buffer,
456 regions: T,
457 ) where
458 T: Iterator<Item = crate::BufferTextureCopy>,
459 {
460 let (src_raw, src_target) = src.inner.as_native();
461 for mut copy in regions {
462 copy.clamp_size_to_virtual(&src.copy_size);
463 self.cmd_buffer
464 .commands
465 .push(C::CopyTextureToBuffer(Box::new(
466 super::TextureToBufferCopy {
467 src: src_raw,
468 src_target,
469 src_format: src.format,
470 dst: dst.clone(),
471 dst_target: dst.target,
472 copy,
473 },
474 )))
475 }
476 }
477
478 unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {
479 let query = set.queries[index as usize];
480 self.cmd_buffer
481 .commands
482 .push(C::BeginQuery(query, set.target));
483 }
484 unsafe fn end_query(&mut self, set: &super::QuerySet, _index: u32) {
485 self.cmd_buffer.commands.push(C::EndQuery(set.target));
486 }
487 unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {
488 let query = set.queries[index as usize];
489 self.cmd_buffer.commands.push(C::TimestampQuery(query));
490 }
491 unsafe fn reset_queries(&mut self, _set: &super::QuerySet, _range: Range<u32>) {
492 }
494 unsafe fn copy_query_results(
495 &mut self,
496 set: &super::QuerySet,
497 range: Range<u32>,
498 buffer: &super::Buffer,
499 offset: wgt::BufferAddress,
500 _stride: wgt::BufferSize,
501 ) {
502 let start = self.cmd_buffer.queries.len();
503 self.cmd_buffer
504 .queries
505 .extend_from_slice(&set.queries[range.start as usize..range.end as usize]);
506 let query_range = start as u32..self.cmd_buffer.queries.len() as u32;
507 self.cmd_buffer
508 .commands
509 .push(C::CopyQueryResults(Box::new(super::QueryResultsCopy {
510 query_range,
511 dst: buffer.clone(),
512 dst_target: buffer.target,
513 dst_offset: offset,
514 })));
515 }
516
517 unsafe fn begin_render_pass(
520 &mut self,
521 desc: &crate::RenderPassDescriptor<super::QuerySet, super::TextureView>,
522 ) -> Result<(), crate::DeviceError> {
523 debug_assert!(self.state.end_of_pass_timestamp.is_none());
524 if let Some(ref t) = desc.timestamp_writes {
525 if let Some(index) = t.beginning_of_pass_write_index {
526 unsafe { self.write_timestamp(t.query_set, index) }
527 }
528 self.state.end_of_pass_timestamp = t
529 .end_of_pass_write_index
530 .map(|index| t.query_set.queries[index as usize]);
531 }
532
533 self.state.render_size = desc.extent;
534 self.state.resolve_attachments.clear();
535 self.state.invalidate_attachments.clear();
536 if let Some(label) = desc.label {
537 let range = self.cmd_buffer.add_marker(label);
538 self.cmd_buffer.commands.push(C::PushDebugGroup(range));
539 self.state.has_pass_label = true;
540 }
541
542 let rendering_to_external_framebuffer = desc
543 .color_attachments
544 .iter()
545 .filter_map(|at| at.as_ref())
546 .any(|at| match at.target.view.inner {
547 #[cfg(webgl)]
548 super::TextureInner::ExternalFramebuffer { .. } => true,
549 #[cfg(native)]
550 super::TextureInner::ExternalNativeFramebuffer { .. } => true,
551 _ => false,
552 });
553
554 if rendering_to_external_framebuffer && desc.color_attachments.len() != 1 {
555 panic!("Multiple render attachments with external framebuffers are not supported.");
556 }
557
558 assert!(desc.color_attachments.len() <= 32);
560
561 match desc
562 .color_attachments
563 .first()
564 .filter(|at| at.is_some())
565 .and_then(|at| at.as_ref().map(|at| &at.target.view.inner))
566 {
567 Some(&super::TextureInner::DefaultRenderbuffer) => {
569 self.cmd_buffer
570 .commands
571 .push(C::ResetFramebuffer { is_default: true });
572 }
573 _ => {
574 self.cmd_buffer
576 .commands
577 .push(C::ResetFramebuffer { is_default: false });
578
579 for (i, cat) in desc.color_attachments.iter().enumerate() {
580 if let Some(cat) = cat.as_ref() {
581 let attachment = glow::COLOR_ATTACHMENT0 + i as u32;
582 if let Some(ref rat) = cat.resolve_target {
584 if matches!(rat.view.inner, super::TextureInner::Texture { .. })
585 && self.private_caps.contains(
586 super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE,
587 )
588 && !cat.ops.contains(crate::AttachmentOps::STORE)
589 && i == 0
591 {
592 self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
593 super::AttachmentBinding {
594 attachment,
595 view: rat.view.clone(),
596 depth_slice: None,
597 sample_count: desc.sample_count,
598 },
599 )));
600 continue;
601 }
602 }
603 self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
604 super::AttachmentBinding {
605 attachment,
606 view: cat.target.view.clone(),
607 depth_slice: cat.depth_slice,
608 sample_count: 1,
609 },
610 )));
611 if let Some(ref rat) = cat.resolve_target {
612 self.state
613 .resolve_attachments
614 .push((attachment, rat.view.clone()));
615 }
616 if cat.ops.contains(crate::AttachmentOps::STORE_DISCARD) {
617 self.state.invalidate_attachments.push(attachment);
618 }
619 }
620 }
621 if let Some(ref dsat) = desc.depth_stencil_attachment {
622 let aspects = dsat.target.view.aspects;
623 let attachment = match aspects {
624 crate::FormatAspects::DEPTH => glow::DEPTH_ATTACHMENT,
625 crate::FormatAspects::STENCIL => glow::STENCIL_ATTACHMENT,
626 _ => glow::DEPTH_STENCIL_ATTACHMENT,
627 };
628 self.cmd_buffer.commands.push(C::BindAttachment(Box::new(
629 super::AttachmentBinding {
630 attachment,
631 view: dsat.target.view.clone(),
632 depth_slice: None,
633 sample_count: 1,
634 },
635 )));
636 if aspects.contains(crate::FormatAspects::DEPTH)
637 && dsat.depth_ops.contains(crate::AttachmentOps::STORE_DISCARD)
638 {
639 self.state
640 .invalidate_attachments
641 .push(glow::DEPTH_ATTACHMENT);
642 }
643 if aspects.contains(crate::FormatAspects::STENCIL)
644 && dsat
645 .stencil_ops
646 .contains(crate::AttachmentOps::STORE_DISCARD)
647 {
648 self.state
649 .invalidate_attachments
650 .push(glow::STENCIL_ATTACHMENT);
651 }
652 }
653 }
654 }
655
656 let rect = crate::Rect {
657 x: 0,
658 y: 0,
659 w: desc.extent.width as i32,
660 h: desc.extent.height as i32,
661 };
662 self.cmd_buffer.commands.push(C::SetScissor(rect.clone()));
663 self.cmd_buffer.commands.push(C::SetViewport {
664 rect,
665 depth: 0.0..1.0,
666 });
667
668 if !rendering_to_external_framebuffer {
669 self.cmd_buffer
671 .commands
672 .push(C::SetDrawColorBuffers(desc.color_attachments.len() as u8));
673 }
674
675 for (i, cat) in desc
677 .color_attachments
678 .iter()
679 .filter_map(|at| at.as_ref())
680 .enumerate()
681 {
682 if cat.ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
683 let c = &cat.clear_value;
684 self.cmd_buffer.commands.push(
685 match cat.target.view.format.sample_type(None, None).unwrap() {
686 wgt::TextureSampleType::Float { .. } => C::ClearColorF {
687 draw_buffer: i as u32,
688 color: [c.r as f32, c.g as f32, c.b as f32, c.a as f32],
689 is_srgb: cat.target.view.format.is_srgb(),
690 },
691 wgt::TextureSampleType::Uint => C::ClearColorU(
692 i as u32,
693 [c.r as u32, c.g as u32, c.b as u32, c.a as u32],
694 ),
695 wgt::TextureSampleType::Sint => C::ClearColorI(
696 i as u32,
697 [c.r as i32, c.g as i32, c.b as i32, c.a as i32],
698 ),
699 wgt::TextureSampleType::Depth => unreachable!(),
700 },
701 );
702 }
703 }
704
705 if let Some(ref dsat) = desc.depth_stencil_attachment {
706 let clear_depth = dsat.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR);
707 let clear_stencil = dsat.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR);
708
709 if clear_depth && clear_stencil {
710 self.cmd_buffer.commands.push(C::ClearDepthAndStencil(
711 dsat.clear_value.0,
712 dsat.clear_value.1,
713 ));
714 } else if clear_depth {
715 self.cmd_buffer
716 .commands
717 .push(C::ClearDepth(dsat.clear_value.0));
718 } else if clear_stencil {
719 self.cmd_buffer
720 .commands
721 .push(C::ClearStencil(dsat.clear_value.1));
722 }
723 }
724 Ok(())
725 }
726 unsafe fn end_render_pass(&mut self) {
727 for (attachment, dst) in self.state.resolve_attachments.drain(..) {
728 self.cmd_buffer.commands.push(C::ResolveAttachment(Box::new(
729 super::AttachmentResolve {
730 attachment,
731 dst,
732 size: self.state.render_size,
733 },
734 )));
735 }
736 if !self.state.invalidate_attachments.is_empty() {
737 self.cmd_buffer.commands.push(C::InvalidateAttachments(
738 self.state.invalidate_attachments.clone(),
739 ));
740 self.state.invalidate_attachments.clear();
741 }
742 if self.state.has_pass_label {
743 self.cmd_buffer.commands.push(C::PopDebugGroup);
744 self.state.has_pass_label = false;
745 }
746 self.state.instance_vbuf_mask = 0;
747 self.state.dirty_vbuf_mask = 0;
748 self.state.active_first_instance = 0;
749 self.state.color_targets.clear();
750 for vat in &self.state.vertex_attributes {
751 self.cmd_buffer
752 .commands
753 .push(C::UnsetVertexAttribute(vat.location));
754 }
755 self.state.vertex_attributes.clear();
756 self.state.primitive = super::PrimitiveState::default();
757
758 if let Some(query) = self.state.end_of_pass_timestamp.take() {
759 self.cmd_buffer.commands.push(C::TimestampQuery(query));
760 }
761 }
762
763 unsafe fn set_bind_group(
764 &mut self,
765 layout: &super::PipelineLayout,
766 index: u32,
767 group: &super::BindGroup,
768 dynamic_offsets: &[wgt::DynamicOffset],
769 ) {
770 let mut do_index = 0;
771 let mut dirty_textures = 0u32;
772 let mut dirty_samplers = 0u32;
773 let group_info = layout.group_infos[index as usize].as_ref().unwrap();
774
775 for (binding_layout, raw_binding) in group_info.entries.iter().zip(group.contents.iter()) {
776 let slot = group_info.binding_to_slot[binding_layout.binding as usize] as u32;
777 match *raw_binding {
778 super::RawBinding::Buffer {
779 raw,
780 offset: base_offset,
781 size,
782 } => {
783 let mut offset = base_offset;
784 let target = match binding_layout.ty {
785 wgt::BindingType::Buffer {
786 ty,
787 has_dynamic_offset,
788 min_binding_size: _,
789 } => {
790 if has_dynamic_offset {
791 offset += dynamic_offsets[do_index] as i32;
792 do_index += 1;
793 }
794 match ty {
795 wgt::BufferBindingType::Uniform => glow::UNIFORM_BUFFER,
796 wgt::BufferBindingType::Storage { .. } => {
797 glow::SHADER_STORAGE_BUFFER
798 }
799 }
800 }
801 _ => unreachable!(),
802 };
803 self.cmd_buffer.commands.push(C::BindBuffer {
804 target,
805 slot,
806 buffer: raw,
807 offset,
808 size,
809 });
810 }
811 super::RawBinding::Sampler(sampler) => {
812 dirty_samplers |= 1 << slot;
813 self.state.samplers[slot as usize] = Some(sampler);
814 }
815 super::RawBinding::Texture {
816 raw,
817 target,
818 aspects,
819 ref mip_levels,
820 } => {
821 dirty_textures |= 1 << slot;
822 self.state.texture_slots[slot as usize].tex_target = target;
823 self.cmd_buffer.commands.push(C::BindTexture {
824 slot,
825 texture: raw,
826 target,
827 aspects,
828 mip_levels: mip_levels.clone(),
829 });
830 }
831 super::RawBinding::Image(ref binding) => {
832 self.cmd_buffer.commands.push(C::BindImage {
833 slot,
834 binding: binding.clone(),
835 });
836 }
837 }
838 }
839
840 self.rebind_sampler_states(dirty_textures, dirty_samplers);
841 }
842
843 unsafe fn set_immediates(
844 &mut self,
845 _layout: &super::PipelineLayout,
846 offset_bytes: u32,
847 data: &[u32],
848 ) {
849 let start_words = offset_bytes / 4;
857 let end_words = start_words + data.len() as u32;
858 self.state.current_immediates_data[start_words as usize..end_words as usize]
859 .copy_from_slice(data);
860
861 for uniform in self.state.immediates_descs.iter().cloned() {
867 let uniform_size_words = uniform.size_bytes / 4;
868 let uniform_start_words = uniform.offset / 4;
869 let uniform_end_words = uniform_start_words + uniform_size_words;
870
871 let needs_updating =
873 start_words < uniform_end_words || uniform_start_words <= end_words;
874
875 if needs_updating {
876 let uniform_data = &self.state.current_immediates_data
877 [uniform_start_words as usize..uniform_end_words as usize];
878
879 let range = self.cmd_buffer.add_immediates_data(uniform_data);
880
881 self.cmd_buffer.commands.push(C::SetImmediates {
882 uniform,
883 offset: range.start,
884 });
885 }
886 }
887 }
888
889 unsafe fn insert_debug_marker(&mut self, label: &str) {
890 let range = self.cmd_buffer.add_marker(label);
891 self.cmd_buffer.commands.push(C::InsertDebugMarker(range));
892 }
893 unsafe fn begin_debug_marker(&mut self, group_label: &str) {
894 let range = self.cmd_buffer.add_marker(group_label);
895 self.cmd_buffer.commands.push(C::PushDebugGroup(range));
896 }
897 unsafe fn end_debug_marker(&mut self) {
898 self.cmd_buffer.commands.push(C::PopDebugGroup);
899 }
900
901 unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) {
902 self.state.topology = conv::map_primitive_topology(pipeline.primitive.topology);
903
904 if self
905 .private_caps
906 .contains(super::PrivateCapabilities::VERTEX_BUFFER_LAYOUT)
907 {
908 for vat in pipeline.vertex_attributes.iter() {
909 let vb = pipeline.vertex_buffers[vat.buffer_index as usize]
910 .as_ref()
911 .unwrap();
912 self.cmd_buffer.commands.push(C::SetVertexAttribute {
914 buffer: None,
915 buffer_desc: vb.clone(),
916 attribute_desc: vat.clone(),
917 });
918 }
919 } else {
920 for vat in &self.state.vertex_attributes {
921 self.cmd_buffer
922 .commands
923 .push(C::UnsetVertexAttribute(vat.location));
924 }
925 self.state.vertex_attributes.clear();
926
927 self.state.dirty_vbuf_mask = 0;
928 for vat in pipeline.vertex_attributes.iter() {
930 self.state.dirty_vbuf_mask |= 1 << vat.buffer_index;
932 self.state.vertex_attributes.push(vat.clone());
933 }
934 }
935
936 self.state.instance_vbuf_mask = 0;
937 for (index, (&mut (ref mut state_desc, _), pipe_desc)) in self
939 .state
940 .vertex_buffers
941 .iter_mut()
942 .zip(pipeline.vertex_buffers.iter())
943 .enumerate()
944 {
945 let Some(pipe_desc) = pipe_desc else {
946 continue;
947 };
948 if pipe_desc.step == wgt::VertexStepMode::Instance {
949 self.state.instance_vbuf_mask |= 1 << index;
950 }
951 if state_desc.as_ref() != Some(pipe_desc) {
952 self.state.dirty_vbuf_mask |= 1 << index;
953 *state_desc = Some(pipe_desc.clone());
954 }
955 }
956
957 self.set_pipeline_inner(&pipeline.inner);
958
959 let prim_state = conv::map_primitive_state(&pipeline.primitive);
961 if prim_state != self.state.primitive {
962 self.cmd_buffer
963 .commands
964 .push(C::SetPrimitive(prim_state.clone()));
965 self.state.primitive = prim_state;
966 }
967
968 let mut aspects = crate::FormatAspects::empty();
970 if pipeline.depth_bias != self.state.depth_bias {
971 self.state.depth_bias = pipeline.depth_bias;
972 self.cmd_buffer
973 .commands
974 .push(C::SetDepthBias(pipeline.depth_bias));
975 }
976 if let Some(ref depth) = pipeline.depth {
977 aspects |= crate::FormatAspects::DEPTH;
978 self.cmd_buffer.commands.push(C::SetDepth(depth.clone()));
979 }
980 if let Some(ref stencil) = pipeline.stencil {
981 aspects |= crate::FormatAspects::STENCIL;
982 self.state.stencil = stencil.clone();
983 self.rebind_stencil_func();
984 if stencil.front.ops == stencil.back.ops
985 && stencil.front.mask_write == stencil.back.mask_write
986 {
987 self.cmd_buffer.commands.push(C::SetStencilOps {
988 face: glow::FRONT_AND_BACK,
989 write_mask: stencil.front.mask_write,
990 ops: stencil.front.ops.clone(),
991 });
992 } else {
993 self.cmd_buffer.commands.push(C::SetStencilOps {
994 face: glow::FRONT,
995 write_mask: stencil.front.mask_write,
996 ops: stencil.front.ops.clone(),
997 });
998 self.cmd_buffer.commands.push(C::SetStencilOps {
999 face: glow::BACK,
1000 write_mask: stencil.back.mask_write,
1001 ops: stencil.back.ops.clone(),
1002 });
1003 }
1004 }
1005 self.cmd_buffer
1006 .commands
1007 .push(C::ConfigureDepthStencil(aspects));
1008
1009 if pipeline.alpha_to_coverage_enabled != self.state.alpha_to_coverage_enabled {
1011 self.state.alpha_to_coverage_enabled = pipeline.alpha_to_coverage_enabled;
1012 self.cmd_buffer
1013 .commands
1014 .push(C::SetAlphaToCoverage(pipeline.alpha_to_coverage_enabled));
1015 }
1016
1017 if self.state.color_targets[..] != pipeline.color_targets[..] {
1019 if pipeline
1020 .color_targets
1021 .iter()
1022 .skip(1)
1023 .any(|ct| *ct != pipeline.color_targets[0])
1024 {
1025 for (index, ct) in pipeline.color_targets.iter().enumerate() {
1026 self.cmd_buffer.commands.push(C::SetColorTarget {
1027 draw_buffer_index: Some(index as u32),
1028 desc: ct.clone(),
1029 });
1030 }
1031 } else {
1032 self.cmd_buffer.commands.push(C::SetColorTarget {
1033 draw_buffer_index: None,
1034 desc: pipeline.color_targets.first().cloned().unwrap_or_default(),
1035 });
1036 }
1037 }
1038 self.state.color_targets.clear();
1039 for ct in pipeline.color_targets.iter() {
1040 self.state.color_targets.push(ct.clone());
1041 }
1042
1043 if pipeline.inner.clip_distance_count != self.state.clip_distance_count {
1045 self.cmd_buffer.commands.push(C::SetClipDistances {
1046 old_count: self.state.clip_distance_count,
1047 new_count: pipeline.inner.clip_distance_count,
1048 });
1049 self.state.clip_distance_count = pipeline.inner.clip_distance_count;
1050 }
1051 }
1052
1053 unsafe fn set_index_buffer<'a>(
1054 &mut self,
1055 binding: crate::BufferBinding<'a, super::Buffer>,
1056 format: wgt::IndexFormat,
1057 ) {
1058 self.state.index_offset = binding.offset;
1059 self.state.index_format = format;
1060 self.cmd_buffer
1061 .commands
1062 .push(C::SetIndexBuffer(binding.buffer.raw.unwrap()));
1063 }
1064 unsafe fn set_vertex_buffer<'a>(
1065 &mut self,
1066 index: u32,
1067 binding: crate::BufferBinding<'a, super::Buffer>,
1068 ) {
1069 self.state.dirty_vbuf_mask |= 1 << index;
1070 let (_, ref mut vb) = self.state.vertex_buffers[index as usize];
1071 *vb = Some(super::BufferBinding {
1072 raw: binding.buffer.raw.unwrap(),
1073 offset: binding.offset,
1074 });
1075 }
1076 unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth: Range<f32>) {
1077 self.cmd_buffer.commands.push(C::SetViewport {
1078 rect: crate::Rect {
1079 x: rect.x as i32,
1080 y: rect.y as i32,
1081 w: rect.w as i32,
1082 h: rect.h as i32,
1083 },
1084 depth,
1085 });
1086 }
1087 unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {
1088 self.cmd_buffer.commands.push(C::SetScissor(crate::Rect {
1089 x: rect.x as i32,
1090 y: rect.y as i32,
1091 w: rect.w as i32,
1092 h: rect.h as i32,
1093 }));
1094 }
1095 unsafe fn set_stencil_reference(&mut self, value: u32) {
1096 self.state.stencil.front.reference = value;
1097 self.state.stencil.back.reference = value;
1098 self.rebind_stencil_func();
1099 }
1100 unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) {
1101 self.cmd_buffer.commands.push(C::SetBlendConstant(*color));
1102 }
1103
1104 unsafe fn draw(
1105 &mut self,
1106 first_vertex: u32,
1107 vertex_count: u32,
1108 first_instance: u32,
1109 instance_count: u32,
1110 ) {
1111 self.prepare_draw(first_instance);
1112 #[allow(
1113 clippy::clone_on_copy,
1114 reason = "False positive when cloning glow::UniformLocation"
1115 )]
1116 self.cmd_buffer.commands.push(C::Draw {
1117 topology: self.state.topology,
1118 first_vertex,
1119 vertex_count,
1120 first_instance,
1121 instance_count,
1122 first_instance_location: self.state.first_instance_location.clone(),
1123 });
1124 }
1125 unsafe fn draw_indexed(
1126 &mut self,
1127 first_index: u32,
1128 index_count: u32,
1129 base_vertex: i32,
1130 first_instance: u32,
1131 instance_count: u32,
1132 ) {
1133 self.prepare_draw(first_instance);
1134 let (index_size, index_type) = match self.state.index_format {
1135 wgt::IndexFormat::Uint16 => (2, glow::UNSIGNED_SHORT),
1136 wgt::IndexFormat::Uint32 => (4, glow::UNSIGNED_INT),
1137 };
1138 let index_offset = self.state.index_offset + index_size * first_index as wgt::BufferAddress;
1139 #[allow(
1140 clippy::clone_on_copy,
1141 reason = "False positive when cloning glow::UniformLocation"
1142 )]
1143 self.cmd_buffer.commands.push(C::DrawIndexed {
1144 topology: self.state.topology,
1145 index_type,
1146 index_offset,
1147 index_count,
1148 base_vertex,
1149 first_instance,
1150 instance_count,
1151 first_instance_location: self.state.first_instance_location.clone(),
1152 });
1153 }
1154 unsafe fn draw_mesh_tasks(
1155 &mut self,
1156 _group_count_x: u32,
1157 _group_count_y: u32,
1158 _group_count_z: u32,
1159 ) {
1160 unreachable!()
1161 }
1162 unsafe fn draw_indirect(
1163 &mut self,
1164 buffer: &super::Buffer,
1165 offset: wgt::BufferAddress,
1166 draw_count: u32,
1167 ) {
1168 self.prepare_draw(0);
1169 for draw in 0..draw_count as wgt::BufferAddress {
1170 let indirect_offset =
1171 offset + draw * size_of::<wgt::DrawIndirectArgs>() as wgt::BufferAddress;
1172 #[allow(
1173 clippy::clone_on_copy,
1174 reason = "False positive when cloning glow::UniformLocation"
1175 )]
1176 self.cmd_buffer.commands.push(C::DrawIndirect {
1177 topology: self.state.topology,
1178 indirect_buf: buffer.raw.unwrap(),
1179 indirect_offset,
1180 first_instance_location: self.state.first_instance_location.clone(),
1181 });
1182 }
1183 }
1184 unsafe fn draw_indexed_indirect(
1185 &mut self,
1186 buffer: &super::Buffer,
1187 offset: wgt::BufferAddress,
1188 draw_count: u32,
1189 ) {
1190 self.prepare_draw(0);
1191 let index_type = match self.state.index_format {
1192 wgt::IndexFormat::Uint16 => glow::UNSIGNED_SHORT,
1193 wgt::IndexFormat::Uint32 => glow::UNSIGNED_INT,
1194 };
1195 for draw in 0..draw_count as wgt::BufferAddress {
1196 let indirect_offset =
1197 offset + draw * size_of::<wgt::DrawIndexedIndirectArgs>() as wgt::BufferAddress;
1198 #[allow(
1199 clippy::clone_on_copy,
1200 reason = "False positive when cloning glow::UniformLocation"
1201 )]
1202 self.cmd_buffer.commands.push(C::DrawIndexedIndirect {
1203 topology: self.state.topology,
1204 index_type,
1205 indirect_buf: buffer.raw.unwrap(),
1206 indirect_offset,
1207 first_instance_location: self.state.first_instance_location.clone(),
1208 });
1209 }
1210 }
1211 unsafe fn draw_mesh_tasks_indirect(
1212 &mut self,
1213 _buffer: &<Self::A as crate::Api>::Buffer,
1214 _offset: wgt::BufferAddress,
1215 _draw_count: u32,
1216 ) {
1217 unreachable!()
1218 }
1219 unsafe fn draw_indirect_count(
1220 &mut self,
1221 _buffer: &super::Buffer,
1222 _offset: wgt::BufferAddress,
1223 _count_buffer: &super::Buffer,
1224 _count_offset: wgt::BufferAddress,
1225 _max_count: u32,
1226 ) {
1227 unreachable!()
1228 }
1229 unsafe fn draw_indexed_indirect_count(
1230 &mut self,
1231 _buffer: &super::Buffer,
1232 _offset: wgt::BufferAddress,
1233 _count_buffer: &super::Buffer,
1234 _count_offset: wgt::BufferAddress,
1235 _max_count: u32,
1236 ) {
1237 unreachable!()
1238 }
1239 unsafe fn draw_mesh_tasks_indirect_count(
1240 &mut self,
1241 _buffer: &<Self::A as crate::Api>::Buffer,
1242 _offset: wgt::BufferAddress,
1243 _count_buffer: &<Self::A as crate::Api>::Buffer,
1244 _count_offset: wgt::BufferAddress,
1245 _max_count: u32,
1246 ) {
1247 unreachable!()
1248 }
1249
1250 unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor<super::QuerySet>) {
1253 debug_assert!(self.state.end_of_pass_timestamp.is_none());
1254 if let Some(ref t) = desc.timestamp_writes {
1255 if let Some(index) = t.beginning_of_pass_write_index {
1256 unsafe { self.write_timestamp(t.query_set, index) }
1257 }
1258 self.state.end_of_pass_timestamp = t
1259 .end_of_pass_write_index
1260 .map(|index| t.query_set.queries[index as usize]);
1261 }
1262
1263 if let Some(label) = desc.label {
1264 let range = self.cmd_buffer.add_marker(label);
1265 self.cmd_buffer.commands.push(C::PushDebugGroup(range));
1266 self.state.has_pass_label = true;
1267 }
1268 }
1269 unsafe fn end_compute_pass(&mut self) {
1270 if self.state.has_pass_label {
1271 self.cmd_buffer.commands.push(C::PopDebugGroup);
1272 self.state.has_pass_label = false;
1273 }
1274
1275 if let Some(query) = self.state.end_of_pass_timestamp.take() {
1276 self.cmd_buffer.commands.push(C::TimestampQuery(query));
1277 }
1278 }
1279
1280 unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) {
1281 self.set_pipeline_inner(&pipeline.inner);
1282 }
1283
1284 unsafe fn dispatch_workgroups(&mut self, count: [u32; 3]) {
1285 if count.contains(&0) {
1287 return;
1288 }
1289 self.cmd_buffer.commands.push(C::Dispatch(count));
1290 }
1291 unsafe fn dispatch_workgroups_indirect(
1292 &mut self,
1293 buffer: &super::Buffer,
1294 offset: wgt::BufferAddress,
1295 ) {
1296 self.cmd_buffer.commands.push(C::DispatchIndirect {
1297 indirect_buf: buffer.raw.unwrap(),
1298 indirect_offset: offset,
1299 });
1300 }
1301
1302 unsafe fn build_acceleration_structures<'a, T>(
1303 &mut self,
1304 _descriptor_count: u32,
1305 _descriptors: T,
1306 ) where
1307 super::Api: 'a,
1308 T: IntoIterator<
1309 Item = crate::BuildAccelerationStructureDescriptor<
1310 'a,
1311 super::Buffer,
1312 super::AccelerationStructure,
1313 >,
1314 >,
1315 {
1316 unimplemented!()
1317 }
1318
1319 unsafe fn place_acceleration_structure_barrier(
1320 &mut self,
1321 _barriers: crate::AccelerationStructureBarrier,
1322 ) {
1323 unimplemented!()
1324 }
1325
1326 unsafe fn copy_acceleration_structure_to_acceleration_structure(
1327 &mut self,
1328 _src: &super::AccelerationStructure,
1329 _dst: &super::AccelerationStructure,
1330 _copy: wgt::AccelerationStructureCopy,
1331 ) {
1332 unimplemented!()
1333 }
1334
1335 unsafe fn read_acceleration_structure_compact_size(
1336 &mut self,
1337 _acceleration_structure: &super::AccelerationStructure,
1338 _buf: &super::Buffer,
1339 ) {
1340 unimplemented!()
1341 }
1342
1343 unsafe fn set_acceleration_structure_dependencies(
1344 _command_buffers: &[&super::CommandBuffer],
1345 _dependencies: &[&super::AccelerationStructure],
1346 ) {
1347 unimplemented!()
1348 }
1349
1350 unsafe fn begin_ray_tracing_pass(&mut self, _desc: &crate::RayTracingPassDescriptor) {
1351 unimplemented!()
1352 }
1353
1354 unsafe fn end_ray_tracing_pass(&mut self) {
1355 unimplemented!()
1356 }
1357
1358 unsafe fn set_ray_tracing_pipeline(
1359 &mut self,
1360 _pipeline: &<Self::A as crate::Api>::RayTracingPipeline,
1361 ) {
1362 unimplemented!()
1363 }
1364
1365 unsafe fn trace_rays(
1366 &mut self,
1367 _count: [u32; 3],
1368 _ray_generation_group_data: crate::PipelineGroupData<super::Buffer>,
1369 _miss_group_data: crate::PipelineGroupData<super::Buffer>,
1370 _intersection_group_data: crate::PipelineGroupData<super::Buffer>,
1371 ) {
1372 unimplemented!()
1373 }
1374}