pub struct RenderDevice { /* private fields */ }
Expand description
This GPU device is responsible for the creation of most rendering and compute resources.
Implementations§
Source§impl RenderDevice
impl RenderDevice
pub fn new(device: WgpuWrapper<Device>) -> RenderDevice
Sourcepub fn features(&self) -> Features
pub fn features(&self) -> Features
List all Features
that may be used with this device.
Functions may panic if you use unsupported features.
Examples found in repository?
45 fn finish(&self, app: &mut App) {
46 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
47 return;
48 };
49
50 let render_device = render_app.world().resource::<RenderDevice>();
51
52 // Check if the device support the required feature. If not, exit the example.
53 // In a real application, you should setup a fallback for the missing feature
54 if !render_device
55 .features()
56 .contains(WgpuFeatures::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING)
57 {
58 error!(
59 "Render device doesn't support feature \
60SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, \
61which is required for texture binding arrays"
62 );
63 exit(1);
64 }
65 }
More examples
142 fn from_world(world: &mut World) -> SavedIndirectParameters {
143 let render_device = world.resource::<RenderDevice>();
144 SavedIndirectParameters(Arc::new(Mutex::new(SavedIndirectParametersData {
145 data: vec![],
146 count: 0,
147 // This gets set to false in `readback_indirect_buffers` if we don't
148 // support GPU preprocessing.
149 occlusion_culling_supported: true,
150 // In order to determine how many meshes were culled, we look at the
151 // indirect count buffer that Bevy only populates if the platform
152 // supports `multi_draw_indirect_count`. So, if we don't have that
153 // feature, then we don't bother to display how many meshes were
154 // culled.
155 occlusion_culling_introspection_supported: render_device
156 .features()
157 .contains(WgpuFeatures::MULTI_DRAW_INDIRECT_COUNT),
158 })))
159 }
101fn cycle_cubemap_asset(
102 time: Res<Time>,
103 mut next_swap: Local<f32>,
104 mut cubemap: ResMut<Cubemap>,
105 asset_server: Res<AssetServer>,
106 render_device: Res<RenderDevice>,
107) {
108 let now = time.elapsed_secs();
109 if *next_swap == 0.0 {
110 *next_swap = now + CUBEMAP_SWAP_DELAY;
111 return;
112 } else if now < *next_swap {
113 return;
114 }
115 *next_swap += CUBEMAP_SWAP_DELAY;
116
117 let supported_compressed_formats =
118 CompressedImageFormats::from_features(render_device.features());
119
120 let mut new_index = cubemap.index;
121 for _ in 0..CUBEMAPS.len() {
122 new_index = (new_index + 1) % CUBEMAPS.len();
123 if supported_compressed_formats.contains(CUBEMAPS[new_index].1) {
124 break;
125 }
126 info!(
127 "Skipping format which is not supported by current hardware: {:?}",
128 CUBEMAPS[new_index]
129 );
130 }
131
132 // Skip swapping to the same texture. Useful for when ktx2, zstd, or compressed texture support
133 // is missing
134 if new_index == cubemap.index {
135 return;
136 }
137
138 cubemap.index = new_index;
139 cubemap.image_handle = asset_server.load(CUBEMAPS[cubemap.index].0);
140 cubemap.is_loaded = false;
141}
Sourcepub fn limits(&self) -> Limits
pub fn limits(&self) -> Limits
List all Limits
that were requested of this device.
If any of these limits are exceeded, functions may panic.
Sourcepub unsafe fn create_shader_module(
&self,
desc: ShaderModuleDescriptor<'_>,
) -> ShaderModule
pub unsafe fn create_shader_module( &self, desc: ShaderModuleDescriptor<'_>, ) -> ShaderModule
Creates a ShaderModule
from either SPIR-V or WGSL source code.
§Safety
Creates a shader module with user-customizable runtime checks which allows shaders to perform operations which can lead to undefined behavior like indexing out of bounds, To avoid UB, ensure any unchecked shaders are sound! This method should never be called for user-supplied shaders.
Sourcepub fn create_and_validate_shader_module(
&self,
desc: ShaderModuleDescriptor<'_>,
) -> ShaderModule
pub fn create_and_validate_shader_module( &self, desc: ShaderModuleDescriptor<'_>, ) -> ShaderModule
Creates and validates a ShaderModule
from either SPIR-V or WGSL source code.
See ValidateShader
for more information on the tradeoffs involved with shader validation.
Sourcepub fn poll(&self, maintain: Maintain<SubmissionIndex>) -> MaintainResult
pub fn poll(&self, maintain: Maintain<SubmissionIndex>) -> MaintainResult
Check for resource cleanups and mapping callbacks.
Return true
if the queue is empty, or false
if there are more queue
submissions still in flight. (Note that, unless access to the wgpu::Queue
is
coordinated somehow, this information could be out of date by the time
the caller receives it. Queue
s can be shared between threads, so
other threads could submit new work at any time.)
no-op on the web, device is automatically polled.
Examples found in repository?
399fn receive_image_from_buffer(
400 image_copiers: Res<ImageCopiers>,
401 render_device: Res<RenderDevice>,
402 sender: Res<RenderWorldSender>,
403) {
404 for image_copier in image_copiers.0.iter() {
405 if !image_copier.enabled() {
406 continue;
407 }
408
409 // Finally time to get our data back from the gpu.
410 // First we get a buffer slice which represents a chunk of the buffer (which we
411 // can't access yet).
412 // We want the whole thing so use unbounded range.
413 let buffer_slice = image_copier.buffer.slice(..);
414
415 // Now things get complicated. WebGPU, for safety reasons, only allows either the GPU
416 // or CPU to access a buffer's contents at a time. We need to "map" the buffer which means
417 // flipping ownership of the buffer over to the CPU and making access legal. We do this
418 // with `BufferSlice::map_async`.
419 //
420 // The problem is that map_async is not an async function so we can't await it. What
421 // we need to do instead is pass in a closure that will be executed when the slice is
422 // either mapped or the mapping has failed.
423 //
424 // The problem with this is that we don't have a reliable way to wait in the main
425 // code for the buffer to be mapped and even worse, calling get_mapped_range or
426 // get_mapped_range_mut prematurely will cause a panic, not return an error.
427 //
428 // Using channels solves this as awaiting the receiving of a message from
429 // the passed closure will force the outside code to wait. It also doesn't hurt
430 // if the closure finishes before the outside code catches up as the message is
431 // buffered and receiving will just pick that up.
432 //
433 // It may also be worth noting that although on native, the usage of asynchronous
434 // channels is wholly unnecessary, for the sake of portability to Wasm
435 // we'll use async channels that work on both native and Wasm.
436
437 let (s, r) = crossbeam_channel::bounded(1);
438
439 // Maps the buffer so it can be read on the cpu
440 buffer_slice.map_async(MapMode::Read, move |r| match r {
441 // This will execute once the gpu is ready, so after the call to poll()
442 Ok(r) => s.send(r).expect("Failed to send map update"),
443 Err(err) => panic!("Failed to map buffer {err}"),
444 });
445
446 // In order for the mapping to be completed, one of three things must happen.
447 // One of those can be calling `Device::poll`. This isn't necessary on the web as devices
448 // are polled automatically but natively, we need to make sure this happens manually.
449 // `Maintain::Wait` will cause the thread to wait on native but not on WebGpu.
450
451 // This blocks until the gpu is done executing everything
452 render_device.poll(Maintain::wait()).panic_on_timeout();
453
454 // This blocks until the buffer is mapped
455 r.recv().expect("Failed to receive the map_async message");
456
457 // This could fail on app exit, if Main world clears resources (including receiver) while Render world still renders
458 let _ = sender.send(buffer_slice.get_mapped_range().to_vec());
459
460 // We need to make sure all `BufferView`'s are dropped before we do what we're about
461 // to do.
462 // Unmap so that we can copy to the staging buffer in the next iteration.
463 image_copier.buffer.unmap();
464 }
465}
Sourcepub fn create_command_encoder(
&self,
desc: &CommandEncoderDescriptor<Option<&str>>,
) -> CommandEncoder
pub fn create_command_encoder( &self, desc: &CommandEncoderDescriptor<Option<&str>>, ) -> CommandEncoder
Creates an empty CommandEncoder
.
Examples found in repository?
340 fn run(
341 &self,
342 _graph: &mut RenderGraphContext,
343 render_context: &mut RenderContext,
344 world: &World,
345 ) -> Result<(), NodeRunError> {
346 let image_copiers = world.get_resource::<ImageCopiers>().unwrap();
347 let gpu_images = world
348 .get_resource::<RenderAssets<bevy::render::texture::GpuImage>>()
349 .unwrap();
350
351 for image_copier in image_copiers.iter() {
352 if !image_copier.enabled() {
353 continue;
354 }
355
356 let src_image = gpu_images.get(&image_copier.src_image).unwrap();
357
358 let mut encoder = render_context
359 .render_device()
360 .create_command_encoder(&CommandEncoderDescriptor::default());
361
362 let block_dimensions = src_image.texture_format.block_dimensions();
363 let block_size = src_image.texture_format.block_copy_size(None).unwrap();
364
365 // Calculating correct size of image row because
366 // copy_texture_to_buffer can copy image only by rows aligned wgpu::COPY_BYTES_PER_ROW_ALIGNMENT
367 // That's why image in buffer can be little bit wider
368 // This should be taken into account at copy from buffer stage
369 let padded_bytes_per_row = RenderDevice::align_copy_bytes_per_row(
370 (src_image.size.width as usize / block_dimensions.0 as usize) * block_size as usize,
371 );
372
373 encoder.copy_texture_to_buffer(
374 src_image.texture.as_image_copy(),
375 TexelCopyBufferInfo {
376 buffer: &image_copier.buffer,
377 layout: TexelCopyBufferLayout {
378 offset: 0,
379 bytes_per_row: Some(
380 std::num::NonZero::<u32>::new(padded_bytes_per_row as u32)
381 .unwrap()
382 .into(),
383 ),
384 rows_per_image: None,
385 },
386 },
387 src_image.size,
388 );
389
390 let render_queue = world.get_resource::<RenderQueue>().unwrap();
391 render_queue.submit(std::iter::once(encoder.finish()));
392 }
393
394 Ok(())
395 }
Sourcepub fn create_render_bundle_encoder(
&self,
desc: &RenderBundleEncoderDescriptor<'_>,
) -> RenderBundleEncoder<'_>
pub fn create_render_bundle_encoder( &self, desc: &RenderBundleEncoderDescriptor<'_>, ) -> RenderBundleEncoder<'_>
Creates an empty RenderBundleEncoder
.
Sourcepub fn create_bind_group<'a>(
&self,
label: impl Into<Option<&'a str>>,
layout: &'a BindGroupLayout,
entries: &'a [BindGroupEntry<'a>],
) -> BindGroup
pub fn create_bind_group<'a>( &self, label: impl Into<Option<&'a str>>, layout: &'a BindGroupLayout, entries: &'a [BindGroupEntry<'a>], ) -> BindGroup
Creates a new BindGroup
.
Examples found in repository?
133fn prepare_bind_group(
134 mut commands: Commands,
135 pipeline: Res<ComputePipeline>,
136 render_device: Res<RenderDevice>,
137 buffer: Res<ReadbackBuffer>,
138 image: Res<ReadbackImage>,
139 buffers: Res<RenderAssets<GpuShaderStorageBuffer>>,
140 images: Res<RenderAssets<GpuImage>>,
141) {
142 let buffer = buffers.get(&buffer.0).unwrap();
143 let image = images.get(&image.0).unwrap();
144 let bind_group = render_device.create_bind_group(
145 None,
146 &pipeline.layout,
147 &BindGroupEntries::sequential((
148 buffer.buffer.as_entire_buffer_binding(),
149 image.texture_view.into_binding(),
150 )),
151 );
152 commands.insert_resource(GpuBufferBindGroup(bind_group));
153}
More examples
131fn prepare_bind_group(
132 mut commands: Commands,
133 pipeline: Res<GameOfLifePipeline>,
134 gpu_images: Res<RenderAssets<GpuImage>>,
135 game_of_life_images: Res<GameOfLifeImages>,
136 render_device: Res<RenderDevice>,
137) {
138 let view_a = gpu_images.get(&game_of_life_images.texture_a).unwrap();
139 let view_b = gpu_images.get(&game_of_life_images.texture_b).unwrap();
140 let bind_group_0 = render_device.create_bind_group(
141 None,
142 &pipeline.texture_bind_group_layout,
143 &BindGroupEntries::sequential((&view_a.texture_view, &view_b.texture_view)),
144 );
145 let bind_group_1 = render_device.create_bind_group(
146 None,
147 &pipeline.texture_bind_group_layout,
148 &BindGroupEntries::sequential((&view_b.texture_view, &view_a.texture_view)),
149 );
150 commands.insert_resource(GameOfLifeImageBindGroups([bind_group_0, bind_group_1]));
151}
102 fn as_bind_group(
103 &self,
104 layout: &BindGroupLayout,
105 render_device: &RenderDevice,
106 (image_assets, fallback_image): &mut SystemParamItem<'_, '_, Self::Param>,
107 ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
108 // retrieve the render resources from handles
109 let mut images = vec![];
110 for handle in self.textures.iter().take(MAX_TEXTURE_COUNT) {
111 match image_assets.get(handle) {
112 Some(image) => images.push(image),
113 None => return Err(AsBindGroupError::RetryNextUpdate),
114 }
115 }
116
117 let fallback_image = &fallback_image.d2;
118
119 let textures = vec![&fallback_image.texture_view; MAX_TEXTURE_COUNT];
120
121 // convert bevy's resource types to WGPU's references
122 let mut textures: Vec<_> = textures.into_iter().map(|texture| &**texture).collect();
123
124 // fill in up to the first `MAX_TEXTURE_COUNT` textures and samplers to the arrays
125 for (id, image) in images.into_iter().enumerate() {
126 textures[id] = &*image.texture_view;
127 }
128
129 let bind_group = render_device.create_bind_group(
130 "bindless_material_bind_group",
131 layout,
132 &BindGroupEntries::sequential((&textures[..], &fallback_image.sampler)),
133 );
134
135 Ok(PreparedBindGroup {
136 bindings: BindingResources(vec![]),
137 bind_group,
138 data: (),
139 })
140 }
142 fn run(
143 &self,
144 _graph: &mut RenderGraphContext,
145 render_context: &mut RenderContext,
146 (view_target, _post_process_settings, settings_index): QueryItem<Self::ViewQuery>,
147 world: &World,
148 ) -> Result<(), NodeRunError> {
149 // Get the pipeline resource that contains the global data we need
150 // to create the render pipeline
151 let post_process_pipeline = world.resource::<PostProcessPipeline>();
152
153 // The pipeline cache is a cache of all previously created pipelines.
154 // It is required to avoid creating a new pipeline each frame,
155 // which is expensive due to shader compilation.
156 let pipeline_cache = world.resource::<PipelineCache>();
157
158 // Get the pipeline from the cache
159 let Some(pipeline) = pipeline_cache.get_render_pipeline(post_process_pipeline.pipeline_id)
160 else {
161 return Ok(());
162 };
163
164 // Get the settings uniform binding
165 let settings_uniforms = world.resource::<ComponentUniforms<PostProcessSettings>>();
166 let Some(settings_binding) = settings_uniforms.uniforms().binding() else {
167 return Ok(());
168 };
169
170 // This will start a new "post process write", obtaining two texture
171 // views from the view target - a `source` and a `destination`.
172 // `source` is the "current" main texture and you _must_ write into
173 // `destination` because calling `post_process_write()` on the
174 // [`ViewTarget`] will internally flip the [`ViewTarget`]'s main
175 // texture to the `destination` texture. Failing to do so will cause
176 // the current main texture information to be lost.
177 let post_process = view_target.post_process_write();
178
179 // The bind_group gets created each frame.
180 //
181 // Normally, you would create a bind_group in the Queue set,
182 // but this doesn't work with the post_process_write().
183 // The reason it doesn't work is because each post_process_write will alternate the source/destination.
184 // The only way to have the correct source/destination for the bind_group
185 // is to make sure you get it during the node execution.
186 let bind_group = render_context.render_device().create_bind_group(
187 "post_process_bind_group",
188 &post_process_pipeline.layout,
189 // It's important for this to match the BindGroupLayout defined in the PostProcessPipeline
190 &BindGroupEntries::sequential((
191 // Make sure to use the source view
192 post_process.source,
193 // Use the sampler created for the pipeline
194 &post_process_pipeline.sampler,
195 // Set the settings binding
196 settings_binding.clone(),
197 )),
198 );
199
200 // Begin the render pass
201 let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
202 label: Some("post_process_pass"),
203 color_attachments: &[Some(RenderPassColorAttachment {
204 // We need to specify the post process destination view here
205 // to make sure we write to the appropriate texture.
206 view: post_process.destination,
207 resolve_target: None,
208 ops: Operations::default(),
209 })],
210 depth_stencil_attachment: None,
211 timestamp_writes: None,
212 occlusion_query_set: None,
213 });
214
215 // This is mostly just wgpu boilerplate for drawing a fullscreen triangle,
216 // using the pipeline/bind_group created above
217 render_pass.set_render_pipeline(pipeline);
218 // By passing in the index of the post process settings on this view, we ensure
219 // that in the event that multiple settings were sent to the GPU (as would be the
220 // case with multiple cameras), we use the correct one.
221 render_pass.set_bind_group(0, &bind_group, &[settings_index.index()]);
222 render_pass.draw(0..3, 0..1);
223
224 Ok(())
225 }
Sourcepub fn create_bind_group_layout<'a>(
&self,
label: impl Into<Option<&'a str>>,
entries: &'a [BindGroupLayoutEntry],
) -> BindGroupLayout
pub fn create_bind_group_layout<'a>( &self, label: impl Into<Option<&'a str>>, entries: &'a [BindGroupLayoutEntry], ) -> BindGroupLayout
Creates a BindGroupLayout
.
Examples found in repository?
162 fn from_world(world: &mut World) -> Self {
163 let render_device = world.resource::<RenderDevice>();
164 let layout = render_device.create_bind_group_layout(
165 None,
166 &BindGroupLayoutEntries::sequential(
167 ShaderStages::COMPUTE,
168 (
169 storage_buffer::<Vec<u32>>(false),
170 texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly),
171 ),
172 ),
173 );
174 let shader = world.load_asset(SHADER_ASSET_PATH);
175 let pipeline_cache = world.resource::<PipelineCache>();
176 let pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
177 label: Some("GPU readback compute shader".into()),
178 layout: vec![layout.clone()],
179 push_constant_ranges: Vec::new(),
180 shader: shader.clone(),
181 shader_defs: Vec::new(),
182 entry_point: "main".into(),
183 zero_initialize_workgroup_memory: false,
184 });
185 ComputePipeline { layout, pipeline }
186 }
More examples
161 fn from_world(world: &mut World) -> Self {
162 let render_device = world.resource::<RenderDevice>();
163 let texture_bind_group_layout = render_device.create_bind_group_layout(
164 "GameOfLifeImages",
165 &BindGroupLayoutEntries::sequential(
166 ShaderStages::COMPUTE,
167 (
168 texture_storage_2d(TextureFormat::R32Float, StorageTextureAccess::ReadOnly),
169 texture_storage_2d(TextureFormat::R32Float, StorageTextureAccess::WriteOnly),
170 ),
171 ),
172 );
173 let shader = world.load_asset(SHADER_ASSET_PATH);
174 let pipeline_cache = world.resource::<PipelineCache>();
175 let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
176 label: None,
177 layout: vec![texture_bind_group_layout.clone()],
178 push_constant_ranges: Vec::new(),
179 shader: shader.clone(),
180 shader_defs: vec![],
181 entry_point: Cow::from("init"),
182 zero_initialize_workgroup_memory: false,
183 });
184 let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
185 label: None,
186 layout: vec![texture_bind_group_layout.clone()],
187 push_constant_ranges: Vec::new(),
188 shader,
189 shader_defs: vec![],
190 entry_point: Cow::from("update"),
191 zero_initialize_workgroup_memory: false,
192 });
193
194 GameOfLifePipeline {
195 texture_bind_group_layout,
196 init_pipeline,
197 update_pipeline,
198 }
199 }
237 fn from_world(world: &mut World) -> Self {
238 let render_device = world.resource::<RenderDevice>();
239
240 // We need to define the bind group layout used for our pipeline
241 let layout = render_device.create_bind_group_layout(
242 "post_process_bind_group_layout",
243 &BindGroupLayoutEntries::sequential(
244 // The layout entries will only be visible in the fragment stage
245 ShaderStages::FRAGMENT,
246 (
247 // The screen texture
248 texture_2d(TextureSampleType::Float { filterable: true }),
249 // The sampler that will be used to sample the screen texture
250 sampler(SamplerBindingType::Filtering),
251 // The settings uniform that will control the effect
252 uniform_buffer::<PostProcessSettings>(true),
253 ),
254 ),
255 );
256
257 // We can create the sampler here since it won't change at runtime and doesn't depend on the view
258 let sampler = render_device.create_sampler(&SamplerDescriptor::default());
259
260 // Get the shader handle
261 let shader = world.load_asset(SHADER_ASSET_PATH);
262
263 let pipeline_id = world
264 .resource_mut::<PipelineCache>()
265 // This will add the pipeline to the cache and queue its creation
266 .queue_render_pipeline(RenderPipelineDescriptor {
267 label: Some("post_process_pipeline".into()),
268 layout: vec![layout.clone()],
269 // This will setup a fullscreen triangle for the vertex state
270 vertex: fullscreen_shader_vertex_state(),
271 fragment: Some(FragmentState {
272 shader,
273 shader_defs: vec![],
274 // Make sure this matches the entry point of your shader.
275 // It can be anything as long as it matches here and in the shader.
276 entry_point: "fragment".into(),
277 targets: vec![Some(ColorTargetState {
278 format: TextureFormat::bevy_default(),
279 blend: None,
280 write_mask: ColorWrites::ALL,
281 })],
282 }),
283 // All of the following properties are not important for this effect so just use the default values.
284 // This struct doesn't have the Default trait implemented because not all fields can have a default value.
285 primitive: PrimitiveState::default(),
286 depth_stencil: None,
287 multisample: MultisampleState::default(),
288 push_constant_ranges: vec![],
289 zero_initialize_workgroup_memory: false,
290 });
291
292 Self {
293 layout,
294 sampler,
295 pipeline_id,
296 }
297 }
Sourcepub fn create_pipeline_layout(
&self,
desc: &PipelineLayoutDescriptor<'_>,
) -> PipelineLayout
pub fn create_pipeline_layout( &self, desc: &PipelineLayoutDescriptor<'_>, ) -> PipelineLayout
Creates a PipelineLayout
.
Sourcepub fn create_render_pipeline(
&self,
desc: &RenderPipelineDescriptor<'_>,
) -> RenderPipeline
pub fn create_render_pipeline( &self, desc: &RenderPipelineDescriptor<'_>, ) -> RenderPipeline
Creates a RenderPipeline
.
Sourcepub fn create_compute_pipeline(
&self,
desc: &ComputePipelineDescriptor<'_>,
) -> ComputePipeline
pub fn create_compute_pipeline( &self, desc: &ComputePipelineDescriptor<'_>, ) -> ComputePipeline
Creates a ComputePipeline
.
Sourcepub fn create_buffer(&self, desc: &BufferDescriptor<Option<&str>>) -> Buffer
pub fn create_buffer(&self, desc: &BufferDescriptor<Option<&str>>) -> Buffer
Creates a Buffer
.
Examples found in repository?
296 pub fn new(
297 src_image: Handle<Image>,
298 size: Extent3d,
299 render_device: &RenderDevice,
300 ) -> ImageCopier {
301 let padded_bytes_per_row =
302 RenderDevice::align_copy_bytes_per_row((size.width) as usize) * 4;
303
304 let cpu_buffer = render_device.create_buffer(&BufferDescriptor {
305 label: None,
306 size: padded_bytes_per_row as u64 * size.height as u64,
307 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
308 mapped_at_creation: false,
309 });
310
311 ImageCopier {
312 buffer: cpu_buffer,
313 src_image,
314 enabled: Arc::new(AtomicBool::new(true)),
315 }
316 }
More examples
493fn create_indirect_parameters_staging_buffers(
494 mut indirect_parameters_staging_buffers: ResMut<IndirectParametersStagingBuffers>,
495 indirect_parameters_buffers: Res<IndirectParametersBuffers>,
496 render_device: Res<RenderDevice>,
497) {
498 let Some(phase_indirect_parameters_buffers) =
499 indirect_parameters_buffers.get(&TypeId::of::<Opaque3d>())
500 else {
501 return;
502 };
503
504 // Fetch the indirect parameters buffers that we're going to copy from.
505 let (Some(indexed_data_buffer), Some(indexed_batch_set_buffer)) = (
506 phase_indirect_parameters_buffers.indexed.data_buffer(),
507 phase_indirect_parameters_buffers
508 .indexed
509 .batch_sets_buffer(),
510 ) else {
511 return;
512 };
513
514 // Build the staging buffers. Make sure they have the same sizes as the
515 // buffers we're copying from.
516 indirect_parameters_staging_buffers.data =
517 Some(render_device.create_buffer(&BufferDescriptor {
518 label: Some("indexed data staging buffer"),
519 size: indexed_data_buffer.size(),
520 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
521 mapped_at_creation: false,
522 }));
523 indirect_parameters_staging_buffers.batch_sets =
524 Some(render_device.create_buffer(&BufferDescriptor {
525 label: Some("indexed batch set staging buffer"),
526 size: indexed_batch_set_buffer.size(),
527 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
528 mapped_at_creation: false,
529 }));
530}
Sourcepub fn create_buffer_with_data(&self, desc: &BufferInitDescriptor<'_>) -> Buffer
pub fn create_buffer_with_data(&self, desc: &BufferInitDescriptor<'_>) -> Buffer
Creates a Buffer
and initializes it with the specified data.
Examples found in repository?
181fn prepare_instance_buffers(
182 mut commands: Commands,
183 query: Query<(Entity, &InstanceMaterialData)>,
184 render_device: Res<RenderDevice>,
185) {
186 for (entity, instance_data) in &query {
187 let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
188 label: Some("instance data buffer"),
189 contents: bytemuck::cast_slice(instance_data.as_slice()),
190 usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
191 });
192 commands.entity(entity).insert(InstanceBuffer {
193 buffer,
194 length: instance_data.len(),
195 });
196 }
197}
Sourcepub fn create_texture_with_data(
&self,
render_queue: &RenderQueue,
desc: &TextureDescriptor<Option<&str>, &[TextureFormat]>,
order: TextureDataOrder,
data: &[u8],
) -> Texture
pub fn create_texture_with_data( &self, render_queue: &RenderQueue, desc: &TextureDescriptor<Option<&str>, &[TextureFormat]>, order: TextureDataOrder, data: &[u8], ) -> Texture
Creates a new Texture
and initializes it with the specified data.
desc
specifies the general format of the texture.
data
is the raw data.
Sourcepub fn create_texture(
&self,
desc: &TextureDescriptor<Option<&str>, &[TextureFormat]>,
) -> Texture
pub fn create_texture( &self, desc: &TextureDescriptor<Option<&str>, &[TextureFormat]>, ) -> Texture
Creates a new Texture
.
desc
specifies the general format of the texture.
Sourcepub fn create_sampler(&self, desc: &SamplerDescriptor<Option<&str>>) -> Sampler
pub fn create_sampler(&self, desc: &SamplerDescriptor<Option<&str>>) -> Sampler
Creates a new Sampler
.
desc
specifies the behavior of the sampler.
Examples found in repository?
237 fn from_world(world: &mut World) -> Self {
238 let render_device = world.resource::<RenderDevice>();
239
240 // We need to define the bind group layout used for our pipeline
241 let layout = render_device.create_bind_group_layout(
242 "post_process_bind_group_layout",
243 &BindGroupLayoutEntries::sequential(
244 // The layout entries will only be visible in the fragment stage
245 ShaderStages::FRAGMENT,
246 (
247 // The screen texture
248 texture_2d(TextureSampleType::Float { filterable: true }),
249 // The sampler that will be used to sample the screen texture
250 sampler(SamplerBindingType::Filtering),
251 // The settings uniform that will control the effect
252 uniform_buffer::<PostProcessSettings>(true),
253 ),
254 ),
255 );
256
257 // We can create the sampler here since it won't change at runtime and doesn't depend on the view
258 let sampler = render_device.create_sampler(&SamplerDescriptor::default());
259
260 // Get the shader handle
261 let shader = world.load_asset(SHADER_ASSET_PATH);
262
263 let pipeline_id = world
264 .resource_mut::<PipelineCache>()
265 // This will add the pipeline to the cache and queue its creation
266 .queue_render_pipeline(RenderPipelineDescriptor {
267 label: Some("post_process_pipeline".into()),
268 layout: vec![layout.clone()],
269 // This will setup a fullscreen triangle for the vertex state
270 vertex: fullscreen_shader_vertex_state(),
271 fragment: Some(FragmentState {
272 shader,
273 shader_defs: vec![],
274 // Make sure this matches the entry point of your shader.
275 // It can be anything as long as it matches here and in the shader.
276 entry_point: "fragment".into(),
277 targets: vec![Some(ColorTargetState {
278 format: TextureFormat::bevy_default(),
279 blend: None,
280 write_mask: ColorWrites::ALL,
281 })],
282 }),
283 // All of the following properties are not important for this effect so just use the default values.
284 // This struct doesn't have the Default trait implemented because not all fields can have a default value.
285 primitive: PrimitiveState::default(),
286 depth_stencil: None,
287 multisample: MultisampleState::default(),
288 push_constant_ranges: vec![],
289 zero_initialize_workgroup_memory: false,
290 });
291
292 Self {
293 layout,
294 sampler,
295 pipeline_id,
296 }
297 }
Sourcepub fn configure_surface(
&self,
surface: &Surface<'_>,
config: &SurfaceConfiguration<Vec<TextureFormat>>,
)
pub fn configure_surface( &self, surface: &Surface<'_>, config: &SurfaceConfiguration<Vec<TextureFormat>>, )
Initializes Surface
for presentation.
§Panics
- A old
SurfaceTexture
is still alive referencing an old surface. - Texture format requested is unsupported on the surface.
Sourcepub fn wgpu_device(&self) -> &Device
pub fn wgpu_device(&self) -> &Device
Returns the wgpu Device
.
pub fn map_buffer( &self, buffer: &BufferSlice<'_>, map_mode: MapMode, callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static, )
Sourcepub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize
pub const fn align_copy_bytes_per_row(row_bytes: usize) -> usize
Examples found in repository?
296 pub fn new(
297 src_image: Handle<Image>,
298 size: Extent3d,
299 render_device: &RenderDevice,
300 ) -> ImageCopier {
301 let padded_bytes_per_row =
302 RenderDevice::align_copy_bytes_per_row((size.width) as usize) * 4;
303
304 let cpu_buffer = render_device.create_buffer(&BufferDescriptor {
305 label: None,
306 size: padded_bytes_per_row as u64 * size.height as u64,
307 usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
308 mapped_at_creation: false,
309 });
310
311 ImageCopier {
312 buffer: cpu_buffer,
313 src_image,
314 enabled: Arc::new(AtomicBool::new(true)),
315 }
316 }
317
318 pub fn enabled(&self) -> bool {
319 self.enabled.load(Ordering::Relaxed)
320 }
321}
322
323/// Extracting `ImageCopier`s into render world, because `ImageCopyDriver` accesses them
324fn image_copy_extract(mut commands: Commands, image_copiers: Extract<Query<&ImageCopier>>) {
325 commands.insert_resource(ImageCopiers(
326 image_copiers.iter().cloned().collect::<Vec<ImageCopier>>(),
327 ));
328}
329
330/// `RenderGraph` label for `ImageCopyDriver`
331#[derive(Debug, PartialEq, Eq, Clone, Hash, RenderLabel)]
332struct ImageCopy;
333
334/// `RenderGraph` node
335#[derive(Default)]
336struct ImageCopyDriver;
337
338// Copies image content from render target to buffer
339impl render_graph::Node for ImageCopyDriver {
340 fn run(
341 &self,
342 _graph: &mut RenderGraphContext,
343 render_context: &mut RenderContext,
344 world: &World,
345 ) -> Result<(), NodeRunError> {
346 let image_copiers = world.get_resource::<ImageCopiers>().unwrap();
347 let gpu_images = world
348 .get_resource::<RenderAssets<bevy::render::texture::GpuImage>>()
349 .unwrap();
350
351 for image_copier in image_copiers.iter() {
352 if !image_copier.enabled() {
353 continue;
354 }
355
356 let src_image = gpu_images.get(&image_copier.src_image).unwrap();
357
358 let mut encoder = render_context
359 .render_device()
360 .create_command_encoder(&CommandEncoderDescriptor::default());
361
362 let block_dimensions = src_image.texture_format.block_dimensions();
363 let block_size = src_image.texture_format.block_copy_size(None).unwrap();
364
365 // Calculating correct size of image row because
366 // copy_texture_to_buffer can copy image only by rows aligned wgpu::COPY_BYTES_PER_ROW_ALIGNMENT
367 // That's why image in buffer can be little bit wider
368 // This should be taken into account at copy from buffer stage
369 let padded_bytes_per_row = RenderDevice::align_copy_bytes_per_row(
370 (src_image.size.width as usize / block_dimensions.0 as usize) * block_size as usize,
371 );
372
373 encoder.copy_texture_to_buffer(
374 src_image.texture.as_image_copy(),
375 TexelCopyBufferInfo {
376 buffer: &image_copier.buffer,
377 layout: TexelCopyBufferLayout {
378 offset: 0,
379 bytes_per_row: Some(
380 std::num::NonZero::<u32>::new(padded_bytes_per_row as u32)
381 .unwrap()
382 .into(),
383 ),
384 rows_per_image: None,
385 },
386 },
387 src_image.size,
388 );
389
390 let render_queue = world.get_resource::<RenderQueue>().unwrap();
391 render_queue.submit(std::iter::once(encoder.finish()));
392 }
393
394 Ok(())
395 }
396}
397
398/// runs in render world after Render stage to send image from buffer via channel (receiver is in main world)
399fn receive_image_from_buffer(
400 image_copiers: Res<ImageCopiers>,
401 render_device: Res<RenderDevice>,
402 sender: Res<RenderWorldSender>,
403) {
404 for image_copier in image_copiers.0.iter() {
405 if !image_copier.enabled() {
406 continue;
407 }
408
409 // Finally time to get our data back from the gpu.
410 // First we get a buffer slice which represents a chunk of the buffer (which we
411 // can't access yet).
412 // We want the whole thing so use unbounded range.
413 let buffer_slice = image_copier.buffer.slice(..);
414
415 // Now things get complicated. WebGPU, for safety reasons, only allows either the GPU
416 // or CPU to access a buffer's contents at a time. We need to "map" the buffer which means
417 // flipping ownership of the buffer over to the CPU and making access legal. We do this
418 // with `BufferSlice::map_async`.
419 //
420 // The problem is that map_async is not an async function so we can't await it. What
421 // we need to do instead is pass in a closure that will be executed when the slice is
422 // either mapped or the mapping has failed.
423 //
424 // The problem with this is that we don't have a reliable way to wait in the main
425 // code for the buffer to be mapped and even worse, calling get_mapped_range or
426 // get_mapped_range_mut prematurely will cause a panic, not return an error.
427 //
428 // Using channels solves this as awaiting the receiving of a message from
429 // the passed closure will force the outside code to wait. It also doesn't hurt
430 // if the closure finishes before the outside code catches up as the message is
431 // buffered and receiving will just pick that up.
432 //
433 // It may also be worth noting that although on native, the usage of asynchronous
434 // channels is wholly unnecessary, for the sake of portability to Wasm
435 // we'll use async channels that work on both native and Wasm.
436
437 let (s, r) = crossbeam_channel::bounded(1);
438
439 // Maps the buffer so it can be read on the cpu
440 buffer_slice.map_async(MapMode::Read, move |r| match r {
441 // This will execute once the gpu is ready, so after the call to poll()
442 Ok(r) => s.send(r).expect("Failed to send map update"),
443 Err(err) => panic!("Failed to map buffer {err}"),
444 });
445
446 // In order for the mapping to be completed, one of three things must happen.
447 // One of those can be calling `Device::poll`. This isn't necessary on the web as devices
448 // are polled automatically but natively, we need to make sure this happens manually.
449 // `Maintain::Wait` will cause the thread to wait on native but not on WebGpu.
450
451 // This blocks until the gpu is done executing everything
452 render_device.poll(Maintain::wait()).panic_on_timeout();
453
454 // This blocks until the buffer is mapped
455 r.recv().expect("Failed to receive the map_async message");
456
457 // This could fail on app exit, if Main world clears resources (including receiver) while Render world still renders
458 let _ = sender.send(buffer_slice.get_mapped_range().to_vec());
459
460 // We need to make sure all `BufferView`'s are dropped before we do what we're about
461 // to do.
462 // Unmap so that we can copy to the staging buffer in the next iteration.
463 image_copier.buffer.unmap();
464 }
465}
466
467/// CPU-side image for saving
468#[derive(Component, Deref, DerefMut)]
469struct ImageToSave(Handle<Image>);
470
471// Takes from channel image content sent from render world and saves it to disk
472fn update(
473 images_to_save: Query<&ImageToSave>,
474 receiver: Res<MainWorldReceiver>,
475 mut images: ResMut<Assets<Image>>,
476 mut scene_controller: ResMut<SceneController>,
477 mut app_exit_writer: EventWriter<AppExit>,
478 mut file_number: Local<u32>,
479) {
480 if let SceneState::Render(n) = scene_controller.state {
481 if n < 1 {
482 // We don't want to block the main world on this,
483 // so we use try_recv which attempts to receive without blocking
484 let mut image_data = Vec::new();
485 while let Ok(data) = receiver.try_recv() {
486 // image generation could be faster than saving to fs,
487 // that's why use only last of them
488 image_data = data;
489 }
490 if !image_data.is_empty() {
491 for image in images_to_save.iter() {
492 // Fill correct data from channel to image
493 let img_bytes = images.get_mut(image.id()).unwrap();
494
495 // We need to ensure that this works regardless of the image dimensions
496 // If the image became wider when copying from the texture to the buffer,
497 // then the data is reduced to its original size when copying from the buffer to the image.
498 let row_bytes = img_bytes.width() as usize
499 * img_bytes.texture_descriptor.format.pixel_size();
500 let aligned_row_bytes = RenderDevice::align_copy_bytes_per_row(row_bytes);
501 if row_bytes == aligned_row_bytes {
502 img_bytes.data.as_mut().unwrap().clone_from(&image_data);
503 } else {
504 // shrink data to original image size
505 img_bytes.data = Some(
506 image_data
507 .chunks(aligned_row_bytes)
508 .take(img_bytes.height() as usize)
509 .flat_map(|row| &row[..row_bytes.min(row.len())])
510 .cloned()
511 .collect(),
512 );
513 }
514
515 // Create RGBA Image Buffer
516 let img = match img_bytes.clone().try_into_dynamic() {
517 Ok(img) => img.to_rgba8(),
518 Err(e) => panic!("Failed to create image buffer {e:?}"),
519 };
520
521 // Prepare directory for images, test_images in bevy folder is used here for example
522 // You should choose the path depending on your needs
523 let images_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_images");
524 info!("Saving image to: {images_dir:?}");
525 std::fs::create_dir_all(&images_dir).unwrap();
526
527 // Choose filename starting from 000.png
528 let image_path = images_dir.join(format!("{:03}.png", file_number.deref()));
529 *file_number.deref_mut() += 1;
530
531 // Finally saving image to file, this heavy blocking operation is kept here
532 // for example simplicity, but in real app you should move it to a separate task
533 if let Err(e) = img.save(image_path) {
534 panic!("Failed to save image: {e}");
535 };
536 }
537 if scene_controller.single_image {
538 app_exit_writer.write(AppExit::Success);
539 }
540 }
541 } else {
542 // clears channel for skipped frames
543 while receiver.try_recv().is_ok() {}
544 scene_controller.state = SceneState::Render(n - 1);
545 }
546 }
547}
pub fn get_supported_read_only_binding_type( &self, buffers_per_shader_stage: u32, ) -> BufferBindingType
Trait Implementations§
Source§impl Clone for RenderDevice
impl Clone for RenderDevice
Source§fn clone(&self) -> RenderDevice
fn clone(&self) -> RenderDevice
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl From<Device> for RenderDevice
impl From<Device> for RenderDevice
Source§fn from(device: Device) -> RenderDevice
fn from(device: Device) -> RenderDevice
impl Resource for RenderDevice
Auto Trait Implementations§
impl Freeze for RenderDevice
impl !RefUnwindSafe for RenderDevice
impl Send for RenderDevice
impl Sync for RenderDevice
impl Unpin for RenderDevice
impl !UnwindSafe for RenderDevice
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T
ShaderType
for self
. When used in AsBindGroup
derives, it is safe to assume that all images in self
exist.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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
, which can then be
downcast
into Box<dyn ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
, which can then be further
downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self
to use its Binary
implementation when Debug
-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self
to use its Display
implementation when
Debug
-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self
to use its LowerExp
implementation when
Debug
-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self
to use its LowerHex
implementation when
Debug
-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self
to use its Octal
implementation when Debug
-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self
to use its Pointer
implementation when
Debug
-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self
to use its UpperExp
implementation when
Debug
-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self
to use its UpperHex
implementation when
Debug
-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
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 moreSource§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self
, then passes self.as_ref()
into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self
, then passes self.as_mut()
into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self
, then passes self.deref()
into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B>
of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B>
of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R>
view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R>
view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target
of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target
of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap()
only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow()
only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref()
only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref()
only in debug builds, and is erased in release
builds.