mepeyew 0.3.5

mepeyew - Small But Powerful Graphics Library
Documentation
use super::*;

impl WebGpuContext {
    pub fn submit(&mut self, submit: Submit, _ext: Option<SubmitExt>) -> GResult<()> {
        submit_transfers(self, &submit)?;

        let command_encoder = self.device.create_command_encoder();

        submit.passes.iter().try_for_each(|pass| {
            match pass {
                SubmitPassType::Render(pass) => submit_pass(self, pass, &command_encoder),
                SubmitPassType::Compute(pass) => {
                    extensions::compute::submit_compute_pass(self, pass, &command_encoder)
                }
            }?;
            Ok(())
        })?;

        let submissions = Array::new();
        submissions.push(&command_encoder.finish());
        self.device.queue().submit(&submissions);

        Ok(())
    }
}

fn submit_transfers(context: &WebGpuContext, submit: &Submit) -> GResult<()> {
    let queue = context.device.queue();
    submit.vbo_transfers.iter().try_for_each(|(vbo_id, data)| {
        let vbo = context.vbos.get(vbo_id.id()).ok_or(gpu_api_err!(
            "webgpu submit transfers vbo id {:?} does not exist",
            vbo_id
        ))?;
        queue.write_buffer_with_u32_and_u8_array(&vbo.buffer, 0, unsafe {
            std::slice::from_raw_parts(
                data.as_ptr() as *const u8,
                data.len() * std::mem::size_of::<VertexBufferElement>(),
            )
        });
        Ok(())
    })?;

    submit.ibo_transfers.iter().try_for_each(|(ibo_id, data)| {
        let ibo = context.ibos.get(ibo_id.id()).ok_or(gpu_api_err!(
            "webgpu submit transfers ibo id {:?} does not exist",
            ibo_id
        ))?;
        queue.write_buffer_with_u32_and_u8_array(&ibo.buffer, 0, unsafe {
            std::slice::from_raw_parts(
                data.as_ptr() as *const u8,
                data.len() * std::mem::size_of::<IndexBufferId>(),
            )
        });
        Ok(())
    })?;

    submit.ubo_transfers.iter().try_for_each(|(ubo_id, data)| {
        let ubo = context.ubos.get(ubo_id.id()).ok_or(gpu_api_err!(
            "webgpu submit transfers ubo id {:?} does not exist",
            ubo_id
        ))?;
        queue.write_buffer_with_u32_and_u8_array(&ubo.buffer, 0, data);
        Ok(())
    })?;

    submit
        .dyn_ubo_transfers
        .iter()
        .try_for_each(|(ubo_id, data, index)| {
            let ubo = context.dyn_ubos.get(ubo_id.id()).ok_or(gpu_api_err!(
                "webgpu submit transfers ubo id {:?} does not exist",
                ubo_id
            ))?;
            ubo.write_buffer(&queue, data, *index);
            Ok(())
        })?;

    Ok(())
}

