pub struct SelectionBundle<G: GaussianPod> {
pub bundles: Vec<ComputeBundle<()>>,
/* private fields */
}Expand description
A collection of specialized ComputeBundle for selection operations.
§Custom Operations
All ComputeBundles supplied to this bundle as a SelectionExpr::Unary,
SelectionExpr::Binary, or SelectionExpr::Selection custom operation must have the same
bind group 0 as the SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR. They must also
not have the bind group itself, as it will be supplied automatically during evaluation.
Note that SelectionExpr::Unary will also get the source selection buffer, but it will be
empty (all zeros), you should operate on the destination selection buffer only.
It is recommended to use ComputeBundleBuilder to create the custom operation bundles,
and build them using ComputeBundleBuilder::build_without_bind_groups.
// Create an editor that holds the buffers for the Gaussians
let editor = Editor::new(
&device,
&vec![core::Gaussian {
rot: Quat::IDENTITY,
pos: Vec3::ZERO,
color: U8Vec4::ZERO,
sh: [Vec3::ZERO; 15],
scale: Vec3::ONE,
}],
);
// Create the selection custom operation compute bundle
let my_selection_custom_op_bundle = core::ComputeBundleBuilder::new()
.label("My Selection")
.bind_group_layouts([
&SelectionBundle::<GaussianPod>::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR,
&MY_CUSTOM_BIND_GROUP_LAYOUT_DESCRIPTOR,
])
.resolver({
let mut resolver =
wesl::StandardResolver::new("path/to/my/folder/containing/wesl");
// Required for using core buffer structs.
resolver.add_package(&core::shader::PACKAGE);
// Optionally add this for some utility functions.
resolver.add_package(&shader::PACKAGE);
resolver
})
.main_shader("package::my_wesl_filename".parse().unwrap())
.entry_point("main")
.wesl_compile_options(wesl::CompileOptions {
// Required for enabling the correct features for core struct.
features: GaussianPod::wesl_features(),
..Default::default()
})
.build_without_bind_groups(&device)
.map_err(|e| log::error!("{e}"))
.expect("my selection custom op bundle");
// Create the selection bundle
let selection_bundle =
SelectionBundle::<GaussianPod>::new(&device, vec![my_selection_custom_op_bundle]);
// Create the bind group for your custom operation
let my_selection_custom_op_bind_group = selection_bundle.bundles[0]
.create_bind_group(
&device,
1, // Index 0 is always the Gaussians buffer
[my_buffer.buffer().as_entire_binding()],
)
.unwrap();
// Create the selection expression
let selection_expr = SelectionExpr::selection(
0, // The bundle index for your custom operation in the selection bundle
vec![my_selection_custom_op_bind_group],
)
.union(
// Combine with other selection expressions using different functions
// Here is an existing selection buffer for example
SelectionExpr::Buffer(my_existing_selection_buffer),
);
// Create a selection buffer for the result
let dest_selection_buffer =
SelectionBuffer::new(&device, editor.gaussians_buffer.len() as u32);
// Evaluate the selection expression
selection_bundle.evaluate(
&device,
&mut encoder,
&selection_expr,
&dest_selection_buffer,
&editor.model_transform_buffer,
&editor.gaussian_transform_buffer,
&editor.gaussians_buffer,
);§Shader Format
You may copy and paste the following shader bindings for
SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR into your custom selection operation
shader to ensure that the bindings are correct, then add your own bindings after that.
import wgpu_3dgs_core::{
gaussian::Gaussian,
gaussian_transform::GaussianTransform,
model_transform::{model_to_world, ModelTransform},
};
@group(0) @binding(0)
var<uniform> op: u32;
@group(0) @binding(1)
var<storage, read> source: array<u32>;
@group(0) @binding(2)
var<storage, read_write> dest: array<atomic<u32>>;
@group(0) @binding(3)
var<uniform> model_transform: ModelTransform;
@group(0) @binding(4)
var<uniform> gaussian_transform: GaussianTransform;
@group(0) @binding(5)
var<storage, read> gaussians: array<Gaussian>;
// Your custom bindings here...
override workgroup_size: u32;
@compute @workgroup_size(workgroup_size)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
let index = id.x;
if index >= arrayLength(&gaussians) {
return;
}
let gaussian = gaussians[index];
let world_pos = model_to_world(model_transform, gaussian.position);
// Your custom selection operation code here...
let word_index = index / 32u;
let bit_index = index % 32u;
let bit_mask = 1u << bit_index;
if /* Condition for selecting the Gaussian */ {
atomicOr(&dest[word_index], bit_mask);
} else {
atomicAnd(&dest[word_index], ~bit_mask);
}
}Fields§
§bundles: Vec<ComputeBundle<()>>The compute bundles for selection custom operations.
Implementations§
Source§impl<G: GaussianPod> SelectionBundle<G>
impl<G: GaussianPod> SelectionBundle<G>
Sourcepub const GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
pub const GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
The Gaussians bind group layout descriptors.
This bind group layout takes the following buffers:
Sourcepub const SPHERE_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
pub const SPHERE_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
The sphere selection bind group layout descriptor.
This bind group layout takes the following buffers:
Sourcepub const BOX_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
pub const BOX_BIND_GROUP_LAYOUT_DESCRIPTOR: BindGroupLayoutDescriptor<'static>
The box selection bind group layout descriptor.
This bind group layout takes the following buffers:
Sourcepub fn new(device: &Device, bundles: Vec<ComputeBundle<()>>) -> Self
pub fn new(device: &Device, bundles: Vec<ComputeBundle<()>>) -> Self
Create a new selection bundle.
bundles are used for SelectionExpr::Unary, SelectionExpr::Binary, or
SelectionExpr::Selection as custom operations, they must have the same bind group 0 as
the SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR, see documentation of
SelectionBundle for more details.
Examples found in repository?
98async fn main() {
99 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
100
101 let args = Args::parse();
102 let model_path = &args.model;
103 let pos = Vec3::from_slice(&args.pos);
104 let rot = Quat::from_slice(&args.rot);
105 let scale = Vec3::from_slice(&args.scale);
106 let shape = match args.shape {
107 Shape::Sphere => gs::SelectionBundle::<GaussianPod>::create_sphere_bundle,
108 Shape::Box => gs::SelectionBundle::<GaussianPod>::create_box_bundle,
109 };
110 let repeat = args.repeat;
111 let offset = Vec3::from_slice(&args.offset);
112
113 log::debug!("Creating wgpu instance");
114 let instance =
115 wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
116
117 log::debug!("Requesting adapter");
118 let adapter = instance
119 .request_adapter(&wgpu::RequestAdapterOptions::default())
120 .await
121 .expect("adapter");
122
123 log::debug!("Requesting device");
124 let (device, queue) = adapter
125 .request_device(&wgpu::DeviceDescriptor {
126 label: Some("Device"),
127 required_limits: adapter.limits(),
128 ..Default::default()
129 })
130 .await
131 .expect("device");
132
133 log::debug!("Creating gaussians");
134 let gaussians = [
135 gs::core::GaussiansSource::Ply,
136 gs::core::GaussiansSource::Spz,
137 ]
138 .into_iter()
139 .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
140 .expect("gaussians");
141
142 log::debug!("Creating gaussians buffer");
143 let gaussians_buffer = gs::core::GaussiansBuffer::<GaussianPod>::new(&device, &gaussians);
144
145 log::debug!("Creating model transform buffer");
146 let model_transform = gs::core::ModelTransformBuffer::new(&device);
147
148 log::debug!("Creating Gaussian transform buffer");
149 let gaussian_transform = gs::core::GaussianTransformBuffer::new(&device);
150
151 log::debug!("Creating shape selection compute bundle");
152 let shape_selection = shape(&device);
153
154 log::debug!("Creating selection bundle");
155 let selection_bundle = gs::SelectionBundle::<GaussianPod>::new(&device, vec![shape_selection]);
156
157 log::debug!("Creating shape selection buffers");
158 let shape_selection_buffers = (0..repeat)
159 .map(|i| {
160 let offset_pos = pos + offset * i as f32;
161 let buffer = gs::InvTransformBuffer::new(&device);
162 buffer.update_with_scale_rot_pos(&queue, scale, rot, offset_pos);
163 buffer
164 })
165 .collect::<Vec<_>>();
166
167 log::debug!("Creating shape selection bind groups");
168 let shape_selection_bind_groups = shape_selection_buffers
169 .iter()
170 .map(|buffer| {
171 selection_bundle.bundles[0]
172 .create_bind_group(
173 &device,
174 // index 0 is the Gaussians buffer, so we use 1,
175 // see docs of create_sphere_bundle or create_box_bundle
176 1,
177 [buffer.buffer().as_entire_binding()],
178 )
179 .expect("bind group")
180 })
181 .collect::<Vec<_>>();
182
183 log::debug!("Creating selection expression");
184 let selection_expr = shape_selection_bind_groups.into_iter().fold(
185 gs::SelectionExpr::Identity,
186 |acc, bind_group| {
187 acc.union(gs::SelectionExpr::selection(
188 0, // the 0 here is the bundle index in the selection bundle
189 vec![bind_group],
190 ))
191 },
192 );
193
194 log::debug!("Creating destination buffer");
195 let dest = gs::SelectionBuffer::new(&device, gaussians_buffer.len() as u32);
196
197 log::info!("Starting selection process");
198 let time = std::time::Instant::now();
199
200 log::debug!("Selecting Gaussians");
201 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
202 label: Some("Selection Encoder"),
203 });
204
205 selection_bundle.evaluate(
206 &device,
207 &mut encoder,
208 &selection_expr,
209 &dest,
210 &model_transform,
211 &gaussian_transform,
212 &gaussians_buffer,
213 );
214
215 queue.submit(Some(encoder.finish()));
216
217 device
218 .poll(wgpu::PollType::wait_indefinitely())
219 .expect("poll");
220
221 log::info!("Editing process completed in {:?}", time.elapsed());
222
223 log::debug!("Filtering Gaussians");
224 let selected_gaussians = dest
225 .download::<u32>(&device, &queue)
226 .await
227 .expect("selected download")
228 .iter()
229 .flat_map(|group| {
230 std::iter::repeat_n(group, 32)
231 .enumerate()
232 .map(|(i, g)| g & (1 << i) != 0)
233 })
234 .zip(gaussians.iter_gaussian())
235 .filter(|(selected, _)| *selected)
236 .map(|(_, g)| g)
237 .collect::<Vec<_>>();
238
239 let selected_gaussians = match &args.output[args.output.len().saturating_sub(4)..] {
240 ".ply" => gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(
241 selected_gaussians.into_iter(),
242 )),
243 ".spz" => {
244 gs::core::Gaussians::Spz(
245 gs::core::SpzGaussians::from_gaussians_with_options(
246 selected_gaussians,
247 &gs::core::SpzGaussiansFromGaussianSliceOptions {
248 version: 2, // Version 2 is more widely supported as of now
249 ..Default::default()
250 },
251 )
252 .expect("SpzGaussians from gaussians"),
253 )
254 }
255 _ => panic!("Unsupported output file extension, expected .ply or .spz"),
256 };
257
258 log::debug!("Writing modified Gaussians to output file");
259 selected_gaussians
260 .write_to_file(&args.output)
261 .expect("write modified Gaussians to output file");
262
263 log::info!("Filtered Gaussians written to {}", args.output);
264}Sourcepub fn gaussians_bind_group_layout(&self) -> &BindGroupLayout
pub fn gaussians_bind_group_layout(&self) -> &BindGroupLayout
Get the Gaussians bind group layout.
Sourcepub fn evaluate(
&self,
device: &Device,
encoder: &mut CommandEncoder,
expr: &SelectionExpr,
dest: &SelectionBuffer,
model_transform: &ModelTransformBuffer,
gaussian_transform: &GaussianTransformBuffer,
gaussians: &GaussiansBuffer<G>,
)
pub fn evaluate( &self, device: &Device, encoder: &mut CommandEncoder, expr: &SelectionExpr, dest: &SelectionBuffer, model_transform: &ModelTransformBuffer, gaussian_transform: &GaussianTransformBuffer, gaussians: &GaussiansBuffer<G>, )
Evaluate and apply the selection expression.
Examples found in repository?
98async fn main() {
99 env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
100
101 let args = Args::parse();
102 let model_path = &args.model;
103 let pos = Vec3::from_slice(&args.pos);
104 let rot = Quat::from_slice(&args.rot);
105 let scale = Vec3::from_slice(&args.scale);
106 let shape = match args.shape {
107 Shape::Sphere => gs::SelectionBundle::<GaussianPod>::create_sphere_bundle,
108 Shape::Box => gs::SelectionBundle::<GaussianPod>::create_box_bundle,
109 };
110 let repeat = args.repeat;
111 let offset = Vec3::from_slice(&args.offset);
112
113 log::debug!("Creating wgpu instance");
114 let instance =
115 wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
116
117 log::debug!("Requesting adapter");
118 let adapter = instance
119 .request_adapter(&wgpu::RequestAdapterOptions::default())
120 .await
121 .expect("adapter");
122
123 log::debug!("Requesting device");
124 let (device, queue) = adapter
125 .request_device(&wgpu::DeviceDescriptor {
126 label: Some("Device"),
127 required_limits: adapter.limits(),
128 ..Default::default()
129 })
130 .await
131 .expect("device");
132
133 log::debug!("Creating gaussians");
134 let gaussians = [
135 gs::core::GaussiansSource::Ply,
136 gs::core::GaussiansSource::Spz,
137 ]
138 .into_iter()
139 .find_map(|source| gs::core::Gaussians::read_from_file(model_path, source).ok())
140 .expect("gaussians");
141
142 log::debug!("Creating gaussians buffer");
143 let gaussians_buffer = gs::core::GaussiansBuffer::<GaussianPod>::new(&device, &gaussians);
144
145 log::debug!("Creating model transform buffer");
146 let model_transform = gs::core::ModelTransformBuffer::new(&device);
147
148 log::debug!("Creating Gaussian transform buffer");
149 let gaussian_transform = gs::core::GaussianTransformBuffer::new(&device);
150
151 log::debug!("Creating shape selection compute bundle");
152 let shape_selection = shape(&device);
153
154 log::debug!("Creating selection bundle");
155 let selection_bundle = gs::SelectionBundle::<GaussianPod>::new(&device, vec![shape_selection]);
156
157 log::debug!("Creating shape selection buffers");
158 let shape_selection_buffers = (0..repeat)
159 .map(|i| {
160 let offset_pos = pos + offset * i as f32;
161 let buffer = gs::InvTransformBuffer::new(&device);
162 buffer.update_with_scale_rot_pos(&queue, scale, rot, offset_pos);
163 buffer
164 })
165 .collect::<Vec<_>>();
166
167 log::debug!("Creating shape selection bind groups");
168 let shape_selection_bind_groups = shape_selection_buffers
169 .iter()
170 .map(|buffer| {
171 selection_bundle.bundles[0]
172 .create_bind_group(
173 &device,
174 // index 0 is the Gaussians buffer, so we use 1,
175 // see docs of create_sphere_bundle or create_box_bundle
176 1,
177 [buffer.buffer().as_entire_binding()],
178 )
179 .expect("bind group")
180 })
181 .collect::<Vec<_>>();
182
183 log::debug!("Creating selection expression");
184 let selection_expr = shape_selection_bind_groups.into_iter().fold(
185 gs::SelectionExpr::Identity,
186 |acc, bind_group| {
187 acc.union(gs::SelectionExpr::selection(
188 0, // the 0 here is the bundle index in the selection bundle
189 vec![bind_group],
190 ))
191 },
192 );
193
194 log::debug!("Creating destination buffer");
195 let dest = gs::SelectionBuffer::new(&device, gaussians_buffer.len() as u32);
196
197 log::info!("Starting selection process");
198 let time = std::time::Instant::now();
199
200 log::debug!("Selecting Gaussians");
201 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
202 label: Some("Selection Encoder"),
203 });
204
205 selection_bundle.evaluate(
206 &device,
207 &mut encoder,
208 &selection_expr,
209 &dest,
210 &model_transform,
211 &gaussian_transform,
212 &gaussians_buffer,
213 );
214
215 queue.submit(Some(encoder.finish()));
216
217 device
218 .poll(wgpu::PollType::wait_indefinitely())
219 .expect("poll");
220
221 log::info!("Editing process completed in {:?}", time.elapsed());
222
223 log::debug!("Filtering Gaussians");
224 let selected_gaussians = dest
225 .download::<u32>(&device, &queue)
226 .await
227 .expect("selected download")
228 .iter()
229 .flat_map(|group| {
230 std::iter::repeat_n(group, 32)
231 .enumerate()
232 .map(|(i, g)| g & (1 << i) != 0)
233 })
234 .zip(gaussians.iter_gaussian())
235 .filter(|(selected, _)| *selected)
236 .map(|(_, g)| g)
237 .collect::<Vec<_>>();
238
239 let selected_gaussians = match &args.output[args.output.len().saturating_sub(4)..] {
240 ".ply" => gs::core::Gaussians::Ply(gs::core::PlyGaussians::from_iter(
241 selected_gaussians.into_iter(),
242 )),
243 ".spz" => {
244 gs::core::Gaussians::Spz(
245 gs::core::SpzGaussians::from_gaussians_with_options(
246 selected_gaussians,
247 &gs::core::SpzGaussiansFromGaussianSliceOptions {
248 version: 2, // Version 2 is more widely supported as of now
249 ..Default::default()
250 },
251 )
252 .expect("SpzGaussians from gaussians"),
253 )
254 }
255 _ => panic!("Unsupported output file extension, expected .ply or .spz"),
256 };
257
258 log::debug!("Writing modified Gaussians to output file");
259 selected_gaussians
260 .write_to_file(&args.output)
261 .expect("write modified Gaussians to output file");
262
263 log::info!("Filtered Gaussians written to {}", args.output);
264}Sourcepub fn create_primitive_bundle(device: &Device) -> ComputeBundle<()>
pub fn create_primitive_bundle(device: &Device) -> ComputeBundle<()>
Create the selection primitive operation ComputeBundle.
- Bind group 0 is
SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR.
You usually do not need to use this method, it is used internally for creating the primitive operation bundle for evaluation.
Sourcepub fn create_sphere_bundle(device: &Device) -> ComputeBundle<()>
pub fn create_sphere_bundle(device: &Device) -> ComputeBundle<()>
Create a sphere selection custom operation.
- Bind group 0 is
SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR. - Bind group 1 is
SelectionBundle::SPHERE_BIND_GROUP_LAYOUT_DESCRIPTOR.
Sourcepub fn create_box_bundle(device: &Device) -> ComputeBundle<()>
pub fn create_box_bundle(device: &Device) -> ComputeBundle<()>
Create a box selection custom operation.
- Bind group 0 is
SelectionBundle::GAUSSIANS_BIND_GROUP_LAYOUT_DESCRIPTOR. - Bind group 1 is
SelectionBundle::BOX_BIND_GROUP_LAYOUT_DESCRIPTOR.
Trait Implementations§
Auto Trait Implementations§
impl<G> Freeze for SelectionBundle<G>
impl<G> !RefUnwindSafe for SelectionBundle<G>
impl<G> Send for SelectionBundle<G>
impl<G> Sync for SelectionBundle<G>
impl<G> Unpin for SelectionBundle<G>where
G: Unpin,
impl<G> UnsafeUnpin for SelectionBundle<G>
impl<G> !UnwindSafe for SelectionBundle<G>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more