Replace UUID based IDs with a atomic-counted ones (#6988)

# Objective

- alternative to #2895 
- as mentioned in #2535 the uuid based ids in the render module should be replaced with atomic-counted ones

## Solution
- instead of generating a random UUID for each render resource, this implementation increases an atomic counter
- this might be replaced by the ids of wgpu if they expose them directly in the future

- I have not benchmarked this solution yet, but this should be slightly faster in theory.
- Bevymark does not seem to be affected much by this change, which is to be expected.

- Nothing of our API has changed, other than that the IDs have lost their IMO rather insignificant documentation.
- Maybe the documentation could be added back into the macro, but this would complicate the code.
This commit is contained in:
Kurt Kühnert 2022-12-25 00:23:15 +00:00
parent d3d635b64f
commit 965ebeff59
8 changed files with 65 additions and 91 deletions

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
define_atomic_id,
render_graph::{ render_graph::{
Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError,
RunSubGraphError, SlotInfo, SlotInfos, SlotType, SlotValue, RunSubGraphError, SlotInfo, SlotInfos, SlotType, SlotValue,
@ -6,28 +7,11 @@ use crate::{
renderer::RenderContext, renderer::RenderContext,
}; };
use bevy_ecs::world::World; use bevy_ecs::world::World;
use bevy_utils::Uuid;
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
use std::{borrow::Cow, fmt::Debug}; use std::{borrow::Cow, fmt::Debug};
use thiserror::Error; use thiserror::Error;
/// A [`Node`] identifier. define_atomic_id!(NodeId);
/// It automatically generates its own random uuid.
///
/// This id is used to reference the node internally (edges, etc).
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NodeId(Uuid);
impl NodeId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
NodeId(Uuid::new_v4())
}
pub fn uuid(&self) -> &Uuid {
&self.0
}
}
/// A render node that can be added to a [`RenderGraph`](super::RenderGraph). /// A render node that can be added to a [`RenderGraph`](super::RenderGraph).
/// ///

View File

