From ddb40d0c2ab1014d65490030438bee8958a591db Mon Sep 17 00:00:00 2001 From: Robin KAY Date: Wed, 8 Nov 2023 22:29:32 +0000 Subject: [PATCH] Port to Bevy 0.12 (#25) --- Cargo.toml | 6 ++-- src/draw.rs | 10 +++++-- src/generate.rs | 19 ++++++------ src/lib.rs | 33 ++++++++++++++++----- src/node.rs | 56 +++++++++++++++++++++++++++++++++++ src/outline.wgsl | 15 +++++----- src/pipeline.rs | 70 ++++++++++++++++++++++++++++++++++---------- src/uniforms.rs | 43 ++++++++++++++------------- src/view_uniforms.rs | 14 ++++----- 9 files changed, 194 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 971463d..867dd38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["gamedev", "bevy", "outline"] categories = ["game-engines", "rendering"] [dependencies] -bevy = { version = "0.11", default-features = false, features = [ +bevy = { version = "0.12", default-features = false, features = [ "bevy_asset", "bevy_render", "bevy_pbr", @@ -23,10 +23,10 @@ bevy = { version = "0.11", default-features = false, features = [ bitfield = "0.14" interpolation = "0.2" thiserror = "1.0" -wgpu-types = "0.16.1" +wgpu-types = "0.17" [dev-dependencies] -bevy = { version = "0.11", default-features = false, features = [ +bevy = { version = "0.12", default-features = false, features = [ "animation", "bevy_gltf", "bevy_pbr", diff --git a/src/draw.rs b/src/draw.rs index 8746dc5..06e4817 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -59,7 +59,7 @@ pub(crate) fn queue_outline_stencil_mesh( if !view_mask.intersects(outline_mask) { continue; // Layer not enabled } - let Some(mesh) = render_meshes.get(&outline.mesh) else { + let Some(mesh) = render_meshes.get(outline.mesh_id) else { continue; // No mesh }; let key = base_key @@ -78,6 +78,8 @@ pub(crate) fn queue_outline_stencil_mesh( pipeline, draw_function: draw_stencil, distance, + batch_range: 0..0, + dynamic_offset: None, }); } } @@ -135,7 +137,7 @@ pub(crate) fn queue_outline_volume_mesh( if !view_mask.intersects(outline_mask) { continue; // Layer not enabled } - let Some(mesh) = render_meshes.get(&outline.mesh) else { + let Some(mesh) = render_meshes.get(outline.mesh_id) else { continue; // No mesh }; let transparent = fragment_uniform.colour[3] < 1.0; @@ -162,6 +164,8 @@ pub(crate) fn queue_outline_volume_mesh( pipeline, draw_function: draw_transparent_outline, distance, + batch_range: 0..0, + dynamic_offset: None, }); } else { opaque_phase.add(OpaqueOutline { @@ -169,6 +173,8 @@ pub(crate) fn queue_outline_volume_mesh( pipeline, draw_function: draw_opaque_outline, distance, + batch_range: 0..0, + dynamic_offset: None, }); } } diff --git a/src/generate.rs b/src/generate.rs index 862ebf7..da6f731 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -138,22 +138,23 @@ impl OutlineMeshExt for Mesh { fn auto_generate_outline_normals( mut meshes: ResMut>, mut events: EventReader<'_, '_, AssetEvent>, - mut squelch: Local>>, + mut squelch: Local>>, ) { - for event in events.iter() { + for event in events.read() { match event { - AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { - if squelch.contains(handle) { + AssetEvent::Added { id } | AssetEvent::Modified { id } => { + if squelch.contains(id) { // Suppress modification events created by this system - squelch.remove(handle); - } else if let Some(mesh) = meshes.get_mut(handle) { + squelch.remove(id); + } else if let Some(mesh) = meshes.get_mut(*id) { let _ = mesh.generate_outline_normals(); - squelch.insert(handle.clone_weak()); + squelch.insert(*id); } } - AssetEvent::Removed { handle } => { - squelch.remove(handle); + AssetEvent::Removed { id } => { + squelch.remove(id); } + _ => {} } } } diff --git a/src/lib.rs b/src/lib.rs index 5158fef..f5979aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ use bevy::asset::load_internal_asset; use bevy::prelude::*; +use bevy::render::batching::{batch_and_prepare_render_phase, write_batched_instance_buffer}; use bevy::render::extract_component::{ ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, }; @@ -42,11 +43,12 @@ use crate::draw::{ use crate::node::{OpaqueOutline, OutlineNode, StencilOutline, TransparentOutline}; use crate::pipeline::{OutlinePipeline, FRAGMENT_SHADER_HANDLE, OUTLINE_SHADER_HANDLE}; use crate::uniforms::{ - extract_outline_uniforms, queue_outline_stencil_bind_group, queue_outline_volume_bind_group, - set_outline_visibility, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform, + extract_outline_uniforms, prepare_outline_stencil_bind_group, + prepare_outline_volume_bind_group, set_outline_visibility, OutlineFragmentUniform, + OutlineStencilUniform, OutlineVolumeUniform, }; use crate::view_uniforms::{ - extract_outline_view_uniforms, queue_outline_view_bind_group, OutlineViewUniform, + extract_outline_view_uniforms, prepare_outline_view_bind_group, OutlineViewUniform, }; mod computed; @@ -258,6 +260,20 @@ impl Plugin for OutlinePlugin { .add_systems( Render, ( + prepare_outline_view_bind_group, + prepare_outline_stencil_bind_group, + prepare_outline_volume_bind_group, + ) + .in_set(RenderSet::PrepareBindGroups), + ) + .add_systems( + Render, + (queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::QueueMeshes), + ) + .add_systems( + Render, + ( + sort_phase_system::, sort_phase_system::, sort_phase_system::, ) @@ -266,15 +282,16 @@ impl Plugin for OutlinePlugin { .add_systems( Render, ( - queue_outline_view_bind_group, - queue_outline_stencil_bind_group, - queue_outline_volume_bind_group, + batch_and_prepare_render_phase::, + batch_and_prepare_render_phase::, + batch_and_prepare_render_phase::, ) - .in_set(RenderSet::Queue), + .in_set(RenderSet::PrepareResources), ) .add_systems( Render, - (queue_outline_stencil_mesh, queue_outline_volume_mesh).in_set(RenderSet::Queue), + write_batched_instance_buffer:: + .in_set(RenderSet::PrepareResourcesFlush), ); let world = &mut app.sub_app_mut(RenderApp).world; diff --git a/src/node.rs b/src/node.rs index b39624d..41f1bfa 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,4 +1,5 @@ use std::cmp::Reverse; +use std::ops::Range; use bevy::ecs::system::lifetimeless::Read; use bevy::prelude::*; @@ -16,6 +17,7 @@ use bevy::render::{ render_graph::{Node, RenderGraphContext}, renderer::RenderContext, }; +use bevy::utils::nonmax::NonMaxU32; use bevy::utils::FloatOrd; pub(crate) struct StencilOutline { @@ -23,6 +25,8 @@ pub(crate) struct StencilOutline { pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, + pub batch_range: Range, + pub dynamic_offset: Option, } impl PhaseItem for StencilOutline { @@ -40,6 +44,22 @@ impl PhaseItem for StencilOutline { fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId { self.draw_function } + + fn batch_range(&self) -> &std::ops::Range { + &self.batch_range + } + + fn batch_range_mut(&mut self) -> &mut std::ops::Range { + &mut self.batch_range + } + + fn dynamic_offset(&self) -> Option { + self.dynamic_offset + } + + fn dynamic_offset_mut(&mut self) -> &mut Option { + &mut self.dynamic_offset + } } impl CachedRenderPipelinePhaseItem for StencilOutline { @@ -54,6 +74,8 @@ pub(crate) struct OpaqueOutline { pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, + pub batch_range: Range, + pub dynamic_offset: Option, } impl PhaseItem for OpaqueOutline { @@ -71,6 +93,22 @@ impl PhaseItem for OpaqueOutline { fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId { self.draw_function } + + fn batch_range(&self) -> &Range { + &self.batch_range + } + + fn batch_range_mut(&mut self) -> &mut Range { + &mut self.batch_range + } + + fn dynamic_offset(&self) -> Option { + self.dynamic_offset + } + + fn dynamic_offset_mut(&mut self) -> &mut Option { + &mut self.dynamic_offset + } } impl CachedRenderPipelinePhaseItem for OpaqueOutline { @@ -85,6 +123,8 @@ pub(crate) struct TransparentOutline { pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, + pub batch_range: Range, + pub dynamic_offset: Option, } impl PhaseItem for TransparentOutline { @@ -102,6 +142,22 @@ impl PhaseItem for TransparentOutline { fn draw_function(&self) -> bevy::render::render_phase::DrawFunctionId { self.draw_function } + + fn batch_range(&self) -> &Range { + &self.batch_range + } + + fn batch_range_mut(&mut self) -> &mut Range { + &mut self.batch_range + } + + fn dynamic_offset(&self) -> Option { + self.dynamic_offset + } + + fn dynamic_offset_mut(&mut self) -> &mut Option { + &mut self.dynamic_offset + } } impl CachedRenderPipelinePhaseItem for TransparentOutline { diff --git a/src/outline.wgsl b/src/outline.wgsl index 748ca6f..a47fb00 100644 --- a/src/outline.wgsl +++ b/src/outline.wgsl @@ -1,10 +1,13 @@ -#import bevy_render::view View -#import bevy_pbr::mesh_types Mesh -#import bevy_pbr::mesh_types SkinnedMesh +#import bevy_render::view::View +#import bevy_render::maths +#import bevy_pbr::mesh_types::Mesh +#import bevy_pbr::mesh_types::SkinnedMesh +#import bevy_pbr::mesh_functions struct Vertex { #ifdef VERTEX_POSITIONS @location(0) position: vec3, + @builtin(instance_index) instance_index: u32, #endif #ifndef OFFSET_ZERO @location(1) outline_normal: vec3, @@ -39,9 +42,7 @@ struct OutlineVertexUniform { @group(0) @binding(0) var view: View; -@group(1) @binding(0) -var mesh: Mesh; - +#import bevy_pbr::mesh_bindings #import bevy_pbr::skinning #import bevy_pbr::morph @@ -90,7 +91,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #ifdef SKINNED let model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights); #else - let model = mesh.model; + let model = bevy_pbr::mesh_functions::get_model_matrix(vertex_no_morph.instance_index); #endif let clip_pos = view.view_proj * (model * vec4(vertex.position, 1.0)); #ifdef OFFSET_ZERO diff --git a/src/pipeline.rs b/src/pipeline.rs index 75e1910..058be66 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -1,8 +1,14 @@ use std::borrow::Cow; -use bevy::pbr::{setup_morph_and_skinning_defs, MeshPipelineKey}; +use bevy::ecs::query::QueryItem; +use bevy::ecs::system::lifetimeless::Read; +use bevy::ecs::system::SystemParamItem; +use bevy::pbr::{ + setup_morph_and_skinning_defs, MeshFlags, MeshPipelineKey, MeshPipelineViewLayoutKey, + MeshTransforms, MeshUniform, +}; use bevy::prelude::*; -use bevy::reflect::TypeUuid; +use bevy::render::batching::GetBatchData; use bevy::render::render_resource::{ BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, BufferBindingType, BufferSize, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, @@ -11,6 +17,7 @@ use bevy::render::render_resource::{ TextureFormat, VertexState, }; use bevy::render::renderer::RenderDevice; +use bevy::render::settings::WgpuSettings; use bevy::render::texture::BevyDefault; use bevy::render::view::ViewTarget; use bevy::{ @@ -23,18 +30,20 @@ use bevy::{ }, }; use bitfield::{bitfield_bitrange, bitfield_fields}; +use wgpu_types::{Backends, PushConstantRange}; use crate::uniforms::{ - DepthMode, OutlineFragmentUniform, OutlineStencilUniform, OutlineVolumeUniform, + DepthMode, ExtractedOutline, OutlineFragmentUniform, OutlineStencilUniform, + OutlineVolumeUniform, }; use crate::view_uniforms::OutlineViewUniform; use crate::ATTRIBUTE_OUTLINE_NORMAL; -pub(crate) const OUTLINE_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2101625026478770097); +pub(crate) const OUTLINE_SHADER_HANDLE: Handle = + Handle::weak_from_u128(2101625026478770097); -pub(crate) const FRAGMENT_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 12033806834125368121); +pub(crate) const FRAGMENT_SHADER_HANDLE: Handle = + Handle::weak_from_u128(12033806834125368121); #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) enum PassType { @@ -245,11 +254,14 @@ impl SpecializedMeshPipeline for OutlinePipeline { buffer_attrs.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); } - let mut bind_layouts = vec![if key.msaa() == Msaa::Off { - self.mesh_pipeline.view_layout.clone() - } else { - self.mesh_pipeline.view_layout_multisampled.clone() - }]; + let mut bind_layouts = vec![self + .mesh_pipeline + .get_view_layout(if key.msaa() == Msaa::Off { + MeshPipelineViewLayoutKey::empty() + } else { + MeshPipelineViewLayoutKey::MULTISAMPLED + }) + .clone()]; bind_layouts.push(setup_morph_and_skinning_defs( &self.mesh_pipeline.mesh_layouts, @@ -308,15 +320,23 @@ impl SpecializedMeshPipeline for OutlinePipeline { } } let buffers = vec![layout.get_layout(&buffer_attrs)?]; + let mut push_constant_ranges = Vec::with_capacity(1); + // Proxy for webgl feature flag in bevy + if WgpuSettings::default().backends == Some(Backends::GL) { + push_constant_ranges.push(PushConstantRange { + stages: ShaderStages::VERTEX, + range: 0..4, + }); + } Ok(RenderPipelineDescriptor { vertex: VertexState { - shader: OUTLINE_SHADER_HANDLE.typed::(), + shader: OUTLINE_SHADER_HANDLE, entry_point: "vertex".into(), shader_defs: vertex_defs, buffers, }, fragment: Some(FragmentState { - shader: FRAGMENT_SHADER_HANDLE.typed::(), + shader: FRAGMENT_SHADER_HANDLE, shader_defs: fragment_defs, entry_point: "fragment".into(), targets, @@ -343,8 +363,28 @@ impl SpecializedMeshPipeline for OutlinePipeline { mask: !0, alpha_to_coverage_enabled: false, }, - push_constant_ranges: default(), + push_constant_ranges, label: Some(Cow::Borrowed("outline_pipeline")), }) } } + +impl GetBatchData for OutlinePipeline { + type Param = (); + type Query = Read; + type QueryFilter = (); + type CompareData = (); + type BufferData = MeshUniform; + + fn get_batch_data( + _: &SystemParamItem, + outline: &QueryItem, + ) -> (Self::BufferData, Option) { + let ts = MeshTransforms { + transform: (&outline.transform).into(), + previous_transform: (&outline.transform).into(), + flags: MeshFlags::NONE.bits(), + }; + ((&ts).into(), None) + } +} diff --git a/src/uniforms.rs b/src/uniforms.rs index 38cb984..86b04e7 100644 --- a/src/uniforms.rs +++ b/src/uniforms.rs @@ -3,11 +3,12 @@ use bevy::{ lifetimeless::{Read, SRes}, SystemParamItem, }, + math::Affine3A, prelude::*, render::{ extract_component::{ComponentUniforms, DynamicUniformIndex}, render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass}, - render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry, ShaderType}, + render_resource::{BindGroup, BindGroupEntry, ShaderType}, renderer::RenderDevice, Extract, }, @@ -18,7 +19,8 @@ use crate::{node::StencilOutline, pipeline::OutlinePipeline, ComputedOutline}; #[derive(Component)] pub(crate) struct ExtractedOutline { pub depth_mode: DepthMode, - pub mesh: Handle, + pub transform: Affine3A, + pub mesh_id: AssetId, } #[derive(Clone, Component, ShaderType)] @@ -57,13 +59,11 @@ pub(crate) struct OutlineVolumeBindGroup { pub bind_group: BindGroup, } -pub(crate) fn set_outline_visibility( - mut query: Query<(&mut ComputedVisibility, &ComputedOutline)>, -) { +pub(crate) fn set_outline_visibility(mut query: Query<(&mut ViewVisibility, &ComputedOutline)>) { for (mut visibility, computed) in query.iter_mut() { if let ComputedOutline(Some(computed)) = computed { if computed.volume.value.enabled || computed.stencil.value.enabled { - visibility.set_visible_in_view(); + visibility.set(); } } } @@ -72,14 +72,15 @@ pub(crate) fn set_outline_visibility( #[allow(clippy::type_complexity)] pub(crate) fn extract_outline_uniforms( mut commands: Commands, - query: Extract)>>, + query: Extract)>>, ) { - for (entity, computed, mesh) in query.iter() { + for (entity, computed, transform, mesh) in query.iter() { let cmds = &mut commands.get_or_spawn(entity); if let ComputedOutline(Some(computed)) = computed { cmds.insert(ExtractedOutline { depth_mode: computed.mode.value.depth_mode, - mesh: mesh.clone_weak(), + transform: transform.affine(), + mesh_id: mesh.id(), }); if computed.volume.value.enabled { cmds.insert(OutlineVolumeUniform { @@ -100,26 +101,26 @@ pub(crate) fn extract_outline_uniforms( } } -pub(crate) fn queue_outline_stencil_bind_group( +pub(crate) fn prepare_outline_stencil_bind_group( mut commands: Commands, render_device: Res, outline_pipeline: Res, vertex: Res>, ) { if let Some(vertex_binding) = vertex.binding() { - let bind_group = render_device.create_bind_group(&BindGroupDescriptor { - entries: &[BindGroupEntry { + let bind_group = render_device.create_bind_group( + Some("outline_stencil_bind_group"), + &outline_pipeline.outline_stencil_bind_group_layout, + &[BindGroupEntry { binding: 0, resource: vertex_binding.clone(), }], - label: Some("outline_stencil_bind_group"), - layout: &outline_pipeline.outline_stencil_bind_group_layout, - }); + ); commands.insert_resource(OutlineStencilBindGroup { bind_group }); } } -pub(crate) fn queue_outline_volume_bind_group( +pub(crate) fn prepare_outline_volume_bind_group( mut commands: Commands, render_device: Res, outline_pipeline: Res, @@ -127,8 +128,10 @@ pub(crate) fn queue_outline_volume_bind_group( fragment: Res>, ) { if let (Some(vertex_binding), Some(fragment_binding)) = (vertex.binding(), fragment.binding()) { - let bind_group = render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ + let bind_group = render_device.create_bind_group( + "outline_volume_bind_group", + &outline_pipeline.outline_volume_bind_group_layout, + &[ BindGroupEntry { binding: 0, resource: vertex_binding.clone(), @@ -138,9 +141,7 @@ pub(crate) fn queue_outline_volume_bind_group( resource: fragment_binding.clone(), }, ], - label: Some("outline_volume_bind_group"), - layout: &outline_pipeline.outline_volume_bind_group_layout, - }); + ); commands.insert_resource(OutlineVolumeBindGroup { bind_group }); } } diff --git a/src/view_uniforms.rs b/src/view_uniforms.rs index cbd4968..b503ab9 100644 --- a/src/view_uniforms.rs +++ b/src/view_uniforms.rs @@ -6,7 +6,7 @@ use bevy::render::render_phase::{ PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, TrackedRenderPass, }; use bevy::render::render_resource::ShaderType; -use bevy::render::render_resource::{BindGroup, BindGroupDescriptor, BindGroupEntry}; +use bevy::render::render_resource::{BindGroup, BindGroupEntry}; use bevy::render::renderer::RenderDevice; use bevy::render::view::RenderLayers; use bevy::render::Extract; @@ -49,21 +49,21 @@ pub(crate) fn extract_outline_view_uniforms( } } -pub(crate) fn queue_outline_view_bind_group( +pub(crate) fn prepare_outline_view_bind_group( mut commands: Commands, render_device: Res, outline_pipeline: Res, view_uniforms: Res>, ) { if let Some(view_binding) = view_uniforms.binding() { - let bind_group = render_device.create_bind_group(&BindGroupDescriptor { - entries: &[BindGroupEntry { + let bind_group = render_device.create_bind_group( + "outline_view_bind_group", + &outline_pipeline.outline_view_bind_group_layout, + &[BindGroupEntry { binding: 0, resource: view_binding.clone(), }], - label: Some("outline_view_bind_group"), - layout: &outline_pipeline.outline_view_bind_group_layout, - }); + ); commands.insert_resource(OutlineViewBindGroup { bind_group }); } }