Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/wgpu-hal/src/dx12/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 69 kB image not shown  

Quelle  command.rs   Sprache: Rust

 

use alloc::vec::Vec;
use core::{mem, ops::Range};

use windows::{
    core::Interface as _,
    Win32::{
        Foundation,
        Graphics::{Direct3D12, Dxgi},
    },
};

use super::conv;
use crate::{
    auxil::{
        self,
        dxgi::{name::ObjectExt as _, result::HResult as _},
    },
    dx12::borrow_interface_temporarily,
    AccelerationStructureEntries, CommandEncoder as _,
};

fn make_box(origin: &wgt::Origin3d, size: &crate::CopyExtent) -> Direct3D12::D3D12_BOX {
    Direct3D12::D3D12_BOX {
        left: origin.x,
        top: origin.y,
        right: origin.x + size.width,
        bottom: origin.y + size.height,
        front: origin.z,
        back: origin.z + size.depth,
    }
}

impl crate::BufferTextureCopy {
    fn to_subresource_footprint(
        &self,
        format: wgt::TextureFormat,
    ) -> Direct3D12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT {
        let (block_width, _) = format.block_dimensions();
        Direct3D12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT {
            Offset: self.buffer_layout.offset,
            Footprint: Direct3D12::D3D12_SUBRESOURCE_FOOTPRINT {
                Format: auxil::dxgi::conv::map_texture_format_for_copy(
                    format,
                    self.texture_base.aspect,
                )
                .unwrap(),
                Width: self.size.width,
                Height: self.size.height,
                Depth: self.size.depth,
                RowPitch: {
                    let actual = self.buffer_layout.bytes_per_row.unwrap_or_else(|| {
                        // this may happen for single-line updates
                        let block_size = format
                            .block_copy_size(Some(self.texture_base.aspect.map()))
                            .unwrap();
                        (self.size.width / block_width) * block_size
                    });
                    wgt::math::align_to(actual, Direct3D12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT)
                },
            },
        }
    }
}

impl super::Temp {
    fn prepare_marker(&mut self, marker: &str) -> (&[u16], u32) {
        self.marker.clear();
        self.marker.extend(marker.encode_utf16());
        self.marker.push(0);
        (&self.marker, self.marker.len() as u32 * 2)
    }
}

impl Drop for super::CommandEncoder {
    fn drop(&mut self) {
        use crate::CommandEncoder;
        unsafe { self.discard_encoding() }

        let mut rtv_pool = self.rtv_pool.lock();
        for handle in self.temp_rtv_handles.drain(..) {
            rtv_pool.free_handle(handle);
        }
        drop(rtv_pool);

        self.counters.command_encoders.sub(1);
    }
}

impl super::CommandEncoder {
    unsafe fn begin_pass(&mut self, kind: super::PassKind, label: crate::Label) {
        let list = self.list.as_ref().unwrap();
        self.pass.kind = kind;
        if let Some(label) = label {
            let (wide_label, size) = self.temp.prepare_marker(label);
            unsafe { list.BeginEvent(0, Some(wide_label.as_ptr().cast()), size) };
            self.pass.has_label = true;
        }
        self.pass.dirty_root_elements = 0;
        self.pass.dirty_vertex_buffers = 0;
        unsafe {
            list.SetDescriptorHeaps(&[
                Some(self.shared.heap_views.raw.clone()),
                Some(self.shared.sampler_heap.heap().clone()),
            ])
        };
    }

    unsafe fn end_pass(&mut self) {
        let list = self.list.as_ref().unwrap();
        unsafe { list.SetDescriptorHeaps(&[]) };
        if self.pass.has_label {
            unsafe { list.EndEvent() };
        }
        self.pass.clear();
    }

    unsafe fn prepare_vertex_buffers(&mut self) {
        while self.pass.dirty_vertex_buffers != 0 {
            let list = self.list.as_ref().unwrap();
            let index = self.pass.dirty_vertex_buffers.trailing_zeros();
            self.pass.dirty_vertex_buffers ^= 1 << index;
            unsafe {
                list.IASetVertexBuffers(
                    index,
                    Some(&self.pass.vertex_buffers[index as usize..][..1]),
                );
            }
        }
    }

    unsafe fn prepare_draw(&mut self, first_vertex: i32, first_instance: u32) {
        unsafe {
            self.prepare_vertex_buffers();
        }
        if let Some(root_index) = self
            .pass
            .layout
            .special_constants
            .as_ref()
            .map(|sc| sc.root_index)
        {
            let special_constants = super::SpecialConstants::from_indirect_draw_call_params(
                first_vertex,
                first_instance,
            );
            let needs_update = match self.pass.root_elements[root_index as usize] {
                super::RootElement::SpecialConstants(old_special_constants) => {
                    old_special_constants != special_constants
                }
                _ => true,
            };
            if needs_update {
                self.pass.dirty_root_elements |= 1 << root_index;
                self.pass.root_elements[root_index as usize] =
                    super::RootElement::SpecialConstants(special_constants);
            }
        }
        self.update_root_elements();
    }

    fn prepare_dispatch(&mut self, workgroup_count: [u32; 3]) {
        if let Some(root_index) = self
            .pass
            .layout
            .special_constants
            .as_ref()
            .map(|sc| sc.root_index)
        {
            let special_constants =
                super::SpecialConstants::from_compute_dispatch_params(workgroup_count);
            let needs_update = match self.pass.root_elements[root_index as usize] {
                super::RootElement::SpecialConstants(old_special_constants) => {
                    old_special_constants != special_constants
                }
                _ => true,
            };
            if needs_update {
                self.pass.dirty_root_elements |= 1 << root_index;
                self.pass.root_elements[root_index as usize] =
                    super::RootElement::SpecialConstants(special_constants);
            }
        }
        self.update_root_elements();
    }

    // Note: we have to call this lazily before draw calls. Otherwise, D3D complains
    // about the root parameters being incompatible with root signature.
    fn update_root_elements(&mut self) {
        use super::PassKind as Pk;

        while self.pass.dirty_root_elements != 0 {
            let list = self.list.as_ref().unwrap();
            let index = self.pass.dirty_root_elements.trailing_zeros();
            self.pass.dirty_root_elements ^= 1 << index;

            match self.pass.root_elements[index as usize] {
                super::RootElement::Empty => unreachable!(
                    "Empty root element at index {index} should not have been marked as dirty"
                ),
                super::RootElement::Immediates => {
                    let info = self.pass.layout.immediates_info.as_ref().unwrap();

                    for offset in 0..info.size {
                        let val = self.pass.immediates[offset as usize];
                        match self.pass.kind {
                            Pk::Render => unsafe {
                                list.SetGraphicsRoot32BitConstant(index, val, offset)
                            },
                            Pk::Compute => unsafe {
                                list.SetComputeRoot32BitConstant(index, val, offset)
                            },
                            Pk::Transfer => (),
                        }
                    }
                }
                super::RootElement::SpecialConstants(super::SpecialConstants {
                    first_vertex_or_x,
                    first_instance_or_y,
                    unused_or_z,
                }) => match self.pass.kind {
                    Pk::Render => {
                        unsafe {
                            list.SetGraphicsRoot32BitConstant(index, first_vertex_or_x as u32, 0)
                        };
                        unsafe { list.SetGraphicsRoot32BitConstant(index, first_instance_or_y, 1) };
                    }
                    Pk::Compute => {
                        unsafe {
                            list.SetComputeRoot32BitConstant(index, first_vertex_or_x as u32, 0)
                        };
                        unsafe { list.SetComputeRoot32BitConstant(index, first_instance_or_y, 1) };
                        unsafe { list.SetComputeRoot32BitConstant(index, unused_or_z, 2) };
                    }
                    Pk::Transfer => (),
                },
                super::RootElement::DescriptorTable(descriptor) => match self.pass.kind {
                    Pk::Render => unsafe { list.SetGraphicsRootDescriptorTable(index, descriptor) },
                    Pk::Compute => unsafe { list.SetComputeRootDescriptorTable(index, descriptor) },
                    Pk::Transfer => (),
                },
                super::RootElement::DynamicUniformBuffer { address } => {
                    let address = address.ptr;
                    match self.pass.kind {
                        Pk::Render => unsafe {
                            list.SetGraphicsRootConstantBufferView(index, address)
                        },
                        Pk::Compute => unsafe {
                            list.SetComputeRootConstantBufferView(index, address)
                        },
                        Pk::Transfer => (),
                    }
                }
                super::RootElement::DynamicStorageBufferOffsets { start, end } => {
                    let values = &self.pass.dynamic_storage_buffer_offsets[start..end];

                    for (offset, &value) in values.iter().enumerate() {
                        match self.pass.kind {
                            Pk::Render => unsafe {
                                list.SetGraphicsRoot32BitConstant(index, value, offset as u32)
                            },
                            Pk::Compute => unsafe {
                                list.SetComputeRoot32BitConstant(index, value, offset as u32)
                            },
                            Pk::Transfer => (),
                        }
                    }
                }
                super::RootElement::SamplerHeapDescriptorTable => match self.pass.kind {
                    Pk::Render => unsafe {
                        list.SetGraphicsRootDescriptorTable(
                            index,
                            self.shared.sampler_heap.gpu_descriptor_table(),
                        )
                    },
                    Pk::Compute => unsafe {
                        list.SetComputeRootDescriptorTable(
                            index,
                            self.shared.sampler_heap.gpu_descriptor_table(),
                        )
                    },
                    Pk::Transfer => (),
                },
            }
        }
    }

    fn reset_signature(&mut self, layout: &super::PipelineLayoutShared) {
        if let Some(root_index) = layout.special_constants.as_ref().map(|sc| sc.root_index) {
            self.pass.root_elements[root_index as usize] =
                super::RootElement::SpecialConstants(super::SpecialConstants::default());
        }
        if let Some(root_index) = layout.sampler_heap_root_index {
            self.pass.root_elements[root_index as usize] =
                super::RootElement::SamplerHeapDescriptorTable;
        }
        self.pass.layout = layout.clone();
        self.pass.dirty_root_elements = (1 << layout.total_root_elements) - 1;
    }

    fn write_pass_end_timestamp_if_requested(&mut self) {
        if let Some((query_set_raw, index)) = self.end_of_pass_timer_query.take() {
            use crate::CommandEncoder as _;
            unsafe {
                self.write_timestamp(
                    &crate::dx12::QuerySet {
                        raw: query_set_raw,
                        raw_ty: Direct3D12::D3D12_QUERY_TYPE_TIMESTAMP,
                    },
                    index,
                );
            }
        }
    }

    unsafe fn buf_tex_intermediate<T>(
        &mut self,
        region: crate::BufferTextureCopy,
        tex_fmt: wgt::TextureFormat,
        copy_op: impl FnOnce(&mut Self, &super::Buffer, wgt::BufferSize, crate::BufferTextureCopy) -> T,
    ) -> (T, super::Buffer) {
        let size = {
            let copy_info = region.buffer_layout.get_buffer_texture_copy_info(
                tex_fmt,
                region.texture_base.aspect.map(),
                ®ion.size.into(),
            );
            copy_info.unwrap().bytes_in_copy
        };

        let size = wgt::BufferSize::new(size).unwrap();

        let buffer = {
            let (resource, allocation) =
                super::suballocation::DeviceAllocationContext::from(&*self)
                    .create_buffer(&crate::BufferDescriptor {
                        label: None,
                        size: size.get(),
                        usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST,
                        memory_flags: crate::MemoryFlags::empty(),
                    })
                    .expect(concat!(
                        "internal error: ",
                        "failed to allocate intermediate buffer ",
                        "for offset alignment"
                    ));
            super::Buffer {
                resource,
                size: size.get(),
                allocation,
            }
        };

        let mut region = region;
        region.buffer_layout.offset = 0;

        unsafe {
            self.transition_buffers(
                [crate::BufferBarrier {
                    buffer: &buffer,
                    usage: crate::StateTransition {
                        from: wgt::BufferUses::empty(),
                        to: wgt::BufferUses::COPY_DST,
                    },
                }]
                .into_iter(),
            )
        };

        let t = copy_op(self, &buffer, size, region);

        unsafe {
            self.transition_buffers(
                [crate::BufferBarrier {
                    buffer: &buffer,
                    usage: crate::StateTransition {
                        from: wgt::BufferUses::COPY_DST,
                        to: wgt::BufferUses::COPY_SRC,
                    },
                }]
                .into_iter(),
            )
        };

        (t, buffer)
    }
}

impl crate::CommandEncoder for super::CommandEncoder {
    type A = super::Api;

    unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> {
        let list = loop {
            if let Some(list) = self.free_lists.pop() {
                // TODO: Is an error expected here and should we print it?
                let reset_result = unsafe { list.Reset(&self.allocator, None) };
                if reset_result.is_ok() {
                    break Some(list);
                }
            } else {
                break None;
            }
        };

        let list = if let Some(list) = list {
            list
        } else {
            unsafe {
                self.device.CreateCommandList(
                    0,
                    Direct3D12::D3D12_COMMAND_LIST_TYPE_DIRECT,
                    &self.allocator,
                    None,
                )
            }
            .into_device_result("Create command list")?
        };

        if let Some(label) = label {
            list.set_name(label)?;
        }

        self.list = Some(list);
        self.temp.clear();
        self.pass.clear();
        Ok(())
    }
    unsafe fn discard_encoding(&mut self) {
        if let Some(list) = self.list.take() {
            if unsafe { list.Close() }.is_ok() {
                self.free_lists.push(list);
            }
        }
    }
    unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
        let raw = self.list.take().unwrap();
        unsafe { raw.Close() }.into_device_result("GraphicsCommandList::close")?;
        Ok(super::CommandBuffer { raw })
    }
    unsafe fn reset_all<I: Iterator<Item = super::CommandBuffer>>(&mut self, command_buffers: I) {
        self.intermediate_copy_bufs.clear();
        for cmd_buf in command_buffers {
            self.free_lists.push(cmd_buf.raw);
        }
        if let Err(e) = unsafe { self.allocator.Reset() } {
            log::error!("ID3D12CommandAllocator::Reset() failed with {e}");
        }
    }

    unsafe fn transition_buffers<'a, T>(&mut self, barriers: T)
    where
        T: Iterator<Item = crate::BufferBarrier<'a, super::Buffer>>,
    {
        self.temp.barriers.clear();

        for barrier in barriers {
            let s0 = conv::map_buffer_usage_to_state(barrier.usage.from);
            let s1 = conv::map_buffer_usage_to_state(barrier.usage.to);
            if s0 != s1 {
                let raw = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        Transition: mem::ManuallyDrop::new(
                            Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER {
                                pResource: unsafe {
                                    borrow_interface_temporarily(&barrier.buffer.resource)
                                },
                                Subresource: Direct3D12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
                                StateBefore: s0,
                                StateAfter: s1,
                            },
                        ),
                    },
                };
                self.temp.barriers.push(raw);
            } else if barrier.usage.from == wgt::BufferUses::STORAGE_READ_WRITE
                || barrier.usage.from == wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY
            {
                let raw = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER {
                            pResource: unsafe {
                                borrow_interface_temporarily(&barrier.buffer.resource)
                            },
                        }),
                    },
                };
                self.temp.barriers.push(raw);
            }
        }

        if !self.temp.barriers.is_empty() {
            unsafe {
                self.list
                    .as_ref()
                    .unwrap()
                    .ResourceBarrier(&self.temp.barriers)
            };
        }
    }

    unsafe fn transition_textures<'a, T>(&mut self, barriers: T)
    where
        T: Iterator<Item = crate::TextureBarrier<'a, super::Texture>>,
    {
        self.temp.barriers.clear();

        for barrier in barriers {
            let s0 = conv::map_texture_usage_to_state(barrier.usage.from);
            let s1 = conv::map_texture_usage_to_state(barrier.usage.to);
            if s0 != s1 {
                let mut raw = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        Transition: mem::ManuallyDrop::new(
                            Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER {
                                pResource: unsafe {
                                    borrow_interface_temporarily(&barrier.texture.resource)
                                },
                                Subresource: Direct3D12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
                                StateBefore: s0,
                                StateAfter: s1,
                            },
                        ),
                    },
                };

                let tex_mip_level_count = barrier.texture.mip_level_count;
                let tex_array_layer_count = barrier.texture.array_layer_count();

                if barrier.range.is_full_resource(
                    barrier.texture.format,
                    tex_mip_level_count,
                    tex_array_layer_count,
                ) {
                    // Only one barrier if it affects the whole image.
                    self.temp.barriers.push(raw);
                } else {
                    // Selected texture aspect is relevant if the texture format has both depth _and_ stencil aspects.
                    let planes = if barrier.texture.format.is_combined_depth_stencil_format() {
                        match barrier.range.aspect {
                            wgt::TextureAspect::All => 0..2,
                            wgt::TextureAspect::DepthOnly => 0..1,
                            wgt::TextureAspect::StencilOnly => 1..2,
                            _ => unreachable!(),
                        }
                    } else if let Some(planes) = barrier.texture.format.planes() {
                        match barrier.range.aspect {
                            wgt::TextureAspect::All => 0..planes,
                            wgt::TextureAspect::Plane0 => 0..1,
                            wgt::TextureAspect::Plane1 => 1..2,
                            wgt::TextureAspect::Plane2 => 2..3,
                            _ => unreachable!(),
                        }
                    } else {
                        match barrier.texture.format {
                            wgt::TextureFormat::Stencil8 => 1..2,
                            wgt::TextureFormat::Depth24Plus => 0..2, // TODO: investigate why tests fail if we set this to 0..1
                            _ => 0..1,
                        }
                    };

                    for mip_level in barrier.range.mip_range(tex_mip_level_count) {
                        for array_layer in barrier.range.layer_range(tex_array_layer_count) {
                            for plane in planes.clone() {
                                unsafe { &mut *raw.Anonymous.Transition }.Subresource = barrier
                                    .texture
                                    .calc_subresource(mip_level, array_layer, plane);
                                self.temp.barriers.push(raw.clone());
                            }
                        }
                    }
                }
            } else if barrier.usage.from == wgt::TextureUses::STORAGE_READ_WRITE {
                let raw = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER {
                            pResource: unsafe {
                                borrow_interface_temporarily(&barrier.texture.resource)
                            },
                        }),
                    },
                };
                self.temp.barriers.push(raw);
            }
        }

        if !self.temp.barriers.is_empty() {
            unsafe {
                self.list
                    .as_ref()
                    .unwrap()
                    .ResourceBarrier(&self.temp.barriers)
            };
        }
    }

    unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) {
        let list = self.list.as_ref().unwrap();
        let mut offset = range.start;
        while offset < range.end {
            let size = super::ZERO_BUFFER_SIZE.min(range.end - offset);
            unsafe {
                list.CopyBufferRegion(&buffer.resource, offset, &self.shared.zero_buffer, 0, size)
            };
            offset += size;
        }
    }

    unsafe fn copy_buffer_to_buffer<T>(
        &mut self,
        src: &super::Buffer,
        dst: &super::Buffer,
        regions: T,
    ) where
        T: Iterator<Item = crate::BufferCopy>,
    {
        let list = self.list.as_ref().unwrap();
        for r in regions {
            unsafe {
                list.CopyBufferRegion(
                    &dst.resource,
                    r.dst_offset,
                    &src.resource,
                    r.src_offset,
                    r.size.get(),
                )
            };
        }
    }

    unsafe fn copy_texture_to_texture<T>(
        &mut self,
        src: &super::Texture,
        _src_usage: wgt::TextureUses,
        dst: &super::Texture,
        regions: T,
    ) where
        T: Iterator<Item = crate::TextureCopy>,
    {
        let list = self.list.as_ref().unwrap();

        for r in regions {
            let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&src.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    SubresourceIndex: src.calc_subresource_for_copy(&r.src_base),
                },
            };
            let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&dst.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    SubresourceIndex: dst.calc_subresource_for_copy(&r.dst_base),
                },
            };

            let src_box = make_box(&r.src_base.origin, &r.size);

            unsafe {
                list.CopyTextureRegion(
                    &dst_location,
                    r.dst_base.origin.x,
                    r.dst_base.origin.y,
                    r.dst_base.origin.z,
                    &src_location,
                    Some(&src_box),
                )
            };
        }
    }

    unsafe fn copy_buffer_to_texture<T>(
        &mut self,
        src: &super::Buffer,
        dst: &super::Texture,
        regions: T,
    ) where
        T: Iterator<Item = crate::BufferTextureCopy>,
    {
        let offset_alignment = self.shared.private_caps.texture_data_placement_alignment();

        for naive_copy_region in regions {
            let is_offset_aligned = naive_copy_region.buffer_layout.offset % offset_alignment == 0;
            let (final_copy_region, src) = if is_offset_aligned {
                (naive_copy_region, src)
            } else {
                let (intermediate_to_dst_region, intermediate_buf) = unsafe {
                    let src_offset = naive_copy_region.buffer_layout.offset;
                    self.buf_tex_intermediate(
                        naive_copy_region,
                        dst.format,
                        |this, buf, size, intermediate_to_dst_region| {
                            let layout = crate::BufferCopy {
                                src_offset,
                                dst_offset: 0,
                                size,
                            };
                            this.copy_buffer_to_buffer(src, buf, [layout].into_iter());
                            intermediate_to_dst_region
                        },
                    )
                };
                self.intermediate_copy_bufs.push(intermediate_buf);
                let intermediate_buf = self.intermediate_copy_bufs.last().unwrap();
                (intermediate_to_dst_region, intermediate_buf)
            };

            let list = self.list.as_ref().unwrap();

            let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&src.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    PlacedFootprint: final_copy_region.to_subresource_footprint(dst.format),
                },
            };
            let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&dst.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    SubresourceIndex: dst
                        .calc_subresource_for_copy(&final_copy_region.texture_base),
                },
            };

            let src_box = make_box(&wgt::Origin3d::ZERO, &final_copy_region.size);
            unsafe {
                list.CopyTextureRegion(
                    &dst_location,
                    final_copy_region.texture_base.origin.x,
                    final_copy_region.texture_base.origin.y,
                    final_copy_region.texture_base.origin.z,
                    &src_location,
                    Some(&src_box),
                )
            };
        }
    }

    unsafe fn copy_texture_to_buffer<T>(
        &mut self,
        src: &super::Texture,
        _src_usage: wgt::TextureUses,
        dst: &super::Buffer,
        regions: T,
    ) where
        T: Iterator<Item = crate::BufferTextureCopy>,
    {
        let copy_aligned = |this: &mut Self,
                            src: &super::Texture,
                            dst: &super::Buffer,
                            r: crate::BufferTextureCopy| {
            let list = this.list.as_ref().unwrap();

            let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&src.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    SubresourceIndex: src.calc_subresource_for_copy(&r.texture_base),
                },
            };
            let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION {
                pResource: unsafe { borrow_interface_temporarily(&dst.resource) },
                Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
                Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 {
                    PlacedFootprint: r.to_subresource_footprint(src.format),
                },
            };

            let src_box = make_box(&r.texture_base.origin, &r.size);
            unsafe {
                list.CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, Some(&src_box))
            };
        };

        let offset_alignment = self.shared.private_caps.texture_data_placement_alignment();

        for r in regions {
            let is_offset_aligned = r.buffer_layout.offset % offset_alignment == 0;
            if is_offset_aligned {
                copy_aligned(self, src, dst, r)
            } else {
                let orig_offset = r.buffer_layout.offset;
                let (intermediate_to_dst_region, src) = unsafe {
                    self.buf_tex_intermediate(
                        r,
                        src.format,
                        |this, buf, size, intermediate_region| {
                            copy_aligned(this, src, buf, intermediate_region);
                            crate::BufferCopy {
                                src_offset: 0,
                                dst_offset: orig_offset,
                                size,
                            }
                        },
                    )
                };

                unsafe {
                    self.copy_buffer_to_buffer(&src, dst, [intermediate_to_dst_region].into_iter());
                }

                self.intermediate_copy_bufs.push(src);
            };
        }
    }

    unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) {
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .BeginQuery(&set.raw, set.raw_ty, index)
        };
    }
    unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) {
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .EndQuery(&set.raw, set.raw_ty, index)
        };
    }
    unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) {
        unsafe {
            self.list.as_ref().unwrap().EndQuery(
                &set.raw,
                Direct3D12::D3D12_QUERY_TYPE_TIMESTAMP,
                index,
            )
        };
    }
    unsafe fn read_acceleration_structure_compact_size(
        &mut self,
        acceleration_structure: &super::AccelerationStructure,
        buf: &super::Buffer,
    ) {
        let list = self
            .list
            .as_ref()
            .unwrap()
            .cast::<Direct3D12::ID3D12GraphicsCommandList4>()
            .unwrap();
        unsafe {
            list.EmitRaytracingAccelerationStructurePostbuildInfo(
                &Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC {
                    DestBuffer: buf.resource.GetGPUVirtualAddress(),
                    InfoType: Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE,
                },
                &[
                    acceleration_structure.resource.GetGPUVirtualAddress()
                ],
            )
        }
    }
    unsafe fn reset_queries(&mut self, _set: &>super::QuerySet, _range: Range<u32>) {
        // nothing to do here
    }
    unsafe fn copy_query_results(
        &mut self,
        set: &super::QuerySet,
        range: Range<u32>,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
        _stride: wgt::BufferSize,
    ) {
        unsafe {
            self.list.as_ref().unwrap().ResolveQueryData(
                &set.raw,
                set.raw_ty,
                range.start,
                range.end - range.start,
                &buffer.resource,
                offset,
            )
        };
    }

    // render

    unsafe fn begin_render_pass(
        &mut self,
        desc: &crate::RenderPassDescriptor<super::QuerySet, super::TextureView>,
    ) -> Result<(), crate::DeviceError> {
        unsafe { self.begin_pass(super::PassKind::Render, desc.label) };

        // Start timestamp if any (before all other commands but after debug marker)
        if let Some(timestamp_writes) = desc.timestamp_writes.as_ref() {
            if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
                unsafe {
                    self.write_timestamp(timestamp_writes.query_set, index);
                }
            }
            self.end_of_pass_timer_query = timestamp_writes
                .end_of_pass_write_index
                .map(|index| (timestamp_writes.query_set.raw.clone(), index));
        }

        let mut color_views =
            [Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 }; crate::MAX_COLOR_ATTACHMENTS];
        let mut rtv_pool = self.rtv_pool.lock();
        for (rtv, cat) in color_views.iter_mut().zip(desc.color_attachments.iter()) {
            if let Some(cat) = cat.as_ref() {
                if cat.target.view.dimension == wgt::TextureViewDimension::D3 {
                    let desc = Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC {
                        Format: cat.target.view.raw_format,
                        ViewDimension: Direct3D12::D3D12_RTV_DIMENSION_TEXTURE3D,
                        Anonymous: Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC_0 {
                            Texture3D: Direct3D12::D3D12_TEX3D_RTV {
                                MipSlice: cat.target.view.mip_slice,
                                FirstWSlice: cat.depth_slice.unwrap(),
                                WSize: 1,
                            },
                        },
                    };
                    let handle = rtv_pool.alloc_handle()?;
                    unsafe {
                        self.device.CreateRenderTargetView(
                            &cat.target.view.texture,
                            Some(&desc),
                            handle.raw,
                        )
                    };
                    *rtv = handle.raw;
                    self.temp_rtv_handles.push(handle);
                } else {
                    *rtv = cat.target.view.handle_rtv.unwrap().raw;
                }
            } else {
                *rtv = self.null_rtv_handle.raw;
            }
        }
        drop(rtv_pool);

        let ds_view = desc.depth_stencil_attachment.as_ref().map(|ds| {
            if ds.target.usage == wgt::TextureUses::DEPTH_STENCIL_WRITE {
                ds.target.view.handle_dsv_rw.as_ref().unwrap().raw
            } else {
                ds.target.view.handle_dsv_ro.as_ref().unwrap().raw
            }
        });

        let list = self.list.as_ref().unwrap();
        unsafe {
            list.OMSetRenderTargets(
                desc.color_attachments.len() as u32,
                Some(color_views.as_ptr()),
                false,
                ds_view.as_ref().map(core::ptr::from_ref),
            )
        };

        self.pass.resolves.clear();
        for (rtv, cat) in color_views.iter().zip(desc.color_attachments.iter()) {
            if let Some(cat) = cat.as_ref() {
                if cat.ops.contains(crate::AttachmentOps::LOAD_CLEAR) {
                    let value = [
                        cat.clear_value.r as f32,
                        cat.clear_value.g as f32,
                        cat.clear_value.b as f32,
                        cat.clear_value.a as f32,
                    ];
                    unsafe { list.ClearRenderTargetView(*rtv, &value, None) };
                }
                if let Some(ref target) = cat.resolve_target {
                    self.pass.resolves.push(super::PassResolve {
                        src: (
                            cat.target.view.texture.clone(),
                            cat.target.view.subresource_index,
                        ),
                        dst: (target.view.texture.clone(), target.view.subresource_index),
                        format: target.view.raw_format,
                    });
                }
            }
        }

        if let Some(ref ds) = desc.depth_stencil_attachment {
            let mut flags = Direct3D12::D3D12_CLEAR_FLAGS::default();
            let aspects = ds.target.view.aspects;
            if ds.depth_ops.contains(crate::AttachmentOps::LOAD_CLEAR)
                && aspects.contains(crate::FormatAspects::DEPTH)
            {
                flags |= Direct3D12::D3D12_CLEAR_FLAG_DEPTH;
            }
            if ds.stencil_ops.contains(crate::AttachmentOps::LOAD_CLEAR)
                && aspects.contains(crate::FormatAspects::STENCIL)
            {
                flags |= Direct3D12::D3D12_CLEAR_FLAG_STENCIL;
            }

            if let Some(ds_view) = ds_view {
                if flags != Direct3D12::D3D12_CLEAR_FLAGS::default() {
                    unsafe {
                        list.ClearDepthStencilView(
                            ds_view,
                            flags,
                            ds.clear_value.0,
                            ds.clear_value.1 as u8,
                            None,
                        )
                    }
                }
            }
        }

        if let Some(multiview_mask) = desc.multiview_mask {
            unsafe {
                list.cast::<Direct3D12::ID3D12GraphicsCommandList2>()
                    .unwrap()
                    .SetViewInstanceMask(multiview_mask.get());
            }
        }

        let raw_vp = Direct3D12::D3D12_VIEWPORT {
            TopLeftX: 0.0,
            TopLeftY: 0.0,
            Width: desc.extent.width as f32,
            Height: desc.extent.height as f32,
            MinDepth: 0.0,
            MaxDepth: 1.0,
        };
        let raw_rect = Foundation::RECT {
            left: 0,
            top: 0,
            right: desc.extent.width as i32,
            bottom: desc.extent.height as i32,
        };
        unsafe { list.RSSetViewports(core::slice::from_ref(&raw_vp)) };
        unsafe { list.RSSetScissorRects(core::slice::from_ref(&raw_rect)) };

        Ok(())
    }

    unsafe fn end_render_pass(&mut self) {
        if !self.pass.resolves.is_empty() {
            let list = self.list.as_ref().unwrap();
            self.temp.barriers.clear();

            // All the targets are expected to be in `COLOR_TARGET` state,
            // but D3D12 has special source/destination states for the resolves.
            for resolve in self.pass.resolves.iter() {
                let barrier = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        // Note: this assumes `D3D12_RESOURCE_STATE_RENDER_TARGET`.
                        // If it's not the case, we can include the `TextureUses` in `PassResolve`.
                        Transition: mem::ManuallyDrop::new(
                            Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER {
                                pResource: unsafe { borrow_interface_temporarily(&resolve.src.0) },
                                Subresource: resolve.src.1,
                                StateBefore: Direct3D12::D3D12_RESOURCE_STATE_RENDER_TARGET,
                                StateAfter: Direct3D12::D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
                            },
                        ),
                    },
                };
                self.temp.barriers.push(barrier);
                let barrier = Direct3D12::D3D12_RESOURCE_BARRIER {
                    Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
                    Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                    Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                        // Note: this assumes `D3D12_RESOURCE_STATE_RENDER_TARGET`.
                        // If it's not the case, we can include the `TextureUses` in `PassResolve`.
                        Transition: mem::ManuallyDrop::new(
                            Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER {
                                pResource: unsafe { borrow_interface_temporarily(&resolve.dst.0) },
                                Subresource: resolve.dst.1,
                                StateBefore: Direct3D12::D3D12_RESOURCE_STATE_RENDER_TARGET,
                                StateAfter: Direct3D12::D3D12_RESOURCE_STATE_RESOLVE_DEST,
                            },
                        ),
                    },
                };
                self.temp.barriers.push(barrier);
            }

            if !self.temp.barriers.is_empty() {
                profiling::scope!("ID3D12GraphicsCommandList::ResourceBarrier");
                unsafe { list.ResourceBarrier(&self.temp.barriers) };
            }

            for resolve in self.pass.resolves.iter() {
                profiling::scope!("ID3D12GraphicsCommandList::ResolveSubresource");
                unsafe {
                    list.ResolveSubresource(
                        &resolve.dst.0,
                        resolve.dst.1,
                        &resolve.src.0,
                        resolve.src.1,
                        resolve.format,
                    )
                };
            }

            // Flip all the barriers to reverse, back into `COLOR_TARGET`.
            for barrier in self.temp.barriers.iter_mut() {
                let transition = unsafe { &mut *barrier.Anonymous.Transition };
                mem::swap(&mut transition.StateBefore, &mut transition.StateAfter);
            }
            if !self.temp.barriers.is_empty() {
                profiling::scope!("ID3D12GraphicsCommandList::ResourceBarrier");
                unsafe { list.ResourceBarrier(&self.temp.barriers) };
            }
        }

        self.write_pass_end_timestamp_if_requested();

        unsafe { self.end_pass() };
    }

    unsafe fn set_bind_group(
        &mut self,
        layout: &super::PipelineLayout,
        index: u32,
        group: &super::BindGroup,
        dynamic_offsets: &[wgt::DynamicOffset],
    ) {
        let info = layout.bind_group_infos[index as usize].as_ref().unwrap();
        let mut root_index = info.base_root_index as usize;

        // Bind CBV/SRC/UAV descriptor tables
        if info.tables.contains(super::TableTypes::SRV_CBV_UAV) {
            self.pass.root_elements[root_index] =
                super::RootElement::DescriptorTable(group.handle_views.unwrap().gpu);
            root_index += 1;
        }

        let mut offsets_index = 0;
        if let Some(dynamic_storage_buffer_offsets) = info.dynamic_storage_buffer_offsets.as_ref() {
            let root_index = dynamic_storage_buffer_offsets.root_index;
            let range = &dynamic_storage_buffer_offsets.range;

            if range.end > self.pass.dynamic_storage_buffer_offsets.len() {
                self.pass
                    .dynamic_storage_buffer_offsets
                    .resize(range.end, 0);
            }

            offsets_index += range.start;

            self.pass.root_elements[root_index as usize] =
                super::RootElement::DynamicStorageBufferOffsets {
                    start: range.start,
                    end: range.end,
                };

            if self.pass.layout.signature == layout.shared.signature {
                self.pass.dirty_root_elements |= 1 << root_index;
            } else {
                // D3D12 requires full reset on signature change
                // but we don't reset it here since it will be reset below
            };
        }

        // Bind root descriptors for dynamic uniform buffers
        // or set root constants for offsets of dynamic storage buffers
        for (&dynamic_buffer, &offset) in group.dynamic_buffers.iter().zip(dynamic_offsets) {
            match dynamic_buffer {
                super::DynamicBuffer::Uniform(gpu_base) => {
                    self.pass.root_elements[root_index] =
                        super::RootElement::DynamicUniformBuffer {
                            address: Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE {
                                ptr: gpu_base.ptr + offset as u64,
                            },
                        };
                    root_index += 1;
                }
                super::DynamicBuffer::Storage => {
                    self.pass.dynamic_storage_buffer_offsets[offsets_index] = offset;
                    offsets_index += 1;
                }
            }
        }

        if self.pass.layout.signature == layout.shared.signature {
            self.pass.dirty_root_elements |= (1 << root_index) - (1 << info.base_root_index);
        } else {
            // D3D12 requires full reset on signature change
            self.reset_signature(&layout.shared);
        };
    }
    unsafe fn set_immediates(
        &mut self,
        layout: &super::PipelineLayout,
        offset_bytes: u32,
        data: &[u32],
    ) {
        let offset_words = offset_bytes as usize / 4;

        let info = layout.shared.immediates_info.as_ref().unwrap();

        self.pass.root_elements[info.root_index as usize] = super::RootElement::Immediates;

        self.pass.immediates[offset_words..(offset_words + data.len())].copy_from_slice(data);

        if self.pass.layout.signature == layout.shared.signature {
            self.pass.dirty_root_elements |= 1 << info.root_index;
        } else {
            // D3D12 requires full reset on signature change
            self.reset_signature(&layout.shared);
        };
    }

    unsafe fn insert_debug_marker(&mut self, label: &str) {
        let (wide_label, size) = self.temp.prepare_marker(label);
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .SetMarker(0, Some(wide_label.as_ptr().cast()), size)
        };
    }
    unsafe fn begin_debug_marker(&mut self, group_label: &str) {
        let (wide_label, size) = self.temp.prepare_marker(group_label);
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .BeginEvent(0, Some(wide_label.as_ptr().cast()), size)
        };
    }
    unsafe fn end_debug_marker(&mut self) {
        unsafe { self.list.as_ref().unwrap().EndEvent() }
    }

    unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) {
        let list = self.list.clone().unwrap();

        if self.pass.layout.signature != pipeline.layout.signature {
            // D3D12 requires full reset on signature change
            unsafe { list.SetGraphicsRootSignature(pipeline.layout.signature.as_ref()) };
            self.reset_signature(&pipeline.layout);
        };

        unsafe { list.SetPipelineState(&pipeline.raw) };
        unsafe { list.IASetPrimitiveTopology(pipeline.topology) };

        for (index, (vb, &stride)) in self
            .pass
            .vertex_buffers
            .iter_mut()
            .zip(pipeline.vertex_strides.iter())
            .enumerate()
        {
            if let Some(stride) = stride {
                if vb.StrideInBytes != stride {
                    vb.StrideInBytes = stride;
                    self.pass.dirty_vertex_buffers |= 1 << index;
                }
            }
        }
    }

    unsafe fn set_index_buffer<'a>(
        &mut self,
        binding: crate::BufferBinding<'a, super::Buffer>,
        format: wgt::IndexFormat,
    ) {
        let ibv = Direct3D12::D3D12_INDEX_BUFFER_VIEW {
            BufferLocation: binding.resolve_address(),
            SizeInBytes: binding.resolve_size().try_into().unwrap(),
            Format: auxil::dxgi::conv::map_index_format(format),
        };

        unsafe { self.list.as_ref().unwrap().IASetIndexBuffer(Some(&ibv)) }
    }
    unsafe fn set_vertex_buffer<'a>(
        &mut self,
        index: u32,
        binding: crate::BufferBinding<'a, super::Buffer>,
    ) {
        let vb = &mut self.pass.vertex_buffers[index as usize];
        vb.BufferLocation = binding.resolve_address();
        vb.SizeInBytes = binding.resolve_size().try_into().unwrap();
        self.pass.dirty_vertex_buffers |= 1 << index;
    }

    unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth_range: Range<f32>) {
        let raw_vp = Direct3D12::D3D12_VIEWPORT {
            TopLeftX: rect.x,
            TopLeftY: rect.y,
            Width: rect.w,
            Height: rect.h,
            MinDepth: depth_range.start,
            MaxDepth: depth_range.end,
        };
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .RSSetViewports(core::slice::from_ref(&raw_vp))
        }
    }
    unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {
        let raw_rect = Foundation::RECT {
            left: rect.x as i32,
            top: rect.y as i32,
            right: (rect.x + rect.w) as i32,
            bottom: (rect.y + rect.h) as i32,
        };
        unsafe {
            self.list
                .as_ref()
                .unwrap()
                .RSSetScissorRects(core::slice::from_ref(&raw_rect))
        }
    }
    unsafe fn set_stencil_reference(&mut self, value: u32) {
        unsafe { self.list.as_ref().unwrap().OMSetStencilRef(value) }
    }
    unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) {
        unsafe { self.list.as_ref().unwrap().OMSetBlendFactor(Some(color)) }
    }

    unsafe fn draw(
        &mut self,
        first_vertex: u32,
        vertex_count: u32,
        first_instance: u32,
        instance_count: u32,
    ) {
        unsafe { self.prepare_draw(first_vertex as i32, first_instance) };
        unsafe {
            self.list.as_ref().unwrap().DrawInstanced(
                vertex_count,
                instance_count,
                first_vertex,
                first_instance,
            )
        }
    }
    unsafe fn draw_indexed(
        &mut self,
        first_index: u32,
        index_count: u32,
        base_vertex: i32,
        first_instance: u32,
        instance_count: u32,
    ) {
        unsafe { self.prepare_draw(base_vertex, first_instance) };
        unsafe {
            self.list.as_ref().unwrap().DrawIndexedInstanced(
                index_count,
                instance_count,
                first_index,
                base_vertex,
                first_instance,
            )
        }
    }
    unsafe fn draw_mesh_tasks(
        &mut self,
        group_count_x: u32,
        group_count_y: u32,
        group_count_z: u32,
    ) {
        self.prepare_dispatch([group_count_x, group_count_y, group_count_z]);
        let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
            self.list.as_ref().unwrap().cast().unwrap();
        unsafe {
            cmd_list6.DispatchMesh(group_count_x, group_count_y, group_count_z);
        }
    }
    unsafe fn draw_indirect(
        &mut self,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
        draw_count: u32,
    ) {
        if self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .is_some()
        {
            unsafe { self.prepare_vertex_buffers() };
            self.update_root_elements();
        } else {
            unsafe { self.prepare_draw(0, 0) };
        }

        let cmd_signature = &self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .unwrap_or_else(|| &self.shared.cmd_signatures)
            .draw;
        unsafe {
            self.list.as_ref().unwrap().ExecuteIndirect(
                cmd_signature,
                draw_count,
                &buffer.resource,
                offset,
                None,
                0,
            )
        }
    }
    unsafe fn draw_indexed_indirect(
        &mut self,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
        draw_count: u32,
    ) {
        if self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .is_some()
        {
            unsafe { self.prepare_vertex_buffers() };
            self.update_root_elements();
        } else {
            unsafe { self.prepare_draw(0, 0) };
        }

        let cmd_signature = &self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .unwrap_or_else(|| &self.shared.cmd_signatures)
            .draw_indexed;
        unsafe {
            self.list.as_ref().unwrap().ExecuteIndirect(
                cmd_signature,
                draw_count,
                &buffer.resource,
                offset,
                None,
                0,
            )
        }
    }
    unsafe fn draw_mesh_tasks_indirect(
        &mut self,
        buffer: &<Self::A as crate::Api>::Buffer,
        offset: wgt::BufferAddress,
        draw_count: u32,
    ) {
        self.prepare_dispatch([0; 3]);
        let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
            self.list.as_ref().unwrap().cast().unwrap();
        let Some(cmd_signature) = &self.shared.cmd_signatures.draw_mesh else {
            panic!("Feature `MESH_SHADING` not enabled");
        };
        unsafe {
            cmd_list6.ExecuteIndirect(cmd_signature, draw_count, &buffer.resource, offset, None, 0);
        }
    }
    unsafe fn draw_indirect_count(
        &mut self,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
        count_buffer: &super::Buffer,
        count_offset: wgt::BufferAddress,
        max_count: u32,
    ) {
        unsafe { self.prepare_draw(0, 0) };
        unsafe {
            self.list.as_ref().unwrap().ExecuteIndirect(
                &self.shared.cmd_signatures.draw,
                max_count,
                &buffer.resource,
                offset,
                &count_buffer.resource,
                count_offset,
            )
        }
    }
    unsafe fn draw_indexed_indirect_count(
        &mut self,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
        count_buffer: &super::Buffer,
        count_offset: wgt::BufferAddress,
        max_count: u32,
    ) {
        unsafe { self.prepare_draw(0, 0) };
        unsafe {
            self.list.as_ref().unwrap().ExecuteIndirect(
                &self.shared.cmd_signatures.draw_indexed,
                max_count,
                &buffer.resource,
                offset,
                &count_buffer.resource,
                count_offset,
            )
        }
    }
    unsafe fn draw_mesh_tasks_indirect_count(
        &mut self,
        buffer: &<Self::A as crate::Api>::Buffer,
        offset: wgt::BufferAddress,
        count_buffer: &<Self::A as crate::Api>::Buffer,
        count_offset: wgt::BufferAddress,
        max_count: u32,
    ) {
        self.prepare_dispatch([0; 3]);
        let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 =
            self.list.as_ref().unwrap().cast().unwrap();
        let Some(ref command_signature) = self.shared.cmd_signatures.draw_mesh else {
            panic!("Feature `MESH_SHADING` not enabled");
        };
        unsafe {
            cmd_list6.ExecuteIndirect(
                command_signature,
                max_count,
                &buffer.resource,
                offset,
                &count_buffer.resource,
                count_offset,
            );
        }
    }

    // compute

    unsafe fn begin_compute_pass<'a>(
        &mut self,
        desc: &crate::ComputePassDescriptor<'a, super::QuerySet>,
    ) {
        unsafe { self.begin_pass(super::PassKind::Compute, desc.label) };

        if let Some(timestamp_writes) = desc.timestamp_writes.as_ref() {
            if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
                unsafe {
                    self.write_timestamp(timestamp_writes.query_set, index);
                }
            }
            self.end_of_pass_timer_query = timestamp_writes
                .end_of_pass_write_index
                .map(|index| (timestamp_writes.query_set.raw.clone(), index));
        }
    }
    unsafe fn end_compute_pass(&mut self) {
        self.write_pass_end_timestamp_if_requested();
        unsafe { self.end_pass() };
    }

    unsafe fn set_compute_pipeline(&mut self, pipeline: &'color:red'>super::ComputePipeline) {
        let list = self.list.clone().unwrap();

        if self.pass.layout.signature != pipeline.layout.signature {
            // D3D12 requires full reset on signature change
            unsafe { list.SetComputeRootSignature(pipeline.layout.signature.as_ref()) };
            self.reset_signature(&pipeline.layout);
        };

        unsafe { list.SetPipelineState(&pipeline.raw) }
    }

    unsafe fn dispatch_workgroups(&mut self, count @ [x, y, z]: [u32; 3]) {
        self.prepare_dispatch(count);
        unsafe { self.list.as_ref().unwrap().Dispatch(x, y, z) }
    }

    unsafe fn dispatch_workgroups_indirect(
        &mut self,
        buffer: &super::Buffer,
        offset: wgt::BufferAddress,
    ) {
        if self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .is_some()
        {
            self.update_root_elements();
        } else {
            self.prepare_dispatch([0; 3]);
        }

        let cmd_signature = &self
            .pass
            .layout
            .special_constants
            .as_ref()
            .and_then(|sc| sc.indirect_cmd_signatures.as_ref())
            .unwrap_or_else(|| &self.shared.cmd_signatures)
            .dispatch;
        unsafe {
            self.list.as_ref().unwrap().ExecuteIndirect(
                cmd_signature,
                1,
                &buffer.resource,
                offset,
                None,
                0,
            )
        }
    }

    unsafe fn build_acceleration_structures<'a, T>(
        &mut self,
        _descriptor_count: u32,
        descriptors: T,
    ) where
        super::Api: 'a,
        T: IntoIterator<
            Item = crate::BuildAccelerationStructureDescriptor<
                'a,
                super::Buffer,
                super::AccelerationStructure,
            >,
        >,
    {
        // Implement using `BuildRaytracingAccelerationStructure`:
        // https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#buildraytracingaccelerationstructure
        let list = self
            .list
            .as_ref()
            .unwrap()
            .cast::<Direct3D12::ID3D12GraphicsCommandList4>()
            .unwrap();
        for descriptor in descriptors {
            // TODO: This is the same as getting build sizes apart from requiring buffers, should this be de-duped?
            let mut geometry_desc;
            let ty;
            let inputs0;
            let num_desc;
            match descriptor.entries {
                AccelerationStructureEntries::Instances(instances) => {
                    let desc_address = unsafe {
                        instances
                            .buffer
                            .expect("needs buffer to build")
                            .resource
                            .GetGPUVirtualAddress()
                    } + instances.offset as u64;
                    ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
                    inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 {
                        InstanceDescs: desc_address,
                    };
                    num_desc = instances.count;
                }
                AccelerationStructureEntries::Triangles(triangles) => {
                    geometry_desc = Vec::with_capacity(triangles.len());
                    for triangle in triangles {
                        let transform_address =
                            triangle.transform.as_ref().map_or(0, |transform| unsafe {
                                transform.buffer.resource.GetGPUVirtualAddress()
                                    + transform.offset as u64
                            });
                        let index_format = triangle
                            .indices
                            .as_ref()
                            .map_or(Dxgi::Common::DXGI_FORMAT_UNKNOWN, |indices| {
                                auxil::dxgi::conv::map_index_format(indices.format)
                            });
                        let vertex_format =
                            auxil::dxgi::conv::map_vertex_format(triangle.vertex_format);
                        let index_count =
                            triangle.indices.as_ref().map_or(0, |indices| indices.count);
                        let index_address = triangle.indices.as_ref().map_or(0, |indices| unsafe {
                            indices
                                .buffer
                                .expect("needs buffer to build")
                                .resource
                                .GetGPUVirtualAddress()
                                + indices.offset as u64
                        });
                        let vertex_address = unsafe {
                            triangle
                                .vertex_buffer
                                .expect("needs buffer to build")
                                .resource
                                .GetGPUVirtualAddress()
                                + (triangle.first_vertex as u64 * triangle.vertex_stride)
                        };

                        let triangle_desc = Direct3D12::D3D12_RAYTRACING_GEOMETRY_TRIANGLES_DESC {
                            Transform3x4: transform_address,
                            IndexFormat: index_format,
                            VertexFormat: vertex_format,
                            IndexCount: index_count,
                            VertexCount: triangle.vertex_count,
                            IndexBuffer: index_address,
                            VertexBuffer: Direct3D12::D3D12_GPU_VIRTUAL_ADDRESS_AND_STRIDE {
                                StartAddress: vertex_address,
                                StrideInBytes: triangle.vertex_stride,
                            },
                        };

                        geometry_desc.push(Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC {
                            Type: Direct3D12::D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES,
                            Flags: conv::map_acceleration_structure_geometry_flags(triangle.flags),
                            Anonymous: Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC_0 {
                                Triangles: triangle_desc,
                            },
                        })
                    }
                    ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
                    inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 {
                        pGeometryDescs: geometry_desc.as_ptr(),
                    };
                    num_desc = geometry_desc.len() as u32;
                }
                AccelerationStructureEntries::AABBs(aabbs) => {
                    geometry_desc = Vec::with_capacity(aabbs.len());
                    for aabb in aabbs {
                        let aabb_address = unsafe {
                            aabb.buffer
                                .expect("needs buffer to build")
                                .resource
                                .GetGPUVirtualAddress()
                                + (aabb.offset as u64 * aabb.stride)
                        };

                        let aabb_desc = Direct3D12::D3D12_RAYTRACING_GEOMETRY_AABBS_DESC {
                            AABBCount: aabb.count as u64,
                            AABBs: Direct3D12::D3D12_GPU_VIRTUAL_ADDRESS_AND_STRIDE {
                                StartAddress: aabb_address,
                                StrideInBytes: aabb.stride,
                            },
                        };

                        geometry_desc.push(Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC {
                            Type: Direct3D12::D3D12_RAYTRACING_GEOMETRY_TYPE_PROCEDURAL_PRIMITIVE_AABBS,
                            Flags: conv::map_acceleration_structure_geometry_flags(aabb.flags),
                            Anonymous: Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC_0 {
                                AABBs: aabb_desc,
                            },
                        })
                    }
                    ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
                    inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 {
                        pGeometryDescs: geometry_desc.as_ptr(),
                    };
                    num_desc = geometry_desc.len() as u32;
                }
            };
            let acceleration_structure_inputs =
                Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS {
                    Type: ty,
                    Flags: conv::map_acceleration_structure_build_flags(
                        descriptor.flags,
                        Some(descriptor.mode),
                    ),
                    NumDescs: num_desc,
                    DescsLayout: Direct3D12::D3D12_ELEMENTS_LAYOUT_ARRAY,
                    Anonymous: inputs0,
                };

            let dst_acceleration_structure_address = unsafe {
                descriptor
                    .destination_acceleration_structure
                    .resource
                    .GetGPUVirtualAddress()
            };
            let src_acceleration_structure_address = descriptor
                .source_acceleration_structure
                .as_ref()
                .map_or(0, |source| unsafe {
                    source.resource.GetGPUVirtualAddress()
                });
            let scratch_address = unsafe {
                descriptor.scratch_buffer.resource.GetGPUVirtualAddress()
                    + descriptor.scratch_buffer_offset
            };

            let desc = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC {
                DestAccelerationStructureData: dst_acceleration_structure_address,
                Inputs: acceleration_structure_inputs,
                SourceAccelerationStructureData: src_acceleration_structure_address,
                ScratchAccelerationStructureData: scratch_address,
            };
            unsafe { list.BuildRaytracingAccelerationStructure(&desc, None) };
        }
    }

    unsafe fn place_acceleration_structure_barrier(
        &mut self,
        _barriers: crate::AccelerationStructureBarrier,
    ) {
        // TODO: This is not very optimal, we should be using [enhanced barriers](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html) if possible
        let list = self
            .list
            .as_ref()
            .unwrap()
            .cast::<Direct3D12::ID3D12GraphicsCommandList4>()
            .unwrap();
        unsafe {
            list.ResourceBarrier(&[Direct3D12::D3D12_RESOURCE_BARRIER {
                Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV,
                Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE,
                Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 {
                    UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER {
                        pResource: Default::default(),
                    }),
                },
            }])
        }
    }

    unsafe fn copy_acceleration_structure_to_acceleration_structure(
        &mut self,
        src: &super::AccelerationStructure,
        dst: &super::AccelerationStructure,
        copy: wgt::AccelerationStructureCopy,
    ) {
        let list = self
            .list
            .as_ref()
            .unwrap()
            .cast::<Direct3D12::ID3D12GraphicsCommandList4>()
            .unwrap();
        unsafe {
            list.CopyRaytracingAccelerationStructure(
                dst.resource.GetGPUVirtualAddress(),
                src.resource.GetGPUVirtualAddress(),
                conv::map_acceleration_structure_copy_mode(copy),
            )
        }
    }

    unsafe fn set_acceleration_structure_dependencies(
        _command_buffers: &[&super::CommandBuffer],
        _dependencies: &[&super::AccelerationStructure],
    ) {
    }
}

Messung V0.5 in Prozent
C=99 H=69 G=85

¤ Dauer der Verarbeitung: 0.39 Sekunden  (vorverarbeitet am  2026-09-30) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.