@ -1,24 +1,19 @@
pub use bevy_render_macros::AsBindGroup;
use encase::ShaderType;
use crate::{ use crate::{
define_atomic_id,
prelude::Image, prelude::Image,
render_asset::RenderAssets, render_asset::RenderAssets,
render_resource::{BindGroupLayout, Buffer, Sampler, TextureView}, render_resource::{resource_macros::*, BindGroupLayout, Buffer, Sampler, TextureView},
renderer::RenderDevice, renderer::RenderDevice,
texture::FallbackImage, texture::FallbackImage,
}; };
use bevy_reflect::Uuid; pub use bevy_render_macros::AsBindGroup;
use encase::ShaderType;
use std::ops::Deref; use std::ops::Deref;
use wgpu::BindingResource; use wgpu::BindingResource;
use crate::render_resource::resource_macros::*; define_atomic_id!(BindGroupId);
render_resource_wrapper!(ErasedBindGroup, wgpu::BindGroup); render_resource_wrapper!(ErasedBindGroup, wgpu::BindGroup);
/// A [`BindGroup`] identifier.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct BindGroupId(Uuid);
/// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers) /// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers)
/// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass). /// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass).
/// This makes them accessible in the pipeline (shaders) as uniforms. /// This makes them accessible in the pipeline (shaders) as uniforms.
@ -42,7 +37,7 @@ impl BindGroup {
impl From<wgpu::BindGroup> for BindGroup { impl From<wgpu::BindGroup> for BindGroup {
fn from(value: wgpu::BindGroup) -> Self { fn from(value: wgpu::BindGroup) -> Self {
BindGroup { BindGroup {
id: BindGroupId(Uuid::new_v4()), id: BindGroupId::new(),
value: ErasedBindGroup::new(value), value: ErasedBindGroup::new(value),
} }
} }

View File

@ -1,10 +1,7 @@
use crate::render_resource::resource_macros::*; use crate::{define_atomic_id, render_resource::resource_macros::*};
use bevy_reflect::Uuid;
use std::ops::Deref; use std::ops::Deref;
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] define_atomic_id!(BindGroupLayoutId);
pub struct BindGroupLayoutId(Uuid);
render_resource_wrapper!(ErasedBindGroupLayout, wgpu::BindGroupLayout); render_resource_wrapper!(ErasedBindGroupLayout, wgpu::BindGroupLayout);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -34,7 +31,7 @@ impl BindGroupLayout {
impl From<wgpu::BindGroupLayout> for BindGroupLayout { impl From<wgpu::BindGroupLayout> for BindGroupLayout {
fn from(value: wgpu::BindGroupLayout) -> Self { fn from(value: wgpu::BindGroupLayout) -> Self {
BindGroupLayout { BindGroupLayout {
id: BindGroupLayoutId(Uuid::new_v4()), id: BindGroupLayoutId::new(),
value: ErasedBindGroupLayout::new(value), value: ErasedBindGroupLayout::new(value),
} }
} }

View File

@ -1,11 +1,7 @@
use bevy_utils::Uuid; use crate::{define_atomic_id, render_resource::resource_macros::render_resource_wrapper};
use std::ops::{Bound, Deref, RangeBounds}; use std::ops::{Bound, Deref, RangeBounds};
use crate::render_resource::resource_macros::*; define_atomic_id!(BufferId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct BufferId(Uuid);
render_resource_wrapper!(ErasedBuffer, wgpu::Buffer); render_resource_wrapper!(ErasedBuffer, wgpu::Buffer);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -42,7 +38,7 @@ impl Buffer {
impl From<wgpu::Buffer> for Buffer { impl From<wgpu::Buffer> for Buffer {
fn from(value: wgpu::Buffer) -> Self { fn from(value: wgpu::Buffer) -> Self {
Buffer { Buffer {
id: BufferId(Uuid::new_v4()), id: BufferId::new(),
value: ErasedBuffer::new(value), value: ErasedBuffer::new(value),
} }
} }

View File

@ -1,19 +1,16 @@
use crate::render_resource::{BindGroupLayout, Shader}; use super::ShaderDefVal;
use crate::{
define_atomic_id,
render_resource::{resource_macros::render_resource_wrapper, BindGroupLayout, Shader},
};
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_reflect::Uuid;
use std::{borrow::Cow, ops::Deref}; use std::{borrow::Cow, ops::Deref};
use wgpu::{ use wgpu::{
BufferAddress, ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState, BufferAddress, ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState,
VertexAttribute, VertexFormat, VertexStepMode, VertexAttribute, VertexFormat, VertexStepMode,
}; };
use super::ShaderDefVal; define_atomic_id!(RenderPipelineId);
use crate::render_resource::resource_macros::*;
/// A [`RenderPipeline`] identifier.
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct RenderPipelineId(Uuid);
render_resource_wrapper!(ErasedRenderPipeline, wgpu::RenderPipeline); render_resource_wrapper!(ErasedRenderPipeline, wgpu::RenderPipeline);
/// A [`RenderPipeline`] represents a graphics pipeline and its stages (shaders), bindings and vertex buffers. /// A [`RenderPipeline`] represents a graphics pipeline and its stages (shaders), bindings and vertex buffers.
@ -36,7 +33,7 @@ impl RenderPipeline {
impl From<wgpu::RenderPipeline> for RenderPipeline { impl From<wgpu::RenderPipeline> for RenderPipeline {
fn from(value: wgpu::RenderPipeline) -> Self { fn from(value: wgpu::RenderPipeline) -> Self {
RenderPipeline { RenderPipeline {
id: RenderPipelineId(Uuid::new_v4()), id: RenderPipelineId::new(),
value: ErasedRenderPipeline::new(value), value: ErasedRenderPipeline::new(value),
} }
} }
@ -51,10 +48,7 @@ impl Deref for RenderPipeline {
} }
} }
/// A [`ComputePipeline`] identifier. define_atomic_id!(ComputePipelineId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct ComputePipelineId(Uuid);
render_resource_wrapper!(ErasedComputePipeline, wgpu::ComputePipeline); render_resource_wrapper!(ErasedComputePipeline, wgpu::ComputePipeline);
/// A [`ComputePipeline`] represents a compute pipeline and its single shader stage. /// A [`ComputePipeline`] represents a compute pipeline and its single shader stage.
@ -78,7 +72,7 @@ impl ComputePipeline {
impl From<wgpu::ComputePipeline> for ComputePipeline { impl From<wgpu::ComputePipeline> for ComputePipeline {
fn from(value: wgpu::ComputePipeline) -> Self { fn from(value: wgpu::ComputePipeline) -> Self {
ComputePipeline { ComputePipeline {
id: ComputePipelineId(Uuid::new_v4()), id: ComputePipelineId::new(),
value: ErasedComputePipeline::new(value), value: ErasedComputePipeline::new(value),
} }
} }

View File

@ -120,4 +120,32 @@ macro_rules! render_resource_wrapper {
}; };
} }
#[macro_export]
macro_rules! define_atomic_id {
($atomic_id_type:ident) => {
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct $atomic_id_type(u32);
// We use new instead of default to indicate that each ID created will be unique.
#[allow(clippy::new_without_default)]
impl $atomic_id_type {
pub fn new() -> Self {
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(1);
match COUNTER.fetch_add(1, Ordering::Relaxed) {
0 => {
panic!(
"The system ran out of unique `{}`s.",
stringify!($atomic_id_type)
);
}
id => Self(id),
}
}
}
};
}
pub use render_resource_wrapper; pub use render_resource_wrapper;

View File

@ -1,27 +1,16 @@
use super::ShaderDefVal;
use crate::define_atomic_id;
use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset}; use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset};
use bevy_reflect::{TypeUuid, Uuid}; use bevy_reflect::TypeUuid;
use bevy_utils::{tracing::error, BoxedFuture, HashMap}; use bevy_utils::{tracing::error, BoxedFuture, HashMap};
use naga::back::wgsl::WriterFlags; use naga::{back::wgsl::WriterFlags, valid::Capabilities, valid::ModuleInfo, Module};
use naga::valid::Capabilities;
use naga::{valid::ModuleInfo, Module};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::{borrow::Cow, marker::Copy, ops::Deref, path::PathBuf, str::FromStr}; use std::{borrow::Cow, marker::Copy, ops::Deref, path::PathBuf, str::FromStr};
use thiserror::Error; use thiserror::Error;
use wgpu::Features; use wgpu::{util::make_spirv, Features, ShaderModuleDescriptor, ShaderSource};
use wgpu::{util::make_spirv, ShaderModuleDescriptor, ShaderSource};
use super::ShaderDefVal; define_atomic_id!(ShaderId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct ShaderId(Uuid);
impl ShaderId {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
ShaderId(Uuid::new_v4())
}
}
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ShaderReflectError { pub enum ShaderReflectError {

View File

@ -1,12 +1,9 @@
use bevy_utils::Uuid; use crate::define_atomic_id;
use std::ops::Deref; use std::ops::Deref;
use crate::render_resource::resource_macros::*; use crate::render_resource::resource_macros::*;
/// A [`Texture`] identifier. define_atomic_id!(TextureId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TextureId(Uuid);
render_resource_wrapper!(ErasedTexture, wgpu::Texture); render_resource_wrapper!(ErasedTexture, wgpu::Texture);
/// A GPU-accessible texture. /// A GPU-accessible texture.
@ -35,7 +32,7 @@ impl Texture {
impl From<wgpu::Texture> for Texture { impl From<wgpu::Texture> for Texture {
fn from(value: wgpu::Texture) -> Self { fn from(value: wgpu::Texture) -> Self {
Texture { Texture {
id: TextureId(Uuid::new_v4()), id: TextureId::new(),
value: ErasedTexture::new(value), value: ErasedTexture::new(value),
} }
} }
@ -50,10 +47,7 @@ impl Deref for Texture {
} }
} }
/// A [`TextureView`] identifier. define_atomic_id!(TextureViewId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct TextureViewId(Uuid);
render_resource_wrapper!(ErasedTextureView, wgpu::TextureView); render_resource_wrapper!(ErasedTextureView, wgpu::TextureView);
render_resource_wrapper!(ErasedSurfaceTexture, wgpu::SurfaceTexture); render_resource_wrapper!(ErasedSurfaceTexture, wgpu::SurfaceTexture);
@ -104,7 +98,7 @@ impl TextureView {
impl From<wgpu::TextureView> for TextureView { impl From<wgpu::TextureView> for TextureView {
fn from(value: wgpu::TextureView) -> Self { fn from(value: wgpu::TextureView) -> Self {
TextureView { TextureView {
id: TextureViewId(Uuid::new_v4()), id: TextureViewId::new(),
value: TextureViewValue::TextureView(ErasedTextureView::new(value)), value: TextureViewValue::TextureView(ErasedTextureView::new(value)),
} }
} }
@ -116,7 +110,7 @@ impl From<wgpu::SurfaceTexture> for TextureView {
let texture = ErasedSurfaceTexture::new(value); let texture = ErasedSurfaceTexture::new(value);
TextureView { TextureView {
id: TextureViewId(Uuid::new_v4()), id: TextureViewId::new(),
value: TextureViewValue::SurfaceTexture { texture, view }, value: TextureViewValue::SurfaceTexture { texture, view },
} }
} }
@ -134,10 +128,7 @@ impl Deref for TextureView {
} }
} }
/// A [`Sampler`] identifier. define_atomic_id!(SamplerId);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
pub struct SamplerId(Uuid);
render_resource_wrapper!(ErasedSampler, wgpu::Sampler); render_resource_wrapper!(ErasedSampler, wgpu::Sampler);
/// A Sampler defines how a pipeline will sample from a [`TextureView`]. /// A Sampler defines how a pipeline will sample from a [`TextureView`].
@ -162,7 +153,7 @@ impl Sampler {
impl From<wgpu::Sampler> for Sampler { impl From<wgpu::Sampler> for Sampler {
fn from(value: wgpu::Sampler) -> Self { fn from(value: wgpu::Sampler) -> Self {
Sampler { Sampler {
id: SamplerId(Uuid::new_v4()), id: SamplerId::new(),
value: ErasedSampler::new(value), value: ErasedSampler::new(value),
} }
} }