Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/wgpu_bindings/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  command.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::{id, server::Global, RawString};
use std::{borrow::Cow, ffi, slice};
use wgc::{
    command::{
        ComputePassDescriptor, PassTimestampWrites, RenderPassColorAttachment,
        RenderPassDepthStencilAttachment, RenderPassDescriptor,
    },
    id::CommandEncoderId,
};
use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat};

use arrayvec::ArrayVec;
use serde::{Deserialize, Serialize};

/// A stream of commands for a render pass or compute pass.
///
/// This also contains side tables referred to by certain commands,
/// like dynamic offsets for [`SetBindGroup`] or string data for
/// [`InsertDebugMarker`].
///
/// Render passes use `BasePass<RenderCommand>`, whereas compute
/// passes use `BasePass<ComputeCommand>`.
///
/// [`SetBindGroup`]: RenderCommand::SetBindGroup
/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
#[doc(hidden)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct BasePass<C> {
    pub label: Option<String>,

    /// The stream of commands.
    pub commands: Vec<C>,

    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
    ///
    /// Each successive `SetBindGroup` consumes the next
    /// [`num_dynamic_offsets`] values from this list.
    pub dynamic_offsets: Vec<wgt::DynamicOffset>,

    /// Strings used by debug instructions.
    ///
    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
    /// instruction consumes the next `len` bytes from this vector.
    pub string_data: Vec<u8>,
}

#[derive(Deserialize, Serialize)]
pub struct RecordedRenderPass {
    base: BasePass<RenderCommand>,
    color_attachments: ArrayVec<Option<RenderPassColorAttachment>, { wgh::MAX_COLOR_ATTACHMENTS }>,
    depth_stencil_attachment: Option<RenderPassDepthStencilAttachment>,
    timestamp_writes: Option<PassTimestampWrites>,
    occlusion_query_set_id: Option<id::QuerySetId>,
}

impl RecordedRenderPass {
    pub fn new(desc: &RenderPassDescriptor) -> Self {
        Self {
            base: BasePass {
                label: desc.label.as_ref().map(|cow| cow.to_string()),
                commands: Vec::new(),
                dynamic_offsets: Vec::new(),
                string_data: Vec::new(),
            },
            color_attachments: desc.color_attachments.iter().cloned().collect(),
            depth_stencil_attachment: desc.depth_stencil_attachment.cloned(),
            timestamp_writes: desc.timestamp_writes.cloned(),
            occlusion_query_set_id: desc.occlusion_query_set,
        }
    }
}

#[derive(serde::Deserialize, serde::Serialize)]
pub struct RecordedComputePass {
    base: BasePass<ComputeCommand>,
    timestamp_writes: Option<PassTimestampWrites>,
}

impl RecordedComputePass {
    pub fn new(desc: &ComputePassDescriptor) -> Self {
        Self {
            base: BasePass {
                label: desc.label.as_ref().map(|cow| cow.to_string()),
                commands: Vec::new(),
                dynamic_offsets: Vec::new(),
                string_data: Vec::new(),
            },
            timestamp_writes: desc.timestamp_writes.cloned(),
        }
    }
}

#[doc(hidden)]
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum RenderCommand {
    SetBindGroup {
        index: u32,
        num_dynamic_offsets: usize,
        bind_group_id: Option<id::BindGroupId>,
    },
    SetPipeline(id::RenderPipelineId),
    SetIndexBuffer {
        buffer_id: id::BufferId,
        index_format: wgt::IndexFormat,
        offset: BufferAddress,
        size: Option<BufferSize>,
    },
    SetVertexBuffer {
        slot: u32,
        buffer_id: id::BufferId,
        offset: BufferAddress,
        size: Option<BufferSize>,
    },
    SetBlendConstant(Color),
    SetStencilReference(u32),
    SetViewport {
        x: f32,
        y: f32,
        w: f32,
        h: f32,
        depth_min: f32,
        depth_max: f32,
    },
    SetScissor {
        x: u32,
        y: u32,
        w: u32,
        h: u32,
    },
    Draw {
        vertex_count: u32,
        instance_count: u32,
        first_vertex: u32,
        first_instance: u32,
    },
    DrawIndexed {
        index_count: u32,
        instance_count: u32,
        first_index: u32,
        base_vertex: i32,
        first_instance: u32,
    },
    MultiDrawIndirect {
        buffer_id: id::BufferId,
        offset: BufferAddress,
        /// Count of `None` represents a non-multi call.
        count: Option<u32>,
        indexed: bool,
    },
    MultiDrawIndirectCount {
        buffer_id: id::BufferId,
        offset: BufferAddress,
        count_buffer_id: id::BufferId,
        count_buffer_offset: BufferAddress,
        max_count: u32,
        indexed: bool,
    },
    PushDebugGroup {
        color: u32,
        len: usize,
    },
    PopDebugGroup,
    InsertDebugMarker {
        color: u32,
        len: usize,
    },
    WriteTimestamp {
        query_set_id: id::QuerySetId,
        query_index: u32,
    },
    BeginOcclusionQuery {
        query_index: u32,
    },
    EndOcclusionQuery,
    BeginPipelineStatisticsQuery {
        query_set_id: id::QuerySetId,
        query_index: u32,
    },
    EndPipelineStatisticsQuery,
    ExecuteBundle(id::RenderBundleId),
}