fn submit_pass(
    context: &WebGpuContext,
    pass_submit: &PassSubmitData,
    command_encoder: &GpuCommandEncoder,
) -> GResult<()> {
    let pass = context
        .compiled_passes
        .get(pass_submit.pass.id())
        .ok_or(gpu_api_err!(
            "webgpu submit pass id {:?} does not exist",
            pass_submit.pass.id()
        ))?;

    pass.original_pass
        .steps
        .iter()
        .zip(pass_submit.steps_datas.iter())
        .enumerate()
        .try_for_each(|(step_idx, (step, step_data))| {
            let color_attachments = Array::new();
            let mut depth_attachment = None;

            let surface_view = context
                .surface
                .as_ref()
                .map(|surface| surface.context.get_current_texture().create_view());

            step.write_colors.iter().try_for_each(|write_color| {
                let attachment =
                    pass.original_pass
                        .attachments
                        .get(write_color.id())
                        .ok_or(gpu_api_err!(
                            "webgpu submit write color {:?} does not exist",
                            write_color
                        ))?;

                let local_attachment_idx = write_color.id();

                let attachment_view = if local_attachment_idx == 0 {
                    surface_view.as_ref().unwrap()
                } else {
                    &pass.attachment_views[local_attachment_idx]
                };

                match &attachment.ty {
                    PassInputType::Color(load_op) => {
                        let op = match load_op {
                            PassInputLoadOpColorType::Clear => GpuLoadOp::Clear,
                            _ => GpuLoadOp::Load,
                        };

                        let mut color_attachment = GpuRenderPassColorAttachment::new(
                            op,
                            if pass.ext.enable_msaa.is_some() {
                                GpuStoreOp::Discard
                            } else {
                                GpuStoreOp::Store
                            },
                            if pass.ext.enable_msaa.is_some() {
                                &pass.resolve_attachment_views[step_idx]
                            } else {
                                attachment_view
                            },
                        );

                        if pass.ext.enable_msaa.is_some() {
                            color_attachment.resolve_target(attachment_view);
                        }

                        if op == GpuLoadOp::Clear {
                            let local_attachment_id =
                                PassLocalAttachment::from_id(attachment.local_attachment_idx);
                            let clear_val = pass_submit
                                .clear_colors
                                .get(&local_attachment_id)
                                .ok_or(gpu_api_err!(
                                    "webpgpu clear color for attachment index {:?} not set",
                                    local_attachment_id
                                ))?;
                            color_attachment.clear_value(&GpuColorDict::new(
                                clear_val.a as f64,
                                clear_val.b as f64,
                                clear_val.g as f64,
                                clear_val.r as f64,
                            ));
                        }
                        color_attachments.push(&color_attachment);
                    }
                    _ => unreachable!(),
                }

                Ok(())
            })?;

            if let Some(write_depth) = step.write_depth {
                let attachment =
                    pass.original_pass
                        .attachments
                        .get(write_depth.id())
                        .ok_or(gpu_api_err!(
                            "webgpu submit write depth {:?} does not exist",
                            write_depth
                        ))?;

                let local_attachment_idx = write_depth.id();
                let mut depth_stencil_attachment = GpuRenderPassDepthStencilAttachment::new(
                    &pass.attachment_views[local_attachment_idx],
                );
                let local_attachment_id =
                    PassLocalAttachment::from_id(attachment.local_attachment_idx);
                let depth_clear_val =
                    pass_submit
                        .clear_depths
                        .get(&local_attachment_id)
                        .ok_or(gpu_api_err!(
                            "webpgpu clear depth stencil for attachment index {:?} not set",
                            local_attachment_id
                        ))?;
                match &attachment.ty {
                    PassInputType::Depth(load_op) => {
                        depth_stencil_attachment
                            .depth_store_op(GpuStoreOp::Store)
                            .depth_load_op(match load_op {
                                PassInputLoadOpDepthStencilType::Clear => GpuLoadOp::Clear,
                                _ => GpuLoadOp::Load,
                            })
                            .depth_clear_value(depth_clear_val.depth)
                            .stencil_load_op(match load_op {
                                PassInputLoadOpDepthStencilType::Clear => GpuLoadOp::Clear,
                                _ => GpuLoadOp::Load,
                            })
                            .stencil_store_op(GpuStoreOp::Store)
                            .stencil_clear_value(depth_clear_val.stencil);
                    }
                    _ => unreachable!(),
                }

                depth_attachment = Some(depth_stencil_attachment);
            }

            let mut pass_info = GpuRenderPassDescriptor::new(&color_attachments);

            if let Some(depth_attachment) = depth_attachment {
                pass_info.depth_stencil_attachment(&depth_attachment);
            }

            let pass_encoder = command_encoder.begin_render_pass(&pass_info);

            step.vertex_buffers
                .iter()
                .enumerate()
                .try_for_each(|(slot_idx, vbo)| {
                    let vbo = context.vbos.get(vbo.id()).ok_or(gpu_api_err!(
                        "webgpu submit vertex buffer id {:?} does not exist",
                        vbo
                    ))?;
                    pass_encoder.set_vertex_buffer(slot_idx as u32, Some(&vbo.buffer));
                    Ok(())
                })?;

            if let Some(ibo) = step.index_buffer {
                let ibo = context.ibos.get(ibo.id()).ok_or(gpu_api_err!(
                    "webgpu submit index buffer id {:?} does not exist",
                    ibo
                ))?;
                assert_eq!(std::mem::size_of::<IndexBufferElement>(), 4);
                pass_encoder.set_index_buffer(&ibo.buffer, GpuIndexFormat::Uint32);
            }

            step_data.draws.iter().try_for_each(|draw| {
                if let Some(viewport) = draw.viewport {
                    pass_encoder.set_viewport(
                        viewport.x,
                        viewport.y,
                        viewport.width,
                        viewport.height,
                        0.0,
                        1.0,
                    );
                }

                if let Some(scissor) = draw.scissor {
                    pass_encoder.set_scissor_rect(
                        scissor.x as u32,
                        scissor.y as u32,
                        scissor.width as u32,
                        scissor.height as u32,
                    );
                }

                pass_encoder.set_pipeline(pass.pipelines[step_idx].get(&draw.program).ok_or(
                    gpu_api_err!("webgpu submit draw missing program id {:?}", draw.program),
                )?);

                let program = context.programs.get(draw.program.id()).ok_or(gpu_api_err!(
                    "webgpu submit program id {:?} does not exist",
                    draw.program
                ))?;

                program.bind_groups.cmd_render_bind_groups(
                    context,
                    &pass_encoder,
                    &draw.dynamic_buffer_indices,
                )?;
                pass_encoder
                    .set_stencil_reference(program.ext.stencil_reference.unwrap_or_default());
                match draw.ty {
                    DrawType::Draw => {
                        pass_encoder.draw_with_instance_count_and_first_vertex_and_first_instance(
                            draw.count as u32,
                            draw.instance_count as u32,
                            draw.first as u32,
                            draw.first_instance as u32,
                        );
                    }
                    DrawType::DrawIndexed => {
                        pass_encoder.draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(
                            draw.count as u32,
                            draw.instance_count as u32,
                            draw.first as u32,
                            0,
                            draw.first_instance as u32,
                        );
                    }
                }

                Ok(())
            })?;

            pass_encoder.end();

            Ok(())
        })?;

    Ok(())
}