Skip to content

Commit

Permalink
Add wgpu as core dependency (#395)
Browse files Browse the repository at this point in the history
Move `wgpu` from a dev-dependency to a core crate dependency, to allow the
direct use of its types.

Clean-up various documentations, and other minor code changes.
  • Loading branch information
djeedai authored Oct 31, 2024
1 parent 5cfcfa2 commit c8b616d
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 99 deletions.
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ bitflags = "2.3"
typetag = { version = "0.2", optional = true }
thiserror = "1.0"
# Same versions as Bevy 0.14 (bevy_render)
wgpu = "0.20"
naga = "0.20"
naga_oil = { version = "0.14", default-features = false, features = ["test_shader"] }

Expand All @@ -67,9 +68,6 @@ features = [ "bevy_core_pipeline", "bevy_render", "bevy_asset", "x11" ]
all-features = true

[dev-dependencies]
# Same versions as Bevy 0.14 (bevy_render)
wgpu = "0.20"

# For world inspector; required if "examples_world_inspector" is used.
bevy-inspector-egui = "0.25"
bevy_egui = { version = "0.28", default-features = false, features = [
Expand Down
71 changes: 69 additions & 2 deletions src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use bevy::{
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
use thiserror::Error;
use wgpu::{BlendComponent, BlendFactor, BlendOperation, BlendState};

use crate::{
modifier::{Modifier, RenderModifier},
Expand Down Expand Up @@ -108,7 +109,7 @@ pub enum SimulationCondition {
/// rendered during the [`Transparent2d`] render phase.
///
/// [`Transparent2d`]: bevy::core_pipeline::core_2d::Transparent2d
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Reflect, Serialize, Deserialize, Hash)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AlphaMode {
/// Render the effect with alpha blending.
Expand Down Expand Up @@ -199,6 +200,36 @@ pub enum AlphaMode {
Mask(ExprHandle),
}

impl From<AlphaMode> for BlendState {
fn from(value: AlphaMode) -> Self {
match value {
AlphaMode::Blend => BlendState::ALPHA_BLENDING,
AlphaMode::Premultiply => BlendState::PREMULTIPLIED_ALPHA_BLENDING,
AlphaMode::Add => BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::Zero,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
},
AlphaMode::Multiply => BlendState {
color: BlendComponent {
src_factor: BlendFactor::Dst,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent::OVER,
},
_ => BlendState::ALPHA_BLENDING,
}
}
}

/// Asset describing a visual effect.
///
/// The effect can be instanciated with a [`ParticleEffect`] component, or a
Expand Down Expand Up @@ -824,7 +855,7 @@ impl EffectAsset {
/// Build the particle layout of the asset based on its modifiers.
///
/// This method calculates the particle layout of the effect based on the
/// currently existing particles, and return it as a newly allocated
/// currently existing modifiers, and return it as a newly allocated
/// [`ParticleLayout`] object.
pub fn particle_layout(&self) -> ParticleLayout {
// Build the set of unique attributes required for all modifiers
Expand Down Expand Up @@ -1166,4 +1197,40 @@ mod tests {
effect_serde.render_modifiers().count()
);
}

#[test]
fn alpha_mode_blend_state() {
assert_eq!(BlendState::ALPHA_BLENDING, AlphaMode::Blend.into());
assert_eq!(
BlendState::PREMULTIPLIED_ALPHA_BLENDING,
AlphaMode::Premultiply.into()
);

let blend_state = BlendState {
color: BlendComponent {
src_factor: BlendFactor::SrcAlpha,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::Zero,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
};
assert_eq!(blend_state, AlphaMode::Add.into());

let blend_state = BlendState {
color: BlendComponent {
src_factor: BlendFactor::Dst,
dst_factor: BlendFactor::OneMinusSrcAlpha,
operation: BlendOperation::Add,
},
alpha: BlendComponent::OVER,
};
assert_eq!(blend_state, AlphaMode::Multiply.into());

let expr = Module::default().lit(0.5);
assert_eq!(BlendState::ALPHA_BLENDING, AlphaMode::Mask(expr).into());
}
}
20 changes: 14 additions & 6 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ use crate::{
properties::EffectProperties,
render::{
extract_effect_events, extract_effects, prepare_bind_groups, prepare_effects,
prepare_resources, queue_effects, DispatchIndirectPipeline, DrawEffects, EffectAssetEvents,
EffectBindGroups, EffectCache, EffectsMeta, ExtractedEffects, GpuDispatchIndirect,
GpuParticleGroup, GpuRenderEffectMetadata, GpuRenderGroupIndirect, GpuSpawnerParams,
ParticlesInitPipeline, ParticlesRenderPipeline, ParticlesUpdatePipeline, ShaderCache,
SimParams, StorageType as _, VfxSimulateDriverNode, VfxSimulateNode,
prepare_gpu_resources, queue_effects, DispatchIndirectPipeline, DrawEffects,
EffectAssetEvents, EffectBindGroups, EffectCache, EffectsMeta, ExtractedEffects,
GpuDispatchIndirect, GpuParticleGroup, GpuRenderEffectMetadata, GpuRenderGroupIndirect,
GpuSpawnerParams, ParticlesInitPipeline, ParticlesRenderPipeline, ParticlesUpdatePipeline,
ShaderCache, SimParams, StorageType as _, VfxSimulateDriverNode, VfxSimulateNode,
},
spawn::{self, Random},
tick_initializers,
Expand Down Expand Up @@ -76,16 +76,24 @@ pub enum EffectSystems {
GatherRemovedEffects,

/// Prepare effect assets for the extracted effects.
///
/// Part of Bevy's own [`RenderSet::PrepareAssets`].
PrepareEffectAssets,

/// Queue the GPU commands for the extracted effects.
///
/// Part of Bevy's own [`RenderSet::Queue`].
QueueEffects,

/// Prepare GPU data for the queued effects.
///
/// Part of Bevy's own [`RenderSet::Prepare`].
PrepareEffectGpuResources,

/// Prepare the GPU bind groups once all buffers have been (re-)allocated
/// and won't change this frame.
///
/// Part of Bevy's own [`RenderSet::PrepareBindGroups`].
PrepareBindGroups,
}

Expand Down Expand Up @@ -299,7 +307,7 @@ impl Plugin for HanabiPlugin {
queue_effects
.in_set(EffectSystems::QueueEffects)
.after(prepare_effects),
prepare_resources
prepare_gpu_resources
.in_set(EffectSystems::PrepareEffectGpuResources)
.after(prepare_view_uniforms),
prepare_bind_groups
Expand Down
35 changes: 26 additions & 9 deletions src/render/aligned_buffer_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@ use copyless::VecHelper;

use crate::next_multiple_of;

/// Like Bevy's [`BufferVec`], but with correct item alignment.
/// Like Bevy's [`BufferVec`], but with extra per-item alignment.
///
/// This is a helper to ensure the data is properly aligned when copied to GPU,
/// depending on the device constraints and the WGSL rules. Generally the
/// alignment is one of the [`WgpuLimits`], and is also ensured to be
/// compatible with WGSL.
/// This helper ensures the individual array elements are properly aligned,
/// depending on the device constraints and the WGSL rules. In general using
/// [`BufferVec`] is enough to ensure alignment; however when some array items
/// also need to be bound individually, then each item (not only the array
/// itself) needs to be aligned to the device requirements. This is admittedly a
/// very specific case, because the device alignment might be very large (256
/// bytes) and this causes a lot of wasted space (padding per-element, instead
/// of padding for the entire array).
///
/// For this buffer to work correctly and items be bindable individually, the
/// alignment must come from one of the [`WgpuLimits`]. For example for a
/// storage buffer, to be able to bind the entire buffer but also any subset of
/// it (including individual elements), the extra alignment must
/// be [`WgpuLimits::min_storage_buffer_offset_alignment`].
///
/// The element type `T` needs to implement the following traits:
/// - [`Pod`] to allow copy.
Expand Down Expand Up @@ -48,7 +58,7 @@ pub struct AlignedBufferVec<T: Pod + ShaderSize> {
label: Option<String>,
}

impl<T: Pod + ShaderType + ShaderSize> Default for AlignedBufferVec<T> {
impl<T: Pod + ShaderSize> Default for AlignedBufferVec<T> {
fn default() -> Self {
let item_size = std::mem::size_of::<T>();
let aligned_size = <T as ShaderSize>::SHADER_SIZE.get() as usize;
Expand All @@ -65,7 +75,7 @@ impl<T: Pod + ShaderType + ShaderSize> Default for AlignedBufferVec<T> {
}
}

impl<T: Pod + ShaderType + ShaderSize> AlignedBufferVec<T> {
impl<T: Pod + ShaderSize> AlignedBufferVec<T> {
/// Create a new collection.
///
/// `item_align` is an optional additional alignment for items in the
Expand Down Expand Up @@ -143,6 +153,7 @@ impl<T: Pod + ShaderType + ShaderSize> AlignedBufferVec<T> {
self.values.is_empty()
}

/// Append a value to the buffer.
pub fn push(&mut self, value: T) -> usize {
let index = self.values.len();
self.values.alloc().init(value);
Expand All @@ -151,10 +162,16 @@ impl<T: Pod + ShaderType + ShaderSize> AlignedBufferVec<T> {

/// Reserve some capacity into the buffer.
///
/// If the buffer is reallocated, the old content (on the GPU) is lost, and
/// needs to be re-uploaded to the newly-created buffer. This is done with
/// [`write_buffer()`].
///
/// # Returns
///
/// `true` if the buffer was (re)allocated, or `false` if an existing buffer
/// was reused which already had enough capacity.
///
/// [`write_buffer()`]: AlignedBufferVec::write_buffer
pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) -> bool {
if capacity > self.capacity {
let size = self.aligned_size * capacity;
Expand Down Expand Up @@ -217,15 +234,15 @@ impl<T: Pod + ShaderType + ShaderSize> AlignedBufferVec<T> {
}
}

impl<T: Pod + ShaderType + ShaderSize> std::ops::Index<usize> for AlignedBufferVec<T> {
impl<T: Pod + ShaderSize> std::ops::Index<usize> for AlignedBufferVec<T> {
type Output = T;

fn index(&self, index: usize) -> &Self::Output {
&self.values[index]
}
}

impl<T: Pod + ShaderType + ShaderSize> std::ops::IndexMut<usize> for AlignedBufferVec<T> {
impl<T: Pod + ShaderSize> std::ops::IndexMut<usize> for AlignedBufferVec<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.values[index]
}
Expand Down
Loading

0 comments on commit c8b616d

Please sign in to comment.