#[doc(hidden)]
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum ComputeCommand {
    SetBindGroup {
        index: u32,
        num_dynamic_offsets: usize,
        bind_group_id: Option<id::BindGroupId>,
    },
    SetPipeline(id::ComputePipelineId),
    Dispatch([u32; 3]),
    DispatchIndirect {
        buffer_id: id::BufferId,
        offset: wgt::BufferAddress,
    },
    PushDebugGroup {
        color: u32,
        len: usize,
    },
    PopDebugGroup,
    InsertDebugMarker {
        color: u32,
        len: usize,
    },
    WriteTimestamp {
        query_set_id: id::QuerySetId,
        query_index: u32,
    },
    BeginPipelineStatisticsQuery {
        query_set_id: id::QuerySetId,
        query_index: u32,
    },
    EndPipelineStatisticsQuery,
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
/// valid for `offset_length` elements.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_render_pass_set_bind_group(
    pass: &mut RecordedRenderPass,
    index: u32,
    bind_group_id: Option<id::BindGroupId>,
    offsets: *const DynamicOffset,
    offset_length: usize,
) {
    pass.base
        .dynamic_offsets
        .extend_from_slice(unsafe { slice::from_raw_parts(offsets, offset_length) });

    pass.base.commands.push(RenderCommand::SetBindGroup {
        index,
        num_dynamic_offsets: offset_length,
        bind_group_id,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_pipeline(
    pass: &mut RecordedRenderPass,
    pipeline_id: id::RenderPipelineId,
) {
    pass.base
        .commands
        .push(RenderCommand::SetPipeline(pipeline_id));
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_vertex_buffer(
    pass: &mut RecordedRenderPass,
    slot: u32,
    buffer_id: id::BufferId,
    offset: BufferAddress,
    size: Option<BufferSize>,
) {
    pass.base.commands.push(RenderCommand::SetVertexBuffer {
        slot,
        buffer_id,
        offset,
        size,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_index_buffer(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    index_format: IndexFormat,
    offset: BufferAddress,
    size: Option<BufferSize>,
) {
    pass.base.commands.push(RenderCommand::SetIndexBuffer {
        buffer_id,
        index_format,
        offset,
        size,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_blend_constant(
    pass: &mut RecordedRenderPass,
    color: &Color,
) {
    pass.base
        .commands
        .push(RenderCommand::SetBlendConstant(*color));
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_stencil_reference(
    pass: &mut RecordedRenderPass,
    value: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::SetStencilReference(value));
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_viewport(
    pass: &mut RecordedRenderPass,
    x: f32,
    y: f32,
    w: f32,
    h: f32,
    depth_min: f32,
    depth_max: f32,
) {
    pass.base.commands.push(RenderCommand::SetViewport {
        x,
        y,
        w,
        h,
        depth_min,
        depth_max,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_set_scissor_rect(
    pass: &mut RecordedRenderPass,
    x: u32,
    y: u32,
    w: u32,
    h: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::SetScissor { x, y, w, h });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_draw(
    pass: &mut RecordedRenderPass,
    vertex_count: u32,
    instance_count: u32,
    first_vertex: u32,
    first_instance: u32,
) {
    pass.base.commands.push(RenderCommand::Draw {
        vertex_count,
        instance_count,
        first_vertex,
        first_instance,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_draw_indexed(
    pass: &mut RecordedRenderPass,
    index_count: u32,
    instance_count: u32,
    first_index: u32,
    base_vertex: i32,
    first_instance: u32,
) {
    pass.base.commands.push(RenderCommand::DrawIndexed {
        index_count,
        instance_count,
        first_index,
        base_vertex,
        first_instance,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_draw_indirect(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
) {
    pass.base.commands.push(RenderCommand::MultiDrawIndirect {
        buffer_id,
        offset,
        count: None,
        indexed: false,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_draw_indexed_indirect(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
) {
    pass.base.commands.push(RenderCommand::MultiDrawIndirect {
        buffer_id,
        offset,
        count: None,
        indexed: true,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
    count: u32,
) {
    pass.base.commands.push(RenderCommand::MultiDrawIndirect {
        buffer_id,
        offset,
        count: Some(count),
        indexed: false,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
    count: u32,
) {
    pass.base.commands.push(RenderCommand::MultiDrawIndirect {
        buffer_id,
        offset,
        count: Some(count),
        indexed: true,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect_count(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
    count_buffer_id: id::BufferId,
    count_buffer_offset: BufferAddress,
    max_count: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::MultiDrawIndirectCount {
            buffer_id,
            offset,
            count_buffer_id,
            count_buffer_offset,
            max_count,
            indexed: false,
        });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect_count(
    pass: &mut RecordedRenderPass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
    count_buffer_id: id::BufferId,
    count_buffer_offset: BufferAddress,
    max_count: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::MultiDrawIndirectCount {
            buffer_id,
            offset,
            count_buffer_id,
            count_buffer_offset,
            max_count,
            indexed: true,
        });
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given `label`
/// is a valid null-terminated string.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_render_pass_push_debug_group(
    pass: &mut RecordedRenderPass,
    label: RawString,
    color: u32,
) {
    let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes();
    pass.base.string_data.extend_from_slice(bytes);

    pass.base.commands.push(RenderCommand::PushDebugGroup {
        color,
        len: bytes.len(),
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_pop_debug_group(pass: &mut RecordedRenderPass) {
    pass.base.commands.push(RenderCommand::PopDebugGroup);
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given `label`
/// is a valid null-terminated string.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_render_pass_insert_debug_marker(
    pass: &mut RecordedRenderPass,
    label: RawString,
    color: u32,
) {
    let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes();
    pass.base.string_data.extend_from_slice(bytes);

    pass.base.commands.push(RenderCommand::InsertDebugMarker {
        color,
        len: bytes.len(),
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_write_timestamp(
    pass: &mut RecordedRenderPass,
    query_set_id: id::QuerySetId,
    query_index: u32,
) {
    pass.base.commands.push(RenderCommand::WriteTimestamp {
        query_set_id,
        query_index,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_begin_occlusion_query(
    pass: &mut RecordedRenderPass,
    query_index: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::BeginOcclusionQuery { query_index });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_end_occlusion_query(pass: &mut RecordedRenderPass) {
    pass.base.commands.push(RenderCommand::EndOcclusionQuery);
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_begin_pipeline_statistics_query(
    pass: &mut RecordedRenderPass,
    query_set_id: id::QuerySetId,
    query_index: u32,
) {
    pass.base
        .commands
        .push(RenderCommand::BeginPipelineStatisticsQuery {
            query_set_id,
            query_index,
        });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_render_pass_end_pipeline_statistics_query(
    pass: &mut RecordedRenderPass,
) {
    pass.base
        .commands
        .push(RenderCommand::EndPipelineStatisticsQuery);
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
/// valid for `render_bundle_ids_length` elements.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_render_pass_execute_bundles(
    pass: &mut RecordedRenderPass,
    render_bundle_ids: *const id::RenderBundleId,
    render_bundle_ids_length: usize,
) {
    for &bundle_id in unsafe { slice::from_raw_parts(render_bundle_ids, render_bundle_ids_length) }
    {
        pass.base
            .commands
            .push(RenderCommand::ExecuteBundle(bundle_id));
    }
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
/// valid for `offset_length` elements.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_compute_pass_set_bind_group(
    pass: &mut RecordedComputePass,
    index: u32,
    bind_group_id: Option<id::BindGroupId>,
    offsets: *const DynamicOffset,
    offset_length: usize,
) {
    pass.base
        .dynamic_offsets
        .extend_from_slice(unsafe { slice::from_raw_parts(offsets, offset_length) });

    pass.base.commands.push(ComputeCommand::SetBindGroup {
        index,
        num_dynamic_offsets: offset_length,
        bind_group_id,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_set_pipeline(
    pass: &mut RecordedComputePass,
    pipeline_id: id::ComputePipelineId,
) {
    pass.base
        .commands
        .push(ComputeCommand::SetPipeline(pipeline_id));
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups(
    pass: &mut RecordedComputePass,
    groups_x: u32,
    groups_y: u32,
    groups_z: u32,
) {
    pass.base
        .commands
        .push(ComputeCommand::Dispatch([groups_x, groups_y, groups_z]));
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups_indirect(
    pass: &mut RecordedComputePass,
    buffer_id: id::BufferId,
    offset: BufferAddress,
) {
    pass.base
        .commands
        .push(ComputeCommand::DispatchIndirect { buffer_id, offset });
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given `label`
/// is a valid null-terminated string.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_compute_pass_push_debug_group(
    pass: &mut RecordedComputePass,
    label: RawString,
    color: u32,
) {
    let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes();
    pass.base.string_data.extend_from_slice(bytes);

    pass.base.commands.push(ComputeCommand::PushDebugGroup {
        color,
        len: bytes.len(),
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_pop_debug_group(pass: &mut RecordedComputePass) {
    pass.base.commands.push(ComputeCommand::PopDebugGroup);
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given `label`
/// is a valid null-terminated string.
#[no_mangle]
pub unsafe extern "C" fn wgpu_recorded_compute_pass_insert_debug_marker(
    pass: &mut RecordedComputePass,
    label: RawString,
    color: u32,
) {
    let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes();
    pass.base.string_data.extend_from_slice(bytes);

    pass.base.commands.push(ComputeCommand::InsertDebugMarker {
        color,
        len: bytes.len(),
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_write_timestamp(
    pass: &mut RecordedComputePass,
    query_set_id: id::QuerySetId,
    query_index: u32,
) {
    pass.base.commands.push(ComputeCommand::WriteTimestamp {
        query_set_id,
        query_index,
    });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_begin_pipeline_statistics_query(
    pass: &mut RecordedComputePass,
    query_set_id: id::QuerySetId,
    query_index: u32,
) {
    pass.base
        .commands
        .push(ComputeCommand::BeginPipelineStatisticsQuery {
            query_set_id,
            query_index,
        });
}

#[no_mangle]
pub extern "C" fn wgpu_recorded_compute_pass_end_pipeline_statistics_query(
    pass: &mut RecordedComputePass,
) {
    pass.base
        .commands
        .push(ComputeCommand::EndPipelineStatisticsQuery);
}

pub fn replay_render_pass(
    global: &Global,
    id: CommandEncoderId,
    src_pass: &RecordedRenderPass,
    mut error_buf: crate::error::ErrorBuffer,
) {
    let (mut dst_pass, err) = global.command_encoder_create_render_pass(
        id,
        &wgc::command::RenderPassDescriptor {
            label: src_pass.base.label.as_ref().map(|s| s.as_str().into()),
            color_attachments: Cow::Borrowed(&src_pass.color_attachments),
            depth_stencil_attachment: src_pass.depth_stencil_attachment.as_ref(),
            timestamp_writes: src_pass.timestamp_writes.as_ref(),
            occlusion_query_set: src_pass.occlusion_query_set_id,
        },
    );
    if let Some(err) = err {
        error_buf.init(err);
        return;
    }
    match replay_render_pass_impl(global, src_pass, &mut dst_pass) {
        Ok(()) => (),
        Err(err) => {
            error_buf.init(err);
            return;
        }
    };

    match global.render_pass_end(&mut dst_pass) {
        Ok(()) => (),
        Err(err) => error_buf.init(err),
    }
}

pub fn replay_render_pass_impl(
    global: &Global,
    src_pass: &RecordedRenderPass,
    dst_pass: &mut wgc::command::RenderPass,
) -> Result<(), wgc::command::RenderPassError> {
    let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice();
    let mut dynamic_offsets = |len| {
        let offsets;
        (offsets, dynamic_offsets) = dynamic_offsets.split_at(len);
        offsets
    };
    let mut strings = src_pass.base.string_data.as_slice();
    let mut strings = |len| {
        let label;
        (label, strings) = strings.split_at(len);
        label
    };
    for command in &src_pass.base.commands {
        match *command {
            RenderCommand::SetBindGroup {
                index,
                num_dynamic_offsets,
                bind_group_id,
            } => {
                let offsets = dynamic_offsets(num_dynamic_offsets);
                global.render_pass_set_bind_group(dst_pass, index, bind_group_id, offsets)
            }
            RenderCommand::SetPipeline(pipeline_id) => {
                global.render_pass_set_pipeline(dst_pass, pipeline_id)
            }
            RenderCommand::SetIndexBuffer {
                buffer_id,
                index_format,
                offset,
                size,
            } => {
                global.render_pass_set_index_buffer(dst_pass, buffer_id, index_format, offset, size)
            }
            RenderCommand::SetVertexBuffer {
                slot,
                buffer_id,
                offset,
                size,
            } => global.render_pass_set_vertex_buffer(dst_pass, slot, buffer_id, offset, size),
            RenderCommand::SetBlendConstant(color) => {
                global.render_pass_set_blend_constant(dst_pass, color)
            }
            RenderCommand::SetStencilReference(value) => {
                global.render_pass_set_stencil_reference(dst_pass, value)
            }
            RenderCommand::SetViewport {
                x,
                y,
                w,
                h,
                depth_min,
                depth_max,
            } => global.render_pass_set_viewport(dst_pass, x, y, w, h, depth_min, depth_max),
            RenderCommand::SetScissor { x, y, w, h } => {
                global.render_pass_set_scissor_rect(dst_pass, x, y, w, h)
            }
            RenderCommand::Draw {
                vertex_count,
                instance_count,
                first_vertex,
                first_instance,
            } => global.render_pass_draw(
                dst_pass,
                vertex_count,
                instance_count,
                first_vertex,
                first_instance,
            ),
            RenderCommand::DrawIndexed {
                index_count,
                instance_count,
                first_index,
                base_vertex,
                first_instance,
            } => global.render_pass_draw_indexed(
                dst_pass,
                index_count,
                instance_count,
                first_index,
                base_vertex,
                first_instance,
            ),
            RenderCommand::MultiDrawIndirect {
                buffer_id,
                offset,
                count,
                indexed,
            } => match (indexed, count) {
                (false, Some(count)) => {
                    global.render_pass_multi_draw_indirect(dst_pass, buffer_id, offset, count)
                }
                (false, None) => global.render_pass_draw_indirect(dst_pass, buffer_id, offset),
                (true, Some(count)) => global
                    .render_pass_multi_draw_indexed_indirect(dst_pass, buffer_id, offset, count),
                (true, None) => {
                    global.render_pass_draw_indexed_indirect(dst_pass, buffer_id, offset)
                }
            },
            RenderCommand::MultiDrawIndirectCount {
                buffer_id,
                offset,
                count_buffer_id,
                count_buffer_offset,
                max_count,
                indexed,
            } => {
                if indexed {
                    global.render_pass_multi_draw_indexed_indirect_count(
                        dst_pass,
                        buffer_id,
                        offset,
                        count_buffer_id,
                        count_buffer_offset,
                        max_count,
                    )
                } else {
                    global.render_pass_multi_draw_indirect_count(
                        dst_pass,
                        buffer_id,
                        offset,
                        count_buffer_id,
                        count_buffer_offset,
                        max_count,
                    )
                }
            }
            RenderCommand::PushDebugGroup { color, len } => {
                let label = strings(len);
                let label = std::str::from_utf8(label).unwrap();
                global.render_pass_push_debug_group(dst_pass, label, color)
            }
            RenderCommand::PopDebugGroup => global.render_pass_pop_debug_group(dst_pass),
            RenderCommand::InsertDebugMarker { color, len } => {
                let label = strings(len);
                let label = std::str::from_utf8(label).unwrap();
                global.render_pass_insert_debug_marker(dst_pass, label, color)
            }
            RenderCommand::WriteTimestamp {
                query_set_id,
                query_index,
            } => global.render_pass_write_timestamp(dst_pass, query_set_id, query_index),
            RenderCommand::BeginOcclusionQuery { query_index } => {
                global.render_pass_begin_occlusion_query(dst_pass, query_index)
            }
            RenderCommand::EndOcclusionQuery => global.render_pass_end_occlusion_query(dst_pass),
            RenderCommand::BeginPipelineStatisticsQuery {
                query_set_id,
                query_index,
            } => global.render_pass_begin_pipeline_statistics_query(
                dst_pass,
                query_set_id,
                query_index,
            ),
            RenderCommand::EndPipelineStatisticsQuery => {
                global.render_pass_end_pipeline_statistics_query(dst_pass)
            }
            RenderCommand::ExecuteBundle(bundle_id) => {
                global.render_pass_execute_bundles(dst_pass, &[bundle_id])
            }
        }?
    }

    Ok(())
}

pub fn replay_compute_pass(
    global: &Global,
    id: CommandEncoderId,
    src_pass: &RecordedComputePass,
    mut error_buf: crate::error::ErrorBuffer,
) {
    let (mut dst_pass, err) = global.command_encoder_create_compute_pass(
        id,
        &wgc::command::ComputePassDescriptor {
            label: src_pass.base.label.as_ref().map(|s| s.as_str().into()),
            timestamp_writes: src_pass.timestamp_writes.as_ref(),
        },
    );
    if let Some(err) = err {
        error_buf.init(err);
        return;
    }
    if let Err(err) = replay_compute_pass_impl(global, src_pass, &mut dst_pass) {
        error_buf.init(err);
    }
}

fn replay_compute_pass_impl(
    global: &Global,
    src_pass: &RecordedComputePass,
    dst_pass: &mut wgc::command::ComputePass,
) -> Result<(), wgc::command::ComputePassError> {
    let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice();
    let mut dynamic_offsets = |len| {
        let offsets;
        (offsets, dynamic_offsets) = dynamic_offsets.split_at(len);
        offsets
    };
    let mut strings = src_pass.base.string_data.as_slice();
    let mut strings = |len| {
        let label;
        (label, strings) = strings.split_at(len);
        label
    };
    for command in &src_pass.base.commands {
        match *command {
            ComputeCommand::SetBindGroup {
                index,
                num_dynamic_offsets,
                bind_group_id,
            } => {
                let offsets = dynamic_offsets(num_dynamic_offsets);
                global.compute_pass_set_bind_group(dst_pass, index, bind_group_id, offsets)?;
            }
            ComputeCommand::SetPipeline(pipeline_id) => {
                global.compute_pass_set_pipeline(dst_pass, pipeline_id)?;
            }
            ComputeCommand::Dispatch([x, y, z]) => {
                global.compute_pass_dispatch_workgroups(dst_pass, x, y, z)?;
            }
            ComputeCommand::DispatchIndirect { buffer_id, offset } => {
                global.compute_pass_dispatch_workgroups_indirect(dst_pass, buffer_id, offset)?;
            }
            ComputeCommand::PushDebugGroup { color, len } => {
                let label = strings(len);
                let label = std::str::from_utf8(label).unwrap();
                global.compute_pass_push_debug_group(dst_pass, label, color)?;
            }
            ComputeCommand::PopDebugGroup => {
                global.compute_pass_pop_debug_group(dst_pass)?;
            }
            ComputeCommand::InsertDebugMarker { color, len } => {
                let label = strings(len);
                let label = std::str::from_utf8(label).unwrap();
                global.compute_pass_insert_debug_marker(dst_pass, label, color)?;
            }
            ComputeCommand::WriteTimestamp {
                query_set_id,
                query_index,
            } => {
                global.compute_pass_write_timestamp(dst_pass, query_set_id, query_index)?;
            }
            ComputeCommand::BeginPipelineStatisticsQuery {
                query_set_id,
                query_index,
            } => {
                global.compute_pass_begin_pipeline_statistics_query(
                    dst_pass,
                    query_set_id,
                    query_index,
                )?;
            }
            ComputeCommand::EndPipelineStatisticsQuery => {
                global.compute_pass_end_pipeline_statistics_query(dst_pass)?;
            }
        }
    }

    global.compute_pass_end(dst_pass)
}

[ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ]