pub struct PipelineCache { /* private fields */ }bevy_render only.Expand description
Cache for render and compute pipelines.
The cache stores existing render and compute pipelines allocated on the GPU, as well as
pending creation. Pipelines inserted into the cache are identified by a unique ID, which
can be used to retrieve the actual GPU object once it’s ready. The creation of the GPU
pipeline object is deferred to the RenderSystems::Render step, just before the render
graph starts being processed, as this requires access to the GPU.
Note that the cache does not perform automatic deduplication of identical pipelines. It is up to the user not to insert the same pipeline twice to avoid wasting GPU resources.
Implementations§
Source§impl PipelineCache
impl PipelineCache
Sourcepub fn pipelines(&self) -> impl Iterator<Item = &CachedPipeline>
pub fn pipelines(&self) -> impl Iterator<Item = &CachedPipeline>
Returns an iterator over the pipelines in the pipeline cache.
Sourcepub fn waiting_pipelines(&self) -> impl Iterator<Item = usize>
pub fn waiting_pipelines(&self) -> impl Iterator<Item = usize>
Returns a iterator of the IDs of all currently waiting pipelines.
Sourcepub fn new(
device: RenderDevice,
synchronous_pipeline_compilation: bool,
) -> PipelineCache
pub fn new( device: RenderDevice, synchronous_pipeline_compilation: bool, ) -> PipelineCache
Create a new pipeline cache associated with the given render device.
Sourcepub fn get_render_pipeline_state(
&self,
id: CachedRenderPipelineId,
) -> &CachedPipelineState
pub fn get_render_pipeline_state( &self, id: CachedRenderPipelineId, ) -> &CachedPipelineState
Get the state of a cached render pipeline.
Sourcepub fn get_compute_pipeline_state(
&self,
id: CachedComputePipelineId,
) -> &CachedPipelineState
pub fn get_compute_pipeline_state( &self, id: CachedComputePipelineId, ) -> &CachedPipelineState
Get the state of a cached compute pipeline.
Examples found in repository?
221fn update(
222 pipeline: Res<GameOfLifePipeline>,
223 pipeline_cache: Res<PipelineCache>,
224 mut state: ResMut<GameOfLifeState>,
225) {
226 // if the corresponding pipeline has loaded, transition to the next stage
227 match *state {
228 GameOfLifeState::Loading => {
229 match pipeline_cache.get_compute_pipeline_state(pipeline.init_pipeline) {
230 CachedPipelineState::Ok(_) => {
231 *state = GameOfLifeState::Init;
232 }
233 // If the shader hasn't loaded yet, just wait.
234 CachedPipelineState::Err(ShaderCacheError::ShaderNotLoaded(_)) => {}
235 CachedPipelineState::Err(err) => {
236 panic!("Initializing assets/{SHADER_ASSET_PATH}:\n{err}")
237 }
238 _ => {}
239 }
240 }
241 GameOfLifeState::Init => {
242 if let CachedPipelineState::Ok(_) =
243 pipeline_cache.get_compute_pipeline_state(pipeline.update_pipeline)
244 {
245 *state = GameOfLifeState::Update(1);
246 }
247 }
248 GameOfLifeState::Update(0) => {
249 *state = GameOfLifeState::Update(1);
250 }
251 GameOfLifeState::Update(1) => {
252 *state = GameOfLifeState::Update(0);
253 }
254 GameOfLifeState::Update(_) => unreachable!(),
255 }
256}Sourcepub fn get_render_pipeline_descriptor(
&self,
id: CachedRenderPipelineId,
) -> &RenderPipelineDescriptor
pub fn get_render_pipeline_descriptor( &self, id: CachedRenderPipelineId, ) -> &RenderPipelineDescriptor
Get the render pipeline descriptor a cached render pipeline was inserted from.
See PipelineCache::queue_render_pipeline().
Note: Be careful calling this method. It will panic if called with a pipeline that
has been queued but has not yet been processed by PipelineCache::process_queue().
Sourcepub fn get_compute_pipeline_descriptor(
&self,
id: CachedComputePipelineId,
) -> &ComputePipelineDescriptor
pub fn get_compute_pipeline_descriptor( &self, id: CachedComputePipelineId, ) -> &ComputePipelineDescriptor
Get the compute pipeline descriptor a cached render pipeline was inserted from.
See PipelineCache::queue_compute_pipeline().
Note: Be careful calling this method. It will panic if called with a pipeline that
has been queued but has not yet been processed by PipelineCache::process_queue().
Sourcepub fn get_mesh_pipeline_descriptor(
&self,
id: CachedRenderPipelineId,
) -> &MeshPipelineDescriptor
pub fn get_mesh_pipeline_descriptor( &self, id: CachedRenderPipelineId, ) -> &MeshPipelineDescriptor
Get the mesh pipeline descriptor a cached mesh pipeline was inserted from.
See PipelineCache::queue_mesh_pipeline().
Note: Be careful calling this method. It will panic if called with a pipeline that
has been queued but has not yet been processed by PipelineCache::process_queue().
Sourcepub fn get_render_pipeline(
&self,
id: CachedRenderPipelineId,
) -> Option<&RenderPipeline>
pub fn get_render_pipeline( &self, id: CachedRenderPipelineId, ) -> Option<&RenderPipeline>
Try to retrieve a render pipeline GPU object from a cached ID.
§Returns
This method returns a successfully created render pipeline if any, or None if the pipeline
was not created yet or if there was an error during creation. You can check the actual creation
state with PipelineCache::get_render_pipeline_state().
Examples found in repository?
202fn draw_mesh_shader_cubes(
203 mut views: Query<(
204 &ExtractedCamera,
205 &ExtractedView,
206 &ViewTarget,
207 &ViewDepthStencilTexture,
208 &ViewUniformOffset,
209 Option<&MainPassResolutionOverride>,
210 )>,
211 mut render_context: RenderContext,
212 data: Res<MyMeshShaderDrawNode>,
213 view_uniforms: Res<ViewUniforms>,
214 globals: Res<GlobalsBuffer>,
215 pipeline_cache: Res<PipelineCache>,
216) {
217 let Some(mesh_pipeline) = pipeline_cache.get_render_pipeline(data.mesh_pipeline) else {
218 return;
219 };
220
221 for (camera, _, target, depth, view_uniform_offset, resolution_override) in &mut views {
222 let Some(view_binding) = view_uniforms.uniforms.binding() else {
223 return;
224 };
225 let Some(globals_binding) = globals.buffer.binding() else {
226 return;
227 };
228 let bind_group = render_context.render_device().create_bind_group(
229 "custom_task_mesh_bind_group",
230 &pipeline_cache.get_bind_group_layout(&data.layout),
231 &BindGroupEntries::sequential((globals_binding, view_binding)),
232 );
233
234 {
235 let mut pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
236 label: Some("custom_mesh_shader_pass"),
237 // Write directly to the view target
238 color_attachments: &[Some(target.get_color_attachment())],
239 depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
240 timestamp_writes: None,
241 occlusion_query_set: None,
242 multiview_mask: None,
243 });
244
245 pass.set_render_pipeline(mesh_pipeline);
246 pass.set_bind_group(0, &bind_group, &[view_uniform_offset.offset]);
247 if let Some(viewport) =
248 Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
249 {
250 pass.set_camera_viewport(&viewport);
251 }
252
253 // Since this MeshPipeline has a task shader, this call
254 // dispatches the task shader workgroup
255 pass.draw_mesh_tasks(1, 1, 1);
256 }
257 }
258}More examples
229fn raymarch_gbuffer_pass(
230 view: ViewQuery<(
231 &ViewUniformOffset,
232 &ViewDepthStencilTexture,
233 &ViewPrepassTextures,
234 )>,
235 pipeline: Option<Res<RaymarchGBufferPipeline>>,
236 pipeline_cache: Res<PipelineCache>,
237 view_uniforms: Res<ViewUniforms>,
238 globals: Res<GlobalsBuffer>,
239 mut ctx: RenderContext,
240) {
241 let Some(pipeline) = pipeline else {
242 return;
243 };
244 let Some(render_pipeline) = pipeline_cache.get_render_pipeline(pipeline.pipeline_id) else {
245 return;
246 };
247
248 let (view_uniform_offset, view_depth, view_prepass_textures) = view.into_inner();
249
250 let (Some(deferred), Some(lighting_pass_id)) = (
251 &view_prepass_textures.deferred,
252 &view_prepass_textures.deferred_lighting_pass_id,
253 ) else {
254 return;
255 };
256
257 let Some(bind_group) = raymarch_bind_group(
258 &ctx,
259 &pipeline_cache,
260 &pipeline.layout,
261 &view_uniforms,
262 &globals,
263 ) else {
264 return;
265 };
266
267 {
268 // We load rather than clear because we only want to overwrite the pixels which the deferred mesh
269 // prepass didn't write
270 let mut pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
271 label: Some("raymarch_gbuffer_pass"),
272 color_attachments: &[
273 Some(deferred.get_attachment()),
274 Some(lighting_pass_id.get_attachment()),
275 ],
276 depth_stencil_attachment: Some(view_depth.get_attachment(StoreOp::Store)),
277 timestamp_writes: None,
278 occlusion_query_set: None,
279 multiview_mask: None,
280 });
281
282 pass.set_render_pipeline(render_pipeline);
283 pass.set_bind_group(0, &bind_group, &[view_uniform_offset.offset]);
284 pass.draw(0..3, 0..1);
285 }
286
287 // The deferred lighting pass reconstructs world position from the prepass depth
288 // texture, not the depth attachment we just wrote, so we have to do a copy.
289 if let Some(prepass_depth) = &view_prepass_textures.depth {
290 ctx.command_encoder().copy_texture_to_texture(
291 view_depth.texture().as_image_copy(),
292 prepass_depth.texture.texture.as_image_copy(),
293 view_prepass_textures.size,
294 );
295 }
296}
297
298fn raymarch_directional_shadow_pass(
299 view: ViewQuery<&ViewLightEntities>,
300 shadow_views: Query<(&ShadowView, &ViewUniformOffset)>,
301 pipeline: Option<Res<RaymarchShadowPipeline>>,
302 pipeline_cache: Res<PipelineCache>,
303 view_uniforms: Res<ViewUniforms>,
304 globals: Res<GlobalsBuffer>,
305 mut ctx: RenderContext,
306) {
307 let Some(pipeline) = pipeline else {
308 return;
309 };
310 let Some(render_pipeline) = pipeline_cache.get_render_pipeline(pipeline.pipeline_id) else {
311 return;
312 };
313
314 let view_lights = view.into_inner();
315 for light_entity in view_lights.lights.iter().copied() {
316 let Ok((shadow_view, view_uniform_offset)) = shadow_views.get(light_entity) else {
317 continue;
318 };
319 draw_raymarch_shadow(
320 &mut ctx,
321 &pipeline_cache,
322 &pipeline.layout,
323 &view_uniforms,
324 &globals,
325 render_pipeline,
326 shadow_view,
327 view_uniform_offset,
328 );
329 }
330}
331
332fn raymarch_shared_shadow_pass(
333 view: ViewQuery<(&ShadowView, &ViewUniformOffset)>,
334 pipeline: Option<Res<RaymarchShadowPipeline>>,
335 pipeline_cache: Res<PipelineCache>,
336 view_uniforms: Res<ViewUniforms>,
337 globals: Res<GlobalsBuffer>,
338 mut ctx: RenderContext,
339) {
340 let Some(pipeline) = pipeline else {
341 return;
342 };
343 let Some(render_pipeline) = pipeline_cache.get_render_pipeline(pipeline.pipeline_id) else {
344 return;
345 };
346
347 let (shadow_view, view_uniform_offset) = view.into_inner();
348 draw_raymarch_shadow(
349 &mut ctx,
350 &pipeline_cache,
351 &pipeline.layout,
352 &view_uniforms,
353 &globals,
354 render_pipeline,
355 shadow_view,
356 view_uniform_offset,
357 );
358}140fn post_process_system(
141 view: ViewQuery<(
142 &ViewTarget,
143 &DynamicUniformIndex<PostProcessSettings>,
144 &PostProcessBindGroups,
145 )>,
146 post_process_pipeline: Option<Res<PostProcessPipeline>>,
147 pipeline_cache: Res<PipelineCache>,
148 mut ctx: RenderContext,
149) {
150 let Some(post_process_pipeline) = post_process_pipeline else {
151 return;
152 };
153
154 let (view_target, settings_index, bind_groups) = view.into_inner();
155
156 let Some(pipeline) = pipeline_cache.get_render_pipeline(post_process_pipeline.pipeline_id)
157 else {
158 return;
159 };
160
161 // This will start a new "post process write", obtaining two texture
162 // views from the view target - a `source` and a `destination`.
163 // `source` is the "current" main texture and you _must_ write into
164 // `destination` because calling `post_process_write()` on the
165 // [`ViewTarget`] will internally flip the [`ViewTarget`]'s main
166 // texture to the `destination` texture. Failing to do so will cause
167 // the current main texture information to be lost.
168 let post_process = view_target.post_process_write();
169
170 // We prepared the bind group ahead of time but now we need to make sure
171 // we pick the bind group associated with the source texture
172 let (_, bind_group) = if bind_groups.a.0 == post_process.source.id() {
173 &bind_groups.a
174 } else {
175 &bind_groups.b
176 };
177
178 let mut render_pass = ctx
179 .command_encoder()
180 .begin_render_pass(&RenderPassDescriptor {
181 label: Some("post_process_pass"),
182 color_attachments: &[Some(RenderPassColorAttachment {
183 // We need to specify the post process destination view here
184 // to make sure we write to the appropriate texture.
185 view: post_process.destination,
186 depth_slice: None,
187 resolve_target: None,
188 ops: Operations::default(),
189 })],
190 depth_stencil_attachment: None,
191 timestamp_writes: None,
192 occlusion_query_set: None,
193 multiview_mask: None,
194 });
195
196 render_pass.set_pipeline(pipeline);
197 // By passing in the index of the post process settings on this view, we ensure
198 // that in the event that multiple settings were sent to the GPU (as would be the
199 // case with multiple cameras), we use the correct one.
200 render_pass.set_bind_group(0, bind_group, &[settings_index.index()]);
201 render_pass.draw(0..3, 0..1);
202}Sourcepub fn block_on_render_pipeline(&mut self, id: CachedRenderPipelineId)
pub fn block_on_render_pipeline(&mut self, id: CachedRenderPipelineId)
Wait for a render pipeline to finish compiling.
Sourcepub fn get_compute_pipeline(
&self,
id: CachedComputePipelineId,
) -> Option<&ComputePipeline>
pub fn get_compute_pipeline( &self, id: CachedComputePipelineId, ) -> Option<&ComputePipeline>
Try to retrieve a compute pipeline GPU object from a cached ID.
§Returns
This method returns a successfully created compute pipeline if any, or None if the pipeline
was not created yet or if there was an error during creation. You can check the actual creation
state with PipelineCache::get_compute_pipeline_state().
Examples found in repository?
214fn compute(
215 mut render_context: RenderContext,
216 pipeline_cache: Res<PipelineCache>,
217 pipeline: Res<ComputePipeline>,
218 bind_group: Res<GpuBufferBindGroup>,
219) {
220 if let Some(init_pipeline) = pipeline_cache.get_compute_pipeline(pipeline.pipeline) {
221 let mut pass =
222 render_context
223 .command_encoder()
224 .begin_compute_pass(&ComputePassDescriptor {
225 label: Some("GPU readback compute pass"),
226 ..default()
227 });
228
229 pass.set_bind_group(0, &bind_group.0, &[]);
230 pass.set_pipeline(init_pipeline);
231 pass.dispatch_workgroups(BUFFER_LEN as u32, 1, 1);
232 }
233}More examples
174fn prepare_chunks(
175 meshes_to_generate: Query<&GenerateMesh>,
176 mut chunks: ResMut<ChunksToProcess>,
177 pipeline_cache: Res<PipelineCache>,
178 pipeline: Res<ComputePipeline>,
179 mut processed: Local<HashSet<AssetId<Mesh>>>,
180) {
181 // If the pipeline isn't ready, then meshes
182 // won't be processed. So we want to wait until
183 // the pipeline is ready before considering any mesh processed.
184 if pipeline_cache
185 .get_compute_pipeline(pipeline.pipeline)
186 .is_some()
187 {
188 // get the AssetId for each Handle<Mesh>
189 // which we'll use later to get the relevant buffers
190 // from the mesh_allocator
191 let chunk_data: Vec<AssetId<Mesh>> = meshes_to_generate
192 .iter()
193 .filter_map(|gmesh| {
194 let id = gmesh.0.id();
195 processed.contains(&id).not().then_some(id)
196 })
197 .collect();
198
199 // Cache any meshes we're going to process this frame
200 for id in &chunk_data {
201 processed.insert(*id);
202 }
203
204 chunks.0 = chunk_data;
205 }
206}
207
208#[derive(Resource)]
209struct ComputePipeline {
210 layout: BindGroupLayoutDescriptor,
211 pipeline: CachedComputePipelineId,
212}
213
214// init only happens once
215fn init_compute_pipeline(
216 mut commands: Commands,
217 asset_server: Res<AssetServer>,
218 pipeline_cache: Res<PipelineCache>,
219) {
220 let layout = BindGroupLayoutDescriptor::new(
221 "",
222 &BindGroupLayoutEntries::sequential(
223 ShaderStages::COMPUTE,
224 (
225 // offsets
226 uniform_buffer::<DataRanges>(false),
227 // vertices
228 storage_buffer::<Vec<f32>>(false),
229 // indices
230 storage_buffer::<Vec<u32>>(false),
231 ),
232 ),
233 );
234 let shader = asset_server.load(SHADER_ASSET_PATH);
235 let pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
236 label: Some("Mesh generation compute shader".into()),
237 layout: vec![layout.clone()],
238 shader: shader.clone(),
239 ..default()
240 });
241 commands.insert_resource(ComputePipeline { layout, pipeline });
242}
243
244// A uniform that holds the vertex and index offsets
245// for the vertex/index mesh_allocator buffer slabs
246#[derive(ShaderType)]
247struct DataRanges {
248 vertex_start: u32,
249 vertex_end: u32,
250 index_start: u32,
251 index_end: u32,
252}
253
254fn compute_mesh(
255 mut render_context: RenderContext,
256 chunks: Res<ChunksToProcess>,
257 mesh_allocator: Res<MeshAllocator>,
258 pipeline_cache: Res<PipelineCache>,
259 pipeline: Res<ComputePipeline>,
260 render_queue: Res<RenderQueue>,
261) {
262 let Some(init_pipeline) = pipeline_cache.get_compute_pipeline(pipeline.pipeline) else {
263 return;
264 };
265
266 for mesh_id in &chunks.0 {
267 info!(?mesh_id, "processing mesh");
268
269 // the mesh_allocator holds slabs of meshes, so the buffers we get here
270 // can contain more data than just the mesh we're asking for.
271 // That's why there is a range field.
272 // You should *not* touch data in these buffers that is outside of the range.
273 let vertex_buffer_slice = mesh_allocator.mesh_vertex_slice(mesh_id).unwrap();
274 let index_buffer_slice = mesh_allocator.mesh_index_slice(mesh_id).unwrap();
275
276 let first = DataRanges {
277 // there are 8 vertex data values (pos, normal, uv) per vertex
278 // and the vertex_buffer_slice.range.start is in "vertex elements"
279 // which includes all of that data, so each index is worth 8 indices
280 // to our shader code.
281 vertex_start: vertex_buffer_slice.range.start * 8,
282 vertex_end: vertex_buffer_slice.range.end * 8,
283 // but each vertex index is a single value, so the index of the
284 // vertex indices is exactly what the value is
285 index_start: index_buffer_slice.range.start,
286 index_end: index_buffer_slice.range.end,
287 };
288
289 let mut uniforms = UniformBuffer::from(first);
290 uniforms.write_buffer(render_context.render_device(), &render_queue);
291
292 // pass in the full mesh_allocator slabs as well as the first index
293 // offsets for the vertex and index buffers
294 let bind_group = render_context.render_device().create_bind_group(
295 None,
296 &pipeline_cache.get_bind_group_layout(&pipeline.layout),
297 &BindGroupEntries::sequential((
298 &uniforms,
299 vertex_buffer_slice.buffer.as_entire_buffer_binding(),
300 index_buffer_slice.buffer.as_entire_buffer_binding(),
301 )),
302 );
303
304 let mut pass =
305 render_context
306 .command_encoder()
307 .begin_compute_pass(&ComputePassDescriptor {
308 label: Some("Mesh generation compute pass"),
309 ..default()
310 });
311 pass.push_debug_group("compute_mesh");
312
313 pass.set_bind_group(0, &bind_group, &[]);
314 pass.set_pipeline(init_pipeline);
315 // we only dispatch 1,1,1 workgroup here, but a real compute shader
316 // would take advantage of more and larger size workgroups
317 pass.dispatch_workgroups(1, 1, 1);
318
319 pass.pop_debug_group();
320 }
321}258fn game_of_life(
259 mut render_context: RenderContext,
260 bind_groups: Res<GameOfLifeImageBindGroups>,
261 pipeline_cache: Res<PipelineCache>,
262 pipeline: Res<GameOfLifePipeline>,
263 state: Res<GameOfLifeState>,
264) {
265 let mut pass = render_context
266 .command_encoder()
267 .begin_compute_pass(&ComputePassDescriptor::default());
268
269 // select the pipeline based on the current state
270 match *state {
271 GameOfLifeState::Loading => {}
272 GameOfLifeState::Init => {
273 let init_pipeline = pipeline_cache
274 .get_compute_pipeline(pipeline.init_pipeline)
275 .unwrap();
276 pass.set_bind_group(0, &bind_groups.0[0], &[]);
277 pass.set_pipeline(init_pipeline);
278 pass.dispatch_workgroups(SIZE.x / WORKGROUP_SIZE, SIZE.y / WORKGROUP_SIZE, 1);
279 }
280 GameOfLifeState::Update(index) => {
281 let update_pipeline = pipeline_cache
282 .get_compute_pipeline(pipeline.update_pipeline)
283 .unwrap();
284 pass.set_bind_group(0, &bind_groups.0[index], &[]);
285 pass.set_pipeline(update_pipeline);
286 pass.dispatch_workgroups(SIZE.x / WORKGROUP_SIZE, SIZE.y / WORKGROUP_SIZE, 1);
287 }
288 }
289}Sourcepub fn queue_render_pipeline(
&self,
descriptor: RenderPipelineDescriptor,
) -> CachedRenderPipelineId
pub fn queue_render_pipeline( &self, descriptor: RenderPipelineDescriptor, ) -> CachedRenderPipelineId
Insert a render pipeline into the cache, and queue its creation.
The pipeline is always inserted and queued for creation. There is no attempt to deduplicate it with an already cached pipeline.
§Returns
This method returns the unique render shader ID of the cached pipeline, which can be used to query
the caching state with get_render_pipeline_state() and to retrieve the created GPU pipeline once
it’s ready with get_render_pipeline().
Examples found in repository?
212fn init_post_process_pipeline(
213 mut commands: Commands,
214 render_device: Res<RenderDevice>,
215 asset_server: Res<AssetServer>,
216 fullscreen_shader: Res<FullscreenShader>,
217 pipeline_cache: Res<PipelineCache>,
218) {
219 // We need to define the bind group layout used for our pipeline
220 let layout = BindGroupLayoutDescriptor::new(
221 "post_process_bind_group_layout",
222 &BindGroupLayoutEntries::sequential(
223 // The layout entries will only be visible in the fragment stage
224 ShaderStages::FRAGMENT,
225 (
226 // The screen texture
227 texture_2d(TextureSampleType::Float { filterable: true }),
228 // The sampler that will be used to sample the screen texture
229 sampler(SamplerBindingType::Filtering),
230 // The settings uniform that will control the effect
231 uniform_buffer::<PostProcessSettings>(true),
232 ),
233 ),
234 );
235 // We can create the sampler here since it won't change at runtime and doesn't depend on the view
236 let sampler = render_device.create_sampler(&SamplerDescriptor::default());
237
238 // Get the shader handle
239 let shader = asset_server.load(SHADER_ASSET_PATH);
240 // This will setup a fullscreen triangle for the vertex state.
241 let vertex_state = fullscreen_shader.to_vertex_state();
242 let pipeline_id = pipeline_cache
243 // This will add the pipeline to the cache and queue its creation
244 .queue_render_pipeline(RenderPipelineDescriptor {
245 label: Some("post_process_pipeline".into()),
246 layout: vec![layout.clone()],
247 vertex: vertex_state,
248 fragment: Some(FragmentState {
249 shader,
250 // Make sure this matches the entry point of your shader.
251 // It can be anything as long as it matches here and in the shader.
252 targets: vec![Some(ColorTargetState {
253 format: TextureFormat::Rgba8UnormSrgb,
254 blend: None,
255 write_mask: ColorWrites::ALL,
256 })],
257 ..default()
258 }),
259 ..default()
260 });
261 commands.insert_resource(PostProcessPipeline {
262 layout,
263 sampler,
264 pipeline_id,
265 });
266}More examples
135fn init_raymarch_pipelines(
136 mut commands: Commands,
137 asset_server: Res<AssetServer>,
138 fullscreen_shader: Res<FullscreenShader>,
139 pipeline_cache: Res<PipelineCache>,
140) {
141 let layout = BindGroupLayoutDescriptor::new(
142 "raymarch_bind_group_layout",
143 &BindGroupLayoutEntries::sequential(
144 ShaderStages::FRAGMENT,
145 (
146 uniform_buffer::<ViewUniform>(true),
147 uniform_buffer::<GlobalsUniform>(false),
148 ),
149 ),
150 );
151
152 let shader = asset_server.load::<Shader>(SHADER_ASSET_PATH);
153 let vertex_state = fullscreen_shader.to_vertex_state();
154
155 // Writing depth lets the SDF sort against meshes
156 let depth_stencil = DepthStencilState {
157 format: CORE_3D_DEPTH_FORMAT,
158 depth_write_enabled: Some(true),
159 depth_compare: Some(CompareFunction::GreaterEqual),
160 stencil: StencilState::default(),
161 bias: DepthBiasState::default(),
162 };
163
164 let gbuffer_pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
165 label: Some("raymarch_gbuffer_pipeline".into()),
166 layout: vec![layout.clone()],
167 vertex: vertex_state.clone(),
168 fragment: Some(FragmentState {
169 shader: shader.clone(),
170 entry_point: Some("fragment".into()),
171 targets: vec![
172 Some(ColorTargetState {
173 format: DEFERRED_PREPASS_FORMAT,
174 blend: None,
175 write_mask: ColorWrites::ALL,
176 }),
177 Some(ColorTargetState {
178 format: DEFERRED_LIGHTING_PASS_ID_FORMAT,
179 blend: None,
180 write_mask: ColorWrites::ALL,
181 }),
182 ],
183 ..default()
184 }),
185 depth_stencil: Some(depth_stencil.clone()),
186 ..default()
187 });
188
189 let shadow_pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
190 label: Some("raymarch_shadow_pipeline".into()),
191 layout: vec![layout.clone()],
192 vertex: vertex_state,
193 fragment: Some(FragmentState {
194 shader,
195 entry_point: Some("fragment_shadow".into()),
196 targets: vec![],
197 ..default()
198 }),
199 depth_stencil: Some(depth_stencil),
200 ..default()
201 });
202
203 commands.insert_resource(RaymarchGBufferPipeline {
204 layout: layout.clone(),
205 pipeline_id: gbuffer_pipeline_id,
206 });
207 commands.insert_resource(RaymarchShadowPipeline {
208 layout,
209 pipeline_id: shadow_pipeline_id,
210 });
211}Sourcepub fn queue_compute_pipeline(
&self,
descriptor: ComputePipelineDescriptor,
) -> CachedComputePipelineId
pub fn queue_compute_pipeline( &self, descriptor: ComputePipelineDescriptor, ) -> CachedComputePipelineId
Insert a compute pipeline into the cache, and queue its creation.
The pipeline is always inserted and queued for creation. There is no attempt to deduplicate it with an already cached pipeline.
§Returns
This method returns the unique compute shader ID of the cached pipeline, which can be used to query
the caching state with get_compute_pipeline_state() and to retrieve the created GPU pipeline once
it’s ready with get_compute_pipeline().
Examples found in repository?
189fn init_compute_pipeline(
190 mut commands: Commands,
191 asset_server: Res<AssetServer>,
192 pipeline_cache: Res<PipelineCache>,
193) {
194 let layout = BindGroupLayoutDescriptor::new(
195 "",
196 &BindGroupLayoutEntries::sequential(
197 ShaderStages::COMPUTE,
198 (
199 storage_buffer::<Vec<u32>>(false),
200 texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly),
201 ),
202 ),
203 );
204 let shader = asset_server.load(SHADER_ASSET_PATH);
205 let pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
206 label: Some("GPU readback compute shader".into()),
207 layout: vec![layout.clone()],
208 shader: shader.clone(),
209 ..default()
210 });
211 commands.insert_resource(ComputePipeline { layout, pipeline });
212}More examples
215fn init_compute_pipeline(
216 mut commands: Commands,
217 asset_server: Res<AssetServer>,
218 pipeline_cache: Res<PipelineCache>,
219) {
220 let layout = BindGroupLayoutDescriptor::new(
221 "",
222 &BindGroupLayoutEntries::sequential(
223 ShaderStages::COMPUTE,
224 (
225 // offsets
226 uniform_buffer::<DataRanges>(false),
227 // vertices
228 storage_buffer::<Vec<f32>>(false),
229 // indices
230 storage_buffer::<Vec<u32>>(false),
231 ),
232 ),
233 );
234 let shader = asset_server.load(SHADER_ASSET_PATH);
235 let pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
236 label: Some("Mesh generation compute shader".into()),
237 layout: vec![layout.clone()],
238 shader: shader.clone(),
239 ..default()
240 });
241 commands.insert_resource(ComputePipeline { layout, pipeline });
242}176fn init_game_of_life_pipeline(
177 mut commands: Commands,
178 asset_server: Res<AssetServer>,
179 pipeline_cache: Res<PipelineCache>,
180) {
181 let texture_bind_group_layout = BindGroupLayoutDescriptor::new(
182 "GameOfLifeImages",
183 &BindGroupLayoutEntries::sequential(
184 ShaderStages::COMPUTE,
185 (
186 texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::ReadOnly),
187 texture_storage_2d(TextureFormat::Rgba32Float, StorageTextureAccess::WriteOnly),
188 uniform_buffer::<GameOfLifeUniforms>(false),
189 ),
190 ),
191 );
192 let shader = asset_server.load(SHADER_ASSET_PATH);
193 let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
194 layout: vec![texture_bind_group_layout.clone()],
195 shader: shader.clone(),
196 entry_point: Some(Cow::from("init")),
197 ..default()
198 });
199 let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
200 layout: vec![texture_bind_group_layout.clone()],
201 shader,
202 entry_point: Some(Cow::from("update")),
203 ..default()
204 });
205
206 commands.insert_resource(GameOfLifePipeline {
207 texture_bind_group_layout,
208 init_pipeline,
209 update_pipeline,
210 });
211}Sourcepub fn queue_mesh_pipeline(
&self,
descriptor: MeshPipelineDescriptor,
) -> CachedRenderPipelineId
pub fn queue_mesh_pipeline( &self, descriptor: MeshPipelineDescriptor, ) -> CachedRenderPipelineId
Insert a mesh pipeline into the cache, and queue its creation.
The pipeline is always inserted and queued for creation. There is no attempt to deduplicate it with an already cached pipeline.
§Returns
This method returns the unique mesh shader ID of the cached pipeline, which can be used to query
the caching state with get_render_pipeline_state() and to retrieve the created GPU pipeline once
it’s ready with get_render_pipeline().
Examples found in repository?
129fn init_mesh_pipelines(
130 mut commands: Commands,
131 asset_server: Res<AssetServer>,
132 pipeline_cache: Res<PipelineCache>,
133) {
134 let task_shader = asset_server.load::<Shader>("shaders/mesh_shader_intro/task.wesl");
135 let mesh_shader = asset_server.load::<Shader>("shaders/mesh_shader_intro/mesh.wesl");
136 let fragment_shader = asset_server.load::<Shader>("shaders/mesh_shader_intro/fragment.wesl");
137
138 let layout = BindGroupLayoutDescriptor::new(
139 "custom_mesh_shader_bind_group_layout",
140 &BindGroupLayoutEntries::sequential(
141 ShaderStages::MESH | ShaderStages::TASK | ShaderStages::FRAGMENT,
142 (
143 uniform_buffer::<GlobalsUniform>(false),
144 uniform_buffer::<ViewUniform>(true),
145 ),
146 ),
147 );
148
149 let depth_stencil = DepthStencilState {
150 format: CORE_3D_DEPTH_FORMAT,
151 depth_write_enabled: Some(true),
152 depth_compare: Some(CompareFunction::GreaterEqual),
153 stencil: StencilState::default(),
154 bias: DepthBiasState::default(),
155 };
156
157 let cached_pipeline_id = pipeline_cache.queue_mesh_pipeline(MeshPipelineDescriptor {
158 label: Some("custom_mesh_shader_pipeline".into()),
159 layout: vec![layout.clone()],
160 immediate_size: 0,
161 task: Some(TaskState {
162 shader: task_shader,
163 entry_point: Some("task".into()),
164 ..default()
165 }),
166 mesh: MeshState {
167 shader: mesh_shader,
168 entry_point: Some("mesh".into()),
169 ..default()
170 },
171 primitive: Default::default(),
172 depth_stencil: Some(depth_stencil),
173 multisample: Default::default(),
174 fragment: Some(FragmentState {
175 shader: fragment_shader,
176 entry_point: Some("fragment".into()),
177 targets: vec![Some(ColorTargetState {
178 format: TextureFormat::Rgba8UnormSrgb,
179 blend: None,
180 write_mask: ColorWrites::ALL,
181 })],
182 ..default()
183 }),
184 zero_initialize_workgroup_memory: false,
185 });
186
187 commands.insert_resource(MyMeshShaderDrawNode {
188 mesh_pipeline: cached_pipeline_id,
189 layout,
190 });
191}Sourcepub fn get_bind_group_layout(
&self,
bind_group_layout_descriptor: &BindGroupLayoutDescriptor,
) -> BindGroupLayout
pub fn get_bind_group_layout( &self, bind_group_layout_descriptor: &BindGroupLayoutDescriptor, ) -> BindGroupLayout
Examples found in repository?
213fn raymarch_bind_group(
214 ctx: &RenderContext,
215 pipeline_cache: &PipelineCache,
216 layout: &BindGroupLayoutDescriptor,
217 view_uniforms: &ViewUniforms,
218 globals: &GlobalsBuffer,
219) -> Option<BindGroup> {
220 let view_binding = view_uniforms.uniforms.binding()?;
221 let globals_binding = globals.buffer.binding()?;
222 Some(ctx.render_device().create_bind_group(
223 "raymarch_bind_group",
224 &pipeline_cache.get_bind_group_layout(layout),
225 &BindGroupEntries::sequential((view_binding, globals_binding)),
226 ))
227}More examples
160fn prepare_bind_group(
161 mut commands: Commands,
162 pipeline: Res<ComputePipeline>,
163 render_device: Res<RenderDevice>,
164 pipeline_cache: Res<PipelineCache>,
165 buffer: Res<ReadbackBuffer>,
166 image: Res<ReadbackImage>,
167 buffers: Res<RenderAssets<GpuShaderBuffer>>,
168 images: Res<RenderAssets<GpuImage>>,
169) {
170 let buffer = buffers.get(&buffer.0).unwrap();
171 let image = images.get(&image.0).unwrap();
172 let bind_group = render_device.create_bind_group(
173 None,
174 &pipeline_cache.get_bind_group_layout(&pipeline.layout),
175 &BindGroupEntries::sequential((
176 buffer.buffer.as_entire_buffer_binding(),
177 image.texture_view.into_binding(),
178 )),
179 );
180 commands.insert_resource(GpuBufferBindGroup(bind_group));
181}130fn prepare_bind_group(
131 mut commands: Commands,
132 pipeline: Res<GameOfLifePipeline>,
133 gpu_images: Res<RenderAssets<GpuImage>>,
134 game_of_life_images: Res<GameOfLifeImages>,
135 game_of_life_uniforms: Res<GameOfLifeUniforms>,
136 render_device: Res<RenderDevice>,
137 pipeline_cache: Res<PipelineCache>,
138 queue: Res<RenderQueue>,
139) {
140 let view_a = gpu_images.get(&game_of_life_images.texture_a).unwrap();
141 let view_b = gpu_images.get(&game_of_life_images.texture_b).unwrap();
142
143 // Uniform buffer is used here to demonstrate how to set up a uniform in a compute shader
144 // Alternatives such as storage buffers or push constants may be more suitable for your use case
145 let mut uniform_buffer = UniformBuffer::from(game_of_life_uniforms.into_inner());
146 uniform_buffer.write_buffer(&render_device, &queue);
147
148 let bind_group_0 = render_device.create_bind_group(
149 None,
150 &pipeline_cache.get_bind_group_layout(&pipeline.texture_bind_group_layout),
151 &BindGroupEntries::sequential((
152 &view_a.texture_view,
153 &view_b.texture_view,
154 &uniform_buffer,
155 )),
156 );
157 let bind_group_1 = render_device.create_bind_group(
158 None,
159 &pipeline_cache.get_bind_group_layout(&pipeline.texture_bind_group_layout),
160 &BindGroupEntries::sequential((
161 &view_b.texture_view,
162 &view_a.texture_view,
163 &uniform_buffer,
164 )),
165 );
166 commands.insert_resource(GameOfLifeImageBindGroups([bind_group_0, bind_group_1]));
167}105 fn as_bind_group(
106 &self,
107 layout: &BindGroupLayoutDescriptor,
108 render_device: &RenderDevice,
109 pipeline_cache: &PipelineCache,
110 _: &FallbackBuffer,
111 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
112 (image_assets, fallback_image): &mut SystemParamItem<'_, '_, Self::Param>,
113 ) -> Result<PreparedBindGroup, AsBindGroupError> {
114 let _ = shader_buffer_assets;
115 // retrieve the render resources from handles
116 let mut images = vec![];
117 for handle in self.textures.iter().take(MAX_TEXTURE_COUNT) {
118 match image_assets.get(handle) {
119 Some(image) => images.push(image),
120 None => return Err(AsBindGroupError::RetryNextUpdate),
121 }
122 }
123
124 let fallback_image = &fallback_image.d2;
125
126 let textures = vec![&fallback_image.texture_view; MAX_TEXTURE_COUNT];
127
128 // convert bevy's resource types to WGPU's references
129 let mut textures: Vec<_> = textures.into_iter().map(|texture| &**texture).collect();
130
131 // fill in up to the first `MAX_TEXTURE_COUNT` textures and samplers to the arrays
132 for (id, image) in images.into_iter().enumerate() {
133 textures[id] = &*image.texture_view;
134 }
135
136 let bind_group = render_device.create_bind_group(
137 Self::label(),
138 &pipeline_cache.get_bind_group_layout(layout),
139 &BindGroupEntries::sequential((&textures[..], &fallback_image.sampler)),
140 );
141
142 Ok(PreparedBindGroup {
143 bindings: BindingResources(vec![]),
144 bind_group,
145 })
146 }88fn prepare_bind_groups(
89 mut commands: Commands,
90 mut views: Query<(Entity, &ViewTarget, Option<&mut PostProcessBindGroups>)>,
91 post_process_pipeline: Option<Res<PostProcessPipeline>>,
92 pipeline_cache: Res<PipelineCache>,
93 settings_uniforms: Res<ComponentUniforms<PostProcessSettings>>,
94 render_device: Res<RenderDevice>,
95) {
96 let Some(post_process_pipeline) = post_process_pipeline else {
97 return;
98 };
99 let Some(settings_binding) = settings_uniforms.uniforms().binding() else {
100 return;
101 };
102
103 let create_bind_group = |texture: &TextureView| {
104 (
105 texture.id(),
106 render_device.create_bind_group(
107 "post_process_bind_group",
108 &pipeline_cache.get_bind_group_layout(&post_process_pipeline.layout),
109 &BindGroupEntries::sequential((
110 texture,
111 &post_process_pipeline.sampler,
112 settings_binding.clone(),
113 )),
114 ),
115 )
116 };
117
118 for (entity, view_target, mut maybe_bind_groups) in &mut views {
119 let main_texture_view = view_target.main_texture_view();
120 let main_texture_other_view = view_target.main_texture_other_view();
121
122 // Only update the cached bind groups if the main texture has changed
123 if let Some(bind_groups) = &mut maybe_bind_groups {
124 if bind_groups.a.0 != main_texture_view.id() {
125 bind_groups.a = create_bind_group(main_texture_view);
126 }
127 if bind_groups.b.0 != main_texture_other_view.id() {
128 bind_groups.b = create_bind_group(main_texture_other_view);
129 }
130 } else {
131 // Create the bind groups and add them to the view
132 commands.entity(entity).insert(PostProcessBindGroups {
133 a: create_bind_group(main_texture_view),
134 b: create_bind_group(main_texture_other_view),
135 });
136 }
137 }
138}202fn draw_mesh_shader_cubes(
203 mut views: Query<(
204 &ExtractedCamera,
205 &ExtractedView,
206 &ViewTarget,
207 &ViewDepthStencilTexture,
208 &ViewUniformOffset,
209 Option<&MainPassResolutionOverride>,
210 )>,
211 mut render_context: RenderContext,
212 data: Res<MyMeshShaderDrawNode>,
213 view_uniforms: Res<ViewUniforms>,
214 globals: Res<GlobalsBuffer>,
215 pipeline_cache: Res<PipelineCache>,
216) {
217 let Some(mesh_pipeline) = pipeline_cache.get_render_pipeline(data.mesh_pipeline) else {
218 return;
219 };
220
221 for (camera, _, target, depth, view_uniform_offset, resolution_override) in &mut views {
222 let Some(view_binding) = view_uniforms.uniforms.binding() else {
223 return;
224 };
225 let Some(globals_binding) = globals.buffer.binding() else {
226 return;
227 };
228 let bind_group = render_context.render_device().create_bind_group(
229 "custom_task_mesh_bind_group",
230 &pipeline_cache.get_bind_group_layout(&data.layout),
231 &BindGroupEntries::sequential((globals_binding, view_binding)),
232 );
233
234 {
235 let mut pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
236 label: Some("custom_mesh_shader_pass"),
237 // Write directly to the view target
238 color_attachments: &[Some(target.get_color_attachment())],
239 depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
240 timestamp_writes: None,
241 occlusion_query_set: None,
242 multiview_mask: None,
243 });
244
245 pass.set_render_pipeline(mesh_pipeline);
246 pass.set_bind_group(0, &bind_group, &[view_uniform_offset.offset]);
247 if let Some(viewport) =
248 Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override)
249 {
250 pass.set_camera_viewport(&viewport);
251 }
252
253 // Since this MeshPipeline has a task shader, this call
254 // dispatches the task shader workgroup
255 pass.draw_mesh_tasks(1, 1, 1);
256 }
257 }
258}Sourcepub fn set_shader(&mut self, id: AssetId<Shader>, shader: Shader)
pub fn set_shader(&mut self, id: AssetId<Shader>, shader: Shader)
Sourcepub fn remove_shader(&mut self, shader: AssetId<Shader>)
pub fn remove_shader(&mut self, shader: AssetId<Shader>)
Removes a Shader from this cache if it exists.
Sourcepub fn process_queue(&mut self)
pub fn process_queue(&mut self)
Process the pipeline queue and create all pending pipelines if possible.
This is generally called automatically during the RenderSystems::Render step, but can
be called manually to force creation at a different time.
Trait Implementations§
Source§impl Component for PipelineCache
impl Component for PipelineCache
Source§const STORAGE_TYPE: StorageType = bevy_ecs::component::StorageType::SparseSet
const STORAGE_TYPE: StorageType = bevy_ecs::component::StorageType::SparseSet
Source§type Mutability = Mutable
type Mutability = Mutable
Component<Mutability = Mutable>,
while immutable components will instead have Component<Mutability = Immutable>. Read moreSource§fn register_required_components(
_requiree: ComponentId,
required_components: &mut RequiredComponentsRegistrator<'_, '_>,
)
fn register_required_components( _requiree: ComponentId, required_components: &mut RequiredComponentsRegistrator<'_, '_>, )
Source§fn clone_behavior() -> ComponentCloneBehavior
fn clone_behavior() -> ComponentCloneBehavior
Source§fn relationship_accessor() -> Option<ComponentRelationshipAccessor<PipelineCache>>
fn relationship_accessor() -> Option<ComponentRelationshipAccessor<PipelineCache>>
ComponentRelationshipAccessor required for working with relationships in dynamic contexts. Read moreSource§fn on_add() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_add() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
Source§fn on_insert() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_insert() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
Source§fn on_discard() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_discard() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
Source§fn on_remove() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_remove() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
Source§fn on_despawn() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
fn on_despawn() -> Option<for<'w> fn(DeferredWorld<'w>, HookContext)>
Source§fn map_entities<E>(_this: &mut Self, _mapper: &mut E)where
E: EntityMapper,
fn map_entities<E>(_this: &mut Self, _mapper: &mut E)where
E: EntityMapper,
EntityMapper. This is used to remap entities in contexts like scenes and entity cloning.
When deriving Component, this is populated by annotating fields containing entities with #[entities] Read moreSource§const HAS_SUMMARY_TICK: bool = false
const HAS_SUMMARY_TICK: bool = false
impl Resource for PipelineCache
Auto Trait Implementations§
impl !Freeze for PipelineCache
impl !RefUnwindSafe for PipelineCache
impl !UnwindSafe for PipelineCache
impl Send for PipelineCache
impl Sync for PipelineCache
impl Unpin for PipelineCache
impl UnsafeUnpin for PipelineCache
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<C> Bundle for Cwhere
C: Component,
impl<C> Bundle for Cwhere
C: Component,
fn component_ids( components: &mut ComponentsRegistrator<'_>, ) -> impl Iterator<Item = ComponentId> + use<C>
Source§fn get_component_ids(
components: &Components,
) -> impl Iterator<Item = Option<ComponentId>>
fn get_component_ids( components: &Components, ) -> impl Iterator<Item = Option<ComponentId>>
Source§impl<C> BundleFromComponents for Cwhere
C: Component,
impl<C> BundleFromComponents for Cwhere
C: Component,
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ConditionalSend for Twhere
T: Send,
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
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<C> DynamicBundle for Cwhere
C: Component,
impl<C> DynamicBundle for Cwhere
C: Component,
Source§unsafe fn get_components(
ptr: MovingPtr<'_, C>,
func: &mut impl FnMut(StorageType, OwningPtr<'_>),
) -> <C as DynamicBundle>::Effect
unsafe fn get_components( ptr: MovingPtr<'_, C>, func: &mut impl FnMut(StorageType, OwningPtr<'_>), ) -> <C as DynamicBundle>::Effect
Source§unsafe fn apply_effect(
_ptr: MovingPtr<'_, MaybeUninit<C>>,
_entity: &mut EntityWorldMut<'_>,
)
unsafe fn apply_effect( _ptr: MovingPtr<'_, MaybeUninit<C>>, _entity: &mut EntityWorldMut<'_>, )
impl<T> ErasedDestructor for Twhere
T: 'static,
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, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
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<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§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.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<C> SceneEffect for Cwhere
C: Component,
impl<C> SceneEffect for Cwhere
C: Component,
Source§fn apply(
self,
context: &mut TemplateContext<'_, '_>,
bundle_writer: &mut BundleWriter<'_>,
)
fn apply( self, context: &mut TemplateContext<'_, '_>, bundle_writer: &mut BundleWriter<'_>, )
impl<T> Settings for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
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.