Skip to content

Commit 71ce303

Browse files
compute-shader mesh generation example (#22296)
# Objective People have been asking how to get a compute shader-built mesh into bevy's "stuff". Some people want to control the lifetime of the mesh via Handle, and others don't don't how to set data in bind groups. ## Solution a new example that shows how to initialize a mesh handle with a render_world usage mesh, and then put the output of the compute shader into the mesh_allocator slab for the mesh. The demo creates a scene with a camera, light, a circular base mesh, and an empty "cube to be" mesh that is shared by cloning the handle across two entities. The compute shader then fills in the data directly into the mesh_allocator slabs for the vertex/index buffers. If the compute shader failed, there would be no cube meshes showing as the data would be empty. ## Testing ``` cargo run --example compute_mesh ``` --- ## Showcase <img width="3392" height="2106" alt="screenshot-2025-12-29-at-16 06 48@2x" src="https://github.com/user-attachments/assets/88d8fed4-e3c1-418e-bb04-6f08d673403a" />
1 parent 7d893cc commit 71ce303

File tree

4 files changed

+452
-0
lines changed

4 files changed

+452
-0
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3197,6 +3197,17 @@ description = "A very simple compute shader that writes to a buffer that is read
31973197
category = "Shaders"
31983198
wasm = false
31993199

3200+
[[example]]
3201+
name = "compute_mesh"
3202+
path = "examples/shader_advanced/compute_mesh.rs"
3203+
doc-scrape-examples = true
3204+
3205+
[package.metadata.example.compute_mesh]
3206+
name = "Compute Shader Mesh"
3207+
description = "A compute shader that generates a mesh that is controlled by a Handle"
3208+
category = "Shaders"
3209+
wasm = false
3210+
32003211
[[example]]
32013212
name = "array_texture"
32023213
path = "examples/shader/array_texture.rs"

assets/shaders/compute_mesh.wgsl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// This shader is used for the compute_mesh example
2+
// The actual work it does is not important for the example and
3+
// has been hardcoded to return a cube mesh
4+
5+
// `vertex_start` is the starting offset of the mesh data in the *vertex_data* storage buffer
6+
// `index_start` is the starting offset of the index data in the *index_data* storage buffer
7+
struct DataRanges {
8+
vertex_start: u32,
9+
vertex_end: u32,
10+
index_start: u32,
11+
index_end: u32,
12+
}
13+
14+
@group(0) @binding(0) var<uniform> data_range: DataRanges;
15+
@group(0) @binding(1) var<storage, read_write> vertex_data: array<f32>;
16+
@group(0) @binding(2) var<storage, read_write> index_data: array<u32>;
17+
18+
@compute @workgroup_size(1)
19+
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
20+
// this loop is iterating over the full list of (position, normal, uv)
21+
// data what we have in `vertices`.
22+
// `192` is used because arrayLength on const arrays doesn't work
23+
for (var i = 0u; i < 192; i++) {
24+
// The vertex_data buffer is bigger than just the mesh we're
25+
// processing because Bevy stores meshes in the mesh_allocator
26+
// which allocates slabs that each can contain multiple meshes.
27+
// This buffer is one slab, and data_range.vertex_start is the
28+
// starting offset for the mesh we care about.
29+
// So the 0 starting value in the for loop is added to
30+
// data_range.vertex_start which means we start writing at the
31+
// correct offset.
32+
//
33+
// The "end" of the available space to write into is known by us
34+
// ahead of time in this example, so we know this has enough space,
35+
// but you may wish to also check to make sure you are not writing
36+
// past the end of the range *because you should not write past the
37+
// end of the range ever*. Doing this can overwrite a different
38+
// mesh's data.
39+
vertex_data[i + data_range.vertex_start] = vertices[i];
40+
}
41+
// `36` is the length of the `indices` array
42+
for (var i = 0u; i < 36; i++) {
43+
// This is doing the same as the vertex_data offset described above
44+
index_data[i + data_range.index_start] = u32(indices[i]);
45+
}
46+
}
47+
48+
// hardcoded compute shader data.
49+
// half_size is half the size of the cube
50+
const half_size = vec3(1.5);
51+
const min = -half_size;
52+
const max = half_size;
53+
54+
// Suppose Y-up right hand, and camera look from +Z to -Z
55+
const vertices = array(
56+
// xyz, normal.xyz, uv.xy
57+
// Front
58+
min.x, min.y, max.z, 0.0, 0.0, 1.0, 0.0, 0.0,
59+
max.x, min.y, max.z, 0.0, 0.0, 1.0, 1.0, 0.0,
60+
max.x, max.y, max.z, 0.0, 0.0, 1.0, 1.0, 1.0,
61+
min.x, max.y, max.z, 0.0, 0.0, 1.0, 0.0, 1.0,
62+
// Back
63+
min.x, max.y, min.z, 0.0, 0.0, -1.0, 1.0, 0.0,
64+
max.x, max.y, min.z, 0.0, 0.0, -1.0, 0.0, 0.0,
65+
max.x, min.y, min.z, 0.0, 0.0, -1.0, 0.0, 1.0,
66+
min.x, min.y, min.z, 0.0, 0.0, -1.0, 1.0, 1.0,
67+
// Right
68+
max.x, min.y, min.z, 1.0, 0.0, 0.0, 0.0, 0.0,
69+
max.x, max.y, min.z, 1.0, 0.0, 0.0, 1.0, 0.0,
70+
max.x, max.y, max.z, 1.0, 0.0, 0.0, 1.0, 1.0,
71+
max.x, min.y, max.z, 1.0, 0.0, 0.0, 0.0, 1.0,
72+
// Left
73+
min.x, min.y, max.z, -1.0, 0.0, 0.0, 1.0, 0.0,
74+
min.x, max.y, max.z, -1.0, 0.0, 0.0, 0.0, 0.0,
75+
min.x, max.y, min.z, -1.0, 0.0, 0.0, 0.0, 1.0,
76+
min.x, min.y, min.z, -1.0, 0.0, 0.0, 1.0, 1.0,
77+
// Top
78+
max.x, max.y, min.z, 0.0, 1.0, 0.0, 1.0, 0.0,
79+
min.x, max.y, min.z, 0.0, 1.0, 0.0, 0.0, 0.0,
80+
min.x, max.y, max.z, 0.0, 1.0, 0.0, 0.0, 1.0,
81+
max.x, max.y, max.z, 0.0, 1.0, 0.0, 1.0, 1.0,
82+
// Bottom
83+
max.x, min.y, max.z, 0.0, -1.0, 0.0, 0.0, 0.0,
84+
min.x, min.y, max.z, 0.0, -1.0, 0.0, 1.0, 0.0,
85+
min.x, min.y, min.z, 0.0, -1.0, 0.0, 1.0, 1.0,
86+
max.x, min.y, min.z, 0.0, -1.0, 0.0, 0.0, 1.0
87+
);
88+
89+
const indices = array(
90+
0, 1, 2, 2, 3, 0, // front
91+
4, 5, 6, 6, 7, 4, // back
92+
8, 9, 10, 10, 11, 8, // right
93+
12, 13, 14, 14, 15, 12, // left
94+
16, 17, 18, 18, 19, 16, // top
95+
20, 21, 22, 22, 23, 20, // bottom
96+
);

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ Example | Description
471471
[Animated](../examples/shader/animate_shader.rs) | A shader that uses dynamic data like the time since startup
472472
[Array Texture](../examples/shader/array_texture.rs) | A shader that shows how to reuse the core bevy PBR shading functionality in a custom material that obtains the base color from an array texture.
473473
[Compute - Game of Life](../examples/shader/compute_shader_game_of_life.rs) | A compute shader that simulates Conway's Game of Life
474+
[Compute Shader Mesh](../examples/shader_advanced/compute_mesh.rs) | A compute shader that generates a mesh that is controlled by a Handle
474475
[Custom Render Phase](../examples/shader_advanced/custom_render_phase.rs) | Shows how to make a complete render phase
475476
[Custom Vertex Attribute](../examples/shader_advanced/custom_vertex_attribute.rs) | A shader that reads a mesh's custom vertex attribute
476477
[Custom phase item](../examples/shader_advanced/custom_phase_item.rs) | Demonstrates how to enqueue custom draw commands in a render phase

0 commit comments

Comments
 (0)