-
Notifications
You must be signed in to change notification settings - Fork 14
Performance Benchmarking - Plan #235
Description
PERFORMANCE TESTING SUITE
TYPES OF FUNCTIONS:
1. CPU NATIVE FUNCTIONS / PROCESSES
-
These can be done in gitHub actions CI/CD and locally
- Use Rust benchmark harness like Criterion for Local benchmarking. Codspeed for CI/CD integration.
- Test for both function and functionality
sphere_mesh()→ pure mesh generation cost (function)create_sphere()→ mesh generation + Bevy asset insertion + ECS entity creation (functionality)
CPU performance suite 1. Function-level benchmarks → sphere_mesh(), tessellate_path(), arc_path(), build_polygon_path(), compile_shader() 2. Functionality-level benchmarks → create_sphere(), flush_draw_commands() with many commands, create_from_geometry(), set_property() 3. Workload tiers → light / medium / heavy inputs 4. Metrics → p50, p90, p95, p99, throughput, allocations if possible 5. CI gates → compare against baseline, fail only on sustained regression, not tiny noise
2. GPU NATIVE RENDER FUNCTIONS / PROCESSES (WHICH IS DONE BY BEVY)
- Should be run locally by maintainers before raising/merging PR.
Notes: External libraries also degrade sometimes in performance. We also need to have that in account.
CRATE: processing_render
TL;DR: processing_render is mostly CPU-heavy in the parts that generate and prepare render data: tessellation, mesh generation, geometry mutation, shader/material setup, command flushing, and buffer/image conversion. These CPU-side generators are the most likely areas contributors will modify, so V1 should focus on deterministic CPU benchmarks for those hot paths.
For GPU benchmarking, we should measure the complete rendering pipeline rather than isolated snippets, because actual GPU work is mostly executed inside Bevy/wgpu. A full render benchmark harness can be added in V2 for frame time, GPU compute dispatch, readback, particles, and end-to-end scene rendering.
processing_render/src/render
⇒ Prepares the data required for rendering. CPU computations to prepare data-structures that are rendered by Bevy.
CPU processes
processing_render/src/render/primitive
processing_render/src/render/primitive/mod.rsThis has the code for generating Mesh with Tessellators. These mostly contains CPU intensive calculations that forms the base that gets rendered.
CPU intensive fns:
tessellate_path(...)Key terms:
Tessellator (github)
Tessellators such as the ones provided by lyon take complex shapes as input and generate geometry made of triangles that can be easily consumed by graphics APIs such as OpenGL, Vulkan or D3D.
TessellationMode:
pub enum TessellationMode { Fill, // fill the inside of the shape. Stroke(f32), // draw only the outline/border of the shape. }Mesh
Meshis the renderable geometry object.Path
A Lyon
Pathis a data structure from the Rustlyoncrate that describes a 2D vector shape.pub struct Path { points: Box<[Point]>, verbs: Box<[Verb]>, num_attributes: usize, } pub struct Point2D<T, U> { pub x: T, pub y: T, #[doc(hidden)] pub _unit: PhantomData<U>, } pub(crate) enum Verb { LineTo, QuadraticTo, CubicTo, Begin, Close, End, }Example:
Let's say points:
(0, 0) (100, 0) (100, 50) (0, 50)Let's say verbs
MoveTo LineTo LineTo LineTo CloseThis means:
Start at (0,0) Draw line to (100,0) Draw line to (100,50) Draw line to (0,50) Close the shape
processing_render/src/render/primitive/arc.rsbuilds arc paths for fill/stroke modes and tessellates them into Bevy mesh geometry.
CPU intensive fns:
arc_path(...),tessellate_path(...)being called fromarc_stroke(...)andarc_fill(...)
processing_render/src/render/primitive/curves.rscurve paths and stroke-tessellates them into Bevy mesh geometry.
CPU intensive:builder.cubic_bezier_to(...)tessellate_path(...)
processing_render/src/render/primitive/ellipse.rs
ellipse.rsbuilds an ellipse as four cubic Bézier curve segments and tessellates it into Bevy mesh geometry.CPU intensive fns:
ellipse_path(...)b.cubic_bezier_to(...)tessellate_path(...)
processing_render/src/render/primitive/line.rs
line.rsbuilds a simple two-point line path and stroke-tessellates it into Bevy mesh geometry.Usually not CPU intensive
processing_render/src/render/primitive/quad.rs
quad.rsdraws quadrilaterals by directly writing filled quad mesh data, or by path-tessellating the quad outline for strokes.CPU intensive:
simple_quad(...)quad_path(...)only if Stroke mode is used
processing_render/src/render/primitive/rect.rs
rect.rsdraws rectangles by directly writing simple filled rectangle mesh data, or by building/tessellating a Lyon path for strokes and rounded corners.CPU intensive:
simple_rect(...), Intensive in Stroke mode, or when it has round corners
processing_render/src/render/primitive/shape.rs
shape.rsimplementsbeginShape()/vertex()/endShape()support by collecting custom shape verticesCPU intensive:
build_polygon_fill(...) build_polygon_stroke(...) tessellate_path(...) build_polygon_path(...) expand_curve_vertices(...) flush_curve_points(...) build_direct_fill(...)
processing_render/src/render/primitive/shape3d.rs
shape3d.rscreates reusable BevyMeshobjects for built-in 3D primitives like box, sphere, cylinder, cone, torus, capsule, grid, and plane.CPU intensive:
sphere.mesh().uv(sectors, stacks) cylinder.mesh().resolution(detail).build() cone.mesh().resolution(detail).build() torus.mesh().major_resolution(...).minor_resolution(...).build() capsule.mesh().longitudes(detail).latitudes(...).build() frustum.mesh().resolution(detail).build()
processing_render/src/render/primitive/triangle.rs
triangle.rsdraws triangles by directly writing filled triangle mesh data, or by path-tessellating the triangle outline for strokes.CPU intensive: Not so much, usually cheap
processing_render/src/render/transform.rsThe main responsibility of
TransformStackis to keep track of the current transformation applied to everything you draw.
processing_render/src/render/mesh_builder.rsThis
MeshBuilderis the adapter between Lyon tessellation and Bevy Mesh. convert Lyon's generated geometry into Bevy's mesh format. (Mostly an abstraction layer used else where, no heavy computations)
processing_render/src/render/material.rsThis file manages materials: color, texture, PBR lighting properties, custom materials, and blending. (Mostly book keeping and adapter fn)
processing_render/src/render/command.rsMostly helper fn for pushing Draw Commands in to
CommandBufferQueue
processing_render/src/render/mod.rsMain orchestration layer. Module file for Flushes from Command Buffer and write to Render State, prepares renderable entities/assets, nothing is rendered yet.
processing_render/src/geometry
⇒ This file is for retained geometry. Meaning: unlike
rect(),ellipse(), etc. which may generate fresh mesh data every frame,Geometrylets you create a mesh once, keep it as an asset, mutate it, and render/reuse it later.CPU processes
processing_render/src/geometry/mod.rsModule file for geometry. This module mostly makes use of render/primitive fns.
CPU intensive fns are : create_sphere() / create_grid() / create_box()
processing_render/src/geometry/attribute.rsThis file manages vertex attributes for retained
Geometrymeshes.
Vertex: A vertex is a single point in 2D or 3D space that forms the building block of a mesh. (Mostly book keeping). No CPU intensive calculations
processing_render/src/geometry/layout.rsThis file defines vertex layouts for retained
Geometry. No CPU intensive tasks mostly book keeping.
processing_render/src/material
CPU processes
processing_render/src/material/mod.rs
material/mod.rsdefines Processing's Bevy material plugin.create_pbr / set_property / destroy → normal CPU-side material management MaterialExtension methods → CPU-side render-pipeline callbacks triggered by Bevy while preparing GPU pipelines/shaders Actual GPU work → happens later inside Bevy/wgpuCPU intensive:
set_property(...) -> mat.shader.reflection().parameter(&name)
processing_render/src/material/custom.rscreate_shader / load_shader → CPU-heavy shader loading + WESL/WGSL compilation/parsing create_custom / set_property / destroy_shader → normal CPU-side custom material/shader management apply_reflect_field / find_param_containing_field → CPU-side reflection/property lookup helpers prepare_asset → CPU-side render-asset preparation triggered by Bevy; builds bind groups, material properties, shader references, and pipeline metadata extract_* / check_entities_needing_specialization → CPU-side Bevy render-world bookkeeping for changed/visible custom materials specialize → CPU-side render-pipeline callback triggered by Bevy while preparing GPU pipeline variants Actual GPU work → happens later inside Bevy/wgpuCPU intensive:
compile_shader(...)create_shader(...)load_shader(...)
create_custom(...)set_property(...)apply_reflect_field(...)find_param_containing_field(...)Triggered by Bevy but CPU intensive
prepare_asset(...)
processing_render/src/material/pbr.rs
pbr.rsmaps user-facing material property names like color, metallic, roughness, emissive, and texture into BevyStandardMaterialfields.No CPU intensive
processing_render/src/particles
CPU processes
processing_render/src/particles/kernels/mod.rs
particles/kernels/mod.rsregisters built-in WGSL compute shader files for particle noise and transform kernels as embedded Bevy assets. Not CPU intensive
processing_render/src/particles/mod.rs
particles/mod.rsdefines GPU-resident particle containers by allocating per-attribute GPU buffers, optionally seeding them fromGeometry, and wiring particle rendering/compute plugins.create / create_from_geometry → CPU-side particle container + GPU-buffer setup make_buffer → CPU-side asset creation + GPU readback buffer allocation through RenderDevice attribute_values_to_bytes → CPU-side data conversion; can be expensive for large meshes destroy → CPU-side cleanup of particle buffers/entities Actual particle compute/rendering → happens later in Bevy/wgpu through compute kernels and GpuInstanceBatchPluginCPU intensive:
create(...) create_from_geometry(...) make_buffer(...)
processing_render/src/particles/material.rs
particles/material.rsdefines the particle material extension that binds a per-particle color buffer and uses a custom WGSL fragment shader for particle rendering. No CPU intensive, usually book-keeping
processing_render/src/particles/pack.rs
particles/pack.rspacks particle position/rotation/scale/dead buffers into Bevy's GPU instancing buffers using a compute pass before mesh preprocessing.extract_particles_draws → CPU-side render-world extraction/bookkeeping get_or_create_pipeline → CPU-side compute pipeline caching/specialization prepare_pack_bind_groups → CPU-side GPU preparation: resolve buffers, create bind groups, write uniforms dispatch_pack → GPU dispatch trigger: begins compute pass and calls dispatch_workgroups(...) Actual GPU work → runs in pack.wgsl through Bevy/wgpuCPU intensive:
prepare_pack_bind_groups(...)
GPU triggering:pass.dispatch_workgroups(...);GPU processes
processing_render/src/particles/kernels/noise.wgsl
noise.wgslapplies procedural 3D value-noise displacement to particle positions in parallel on the GPU.GPU-intensive parts:
value_noise(...), noise3(...)
processing_render/src/particles/kernels/transform.wgsl
transform.wgslapplies scale, optional axis-angle rotation, and translation to particle positions in parallel on the GPU.GPU intensive parts:
cos(angle) sin(angle) cross(axis, p) dot(axis, p)
processing_render/src/particles/pack.wgsl
pack.wgslis the GPU compute shader that converts particle buffers into Bevy's per-instance mesh input/culling buffers triggered byprocessing_render/src/particles/pack.rsGPU intensive:
quat_to_basis(q)
rocessing_render/src/particles/pack.wgsl
particles.wgslis a particle fragment shader that multiplies the material color by each particle's per-instance color before running Bevy PBR lighting/output.GPU intensive:
fragment(...)
processing_render/src/transform.rs
This file provides CPU-side helper APIs for mutating Bevy
Transformcomponents: position, rotation, scale, look-at, and reset.CPU Processes
None
processing_render/src/time.rs
This file exposes Processing-style time helpers for frame count, delta time, and elapsed time using Bevy's
Timeresource.CPU Processes
None
processing_render/src/surface.rs
surface.rscreates and manages Processing render surfaces: native windows, offscreen targets, resizing, pixel density, monitor placement, and window controls.create_surface_* / spawn_surface → CPU-side native window/surface setup; → GPU surface/swapchain is created later by Bevy/wgpu prepare_offscreen → CPU/memory-heavy for large surfaces because it allocates pixel buffer: vec![0u8; width * height * pixel_size] resize / set_pixel_density → CPU-side window metadata updates; → may trigger GPU swapchain/texture resize later in Bevy/wgpu destroy → CPU-side ECS + asset cleanup window property helpers → cheap CPU-side bookkeepingCPU Processes
prepare_offscreen(...)
processing_render/src/sketch.rs
sketch.rsloads a user sketch source file, stores it as a BevySketchasset, and detects hot-reload updates.CPU Processes
None (Not Heavy)
processing_render/src/shader_value.rs
shader_value.rsdefines a typed container for shader uniform/resource values and converts scalar/vector/matrix values to/from raw bytes for GPU buffer usage.CPU Processes
None
processing_render/src/monitor.rs
monitor.rsexposes simple CPU-side helpers for listing monitors and reading monitor properties like size, scale factor, refresh rate, and name.CPU Processes: None
processing_render/src/light.rs
light.rscreates Bevy directional, point, and spot light entities for a Processing graphics surface.CPU heavy: None
processing_render/src/image.rs
image.rscreates, loads, resizes, updates, reads back, and destroys Processing image/texture assets backed by Bevy GPU images.CPU Heavy
pixels_to_bytes(...) bytes_to_pixels(...) readback(...) prepare_update_region(...) resize(...) create_readback_buffer(...)GPU Triggers
render_queue.write_texture(...) encoder.copy_texture_to_buffer(...) render_queue.submit(...) render_device.create_buffer(...)
processing_render/src/graphics.rs
graphics.rscreates and manages the Processing graphics context: camera/render target setup, draw command recording/flushing, 2D/3D projection modes, render layers, texture updates, and GPU readback.CPU Heavy
create(...) sync_to_surface(...) readback_raw(...) prepare_update_region(...) warmup(...)GPU Triggers
app.update() encoder.copy_texture_to_buffer(...) render_queue.submit(...) render_queue.write_texture(...)
processing_render/src/gltf.rs
gltf.rsloads GLTF scenes, extracts named meshes/materials/cameras/lights, and adapts them into Processing/BevyGeometry, materials, transforms, and render layers.CPU
load(...) compute_global_transform(...) geometry(...) material(...)
processing_render/src/compute.rs
compute.rsprovides generic GPU compute support: creates shader buffers, builds compute pipelines from custom shaders, binds resources/uniforms, dispatches compute workgroups, and reads buffers back to CPU.CPU
create_buffer(...) create_buffer_with_data(...) read_buffer_gpu(...) create_compute(...) set_compute_property(...) dispatch(...)
processing_render/src/color.rs
color.rsdefines Processing-style color modes/spaces and converts normalized or scaled color inputs into BevyColorvalues.CPU intensive: None
processing_render/src/camera.rs
camera.rsadds orbit/free/pan camera controls and updates camera transforms from mouse/input state.CPU heavy: None
processing_render/src/lib.rs
public API
CPU
shader_create shader_load compute_create geometry_sphere geometry_grid gltf_load particles_emit graphics_update image_updateGPU
graphics_flush graphics_present graphics_readback image_readback buffer_read compute_dispatch particles_apply particles_emit_gpu