Bind group entries (#9694)

# Objective

Simplify bind group creation code. alternative to (and based on) #9476

## Solution

- Add a `BindGroupEntries` struct that can transparently be used where
`&[BindGroupEntry<'b>]` is required in BindGroupDescriptors.

Allows constructing the descriptor's entries as:
```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::with_indexes((
        (2, &my_sampler),
        (3, my_uniform),
    )),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 2,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 3,
            resource: my_uniform,
        },
    ],
);
```

or

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &BindGroupEntries::sequential((&my_sampler, my_uniform)),
);
```

instead of

```rust
render_device.create_bind_group(
    "my_bind_group",
    &my_layout,
    &[
        BindGroupEntry {
            binding: 0,
            resource: BindingResource::Sampler(&my_sampler),
        },
        BindGroupEntry {
            binding: 1,
            resource: my_uniform,
        },
    ],
);
```

the structs has no user facing macros, is tuple-type-based so stack
allocated, and has no noticeable impact on compile time.

- Also adds a `DynamicBindGroupEntries` struct with a similar api that
uses a `Vec` under the hood and allows extending the entries.
- Modifies `RenderDevice::create_bind_group` to take separate arguments
`label`, `layout` and `entries` instead of a `BindGroupDescriptor`
struct. The struct can't be stored due to the internal references, and
with only 3 members arguably does not add enough context to justify
itself.
- Modify the codebase to use the new api and the `BindGroupEntries` /
`DynamicBindGroupEntries` structs where appropriate (whenever the
entries slice contains more than 1 member).

## Migration Guide

- Calls to `RenderDevice::create_bind_group({BindGroupDescriptor {
label, layout, entries })` must be amended to
`RenderDevice::create_bind_group(label, layout, entries)`.
- If `label`s have been specified as `"bind_group_name".into()`, they
need to change to just `"bind_group_name"`. `Some("bind_group_name")`
and `None` will still work, but `Some("bind_group_name")` can optionally
be simplified to just `"bind_group_name"`.

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
robtfm 2023-10-21 16:39:22 +01:00 committed by GitHub
parent 61bad4eb57
commit 6f2a5cb862
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 701 additions and 830 deletions

View file

@ -170,30 +170,16 @@ impl ViewNode for BloomNode {
// First downsample pass
{
let downsampling_first_bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("bloom_downsampling_first_bind_group"),
layout: &downsampling_pipeline_res.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
// Read from main texture directly
resource: BindingResource::TextureView(
view_target.main_texture_view(),
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&bind_groups.sampler),
},
BindGroupEntry {
binding: 2,
resource: uniforms.clone(),
},
],
});
let downsampling_first_bind_group = render_context.render_device().create_bind_group(
"bloom_downsampling_first_bind_group",
&downsampling_pipeline_res.bind_group_layout,
&BindGroupEntries::sequential((
// Read from main texture directly
view_target.main_texture_view(),
&bind_groups.sampler,
uniforms.clone(),
)),
);
let view = &bloom_texture.view(0);
let mut downsampling_first_pass =
@ -416,46 +402,28 @@ fn prepare_bloom_bind_groups(
let mut downsampling_bind_groups = Vec::with_capacity(bind_group_count);
for mip in 1..bloom_texture.mip_count {
downsampling_bind_groups.push(render_device.create_bind_group(&BindGroupDescriptor {
label: Some("bloom_downsampling_bind_group"),
layout: &downsampling_pipeline.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&bloom_texture.view(mip - 1)),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(sampler),
},
BindGroupEntry {
binding: 2,
resource: uniforms.binding().unwrap(),
},
],
}));
downsampling_bind_groups.push(render_device.create_bind_group(
"bloom_downsampling_bind_group",
&downsampling_pipeline.bind_group_layout,
&BindGroupEntries::sequential((
&bloom_texture.view(mip - 1),
sampler,
uniforms.binding().unwrap(),
)),
));
}
let mut upsampling_bind_groups = Vec::with_capacity(bind_group_count);
for mip in (0..bloom_texture.mip_count).rev() {
upsampling_bind_groups.push(render_device.create_bind_group(&BindGroupDescriptor {
label: Some("bloom_upsampling_bind_group"),
layout: &upsampling_pipeline.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&bloom_texture.view(mip)),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(sampler),
},
BindGroupEntry {
binding: 2,
resource: uniforms.binding().unwrap(),
},
],
}));
upsampling_bind_groups.push(render_device.create_bind_group(
"bloom_upsampling_bind_group",
&upsampling_pipeline.bind_group_layout,
&BindGroupEntries::sequential((
&bloom_texture.view(mip),
sampler,
uniforms.binding().unwrap(),
)),
));
}
commands.entity(entity).insert(BloomBindGroups {

View file

@ -7,8 +7,8 @@ use bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferId, Operations,
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, TextureViewId,
BindGroup, BindGroupEntries, BufferId, Operations, PipelineCache,
RenderPassColorAttachment, RenderPassDescriptor, TextureViewId,
},
renderer::RenderContext,
view::{ExtractedView, ViewTarget},
@ -77,29 +77,15 @@ impl Node for CASNode {
bind_group
}
cached_bind_group => {
let bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("cas_bind_group"),
layout: &sharpening_pipeline.texture_bind_group,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(view_target.source),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(
&sharpening_pipeline.sampler,
),
},
BindGroupEntry {
binding: 2,
resource: uniforms,
},
],
});
let bind_group = render_context.render_device().create_bind_group(
"cas_bind_group",
&sharpening_pipeline.texture_bind_group,
&BindGroupEntries::sequential((
view_target.source,
&sharpening_pipeline.sampler,
uniforms,
)),
);
let (_, _, bind_group) =
cached_bind_group.insert((uniforms_id, source.id(), bind_group));

View file

@ -18,10 +18,7 @@ use bevy_render::{
use bevy_ecs::query::QueryItem;
use bevy_render::{
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_resource::{
BindGroupDescriptor, BindGroupEntry, BindingResource, Operations, PipelineCache,
RenderPassDescriptor,
},
render_resource::{Operations, PipelineCache, RenderPassDescriptor},
renderer::RenderContext,
};
@ -94,18 +91,11 @@ impl ViewNode for CopyDeferredLightingIdNode {
return Ok(());
};
let bind_group = render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("copy_deferred_lighting_id_bind_group"),
layout: &copy_deferred_lighting_id_pipeline.layout,
entries: &[BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&deferred_lighting_pass_id_texture.default_view,
),
}],
});
let bind_group = render_context.render_device().create_bind_group(
"copy_deferred_lighting_id_bind_group",
&copy_deferred_lighting_id_pipeline.layout,
&BindGroupEntries::single(&deferred_lighting_pass_id_texture.default_view),
);
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("copy_deferred_lighting_id_pass"),

View file

@ -6,9 +6,8 @@ use bevy_ecs::query::QueryItem;
use bevy_render::{
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode, Operations,
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
TextureViewId,
BindGroup, BindGroupEntries, FilterMode, Operations, PipelineCache,
RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor, TextureViewId,
},
renderer::RenderContext,
view::ViewTarget,
@ -61,23 +60,11 @@ impl ViewNode for FxaaNode {
..default()
});
let bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &fxaa_pipeline.texture_bind_group,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(source),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&sampler),
},
],
});
let bind_group = render_context.render_device().create_bind_group(
None,
&fxaa_pipeline.texture_bind_group,
&BindGroupEntries::sequential((source, &sampler)),
);
let (_, bind_group) = cached_bind_group.insert((source.id(), bind_group));
bind_group

View file

@ -8,6 +8,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
render_resource::BindGroupEntries,
renderer::RenderContext,
view::{Msaa, ViewTarget},
Render, RenderSet,
@ -90,23 +91,11 @@ impl Node for MsaaWritebackNode {
depth_stencil_attachment: None,
};
let bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &blit_pipeline.texture_bind_group,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(post_process.source),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&blit_pipeline.sampler),
},
],
});
let bind_group = render_context.render_device().create_bind_group(
None,
&blit_pipeline.texture_bind_group,
&BindGroupEntries::sequential((post_process.source, &blit_pipeline.sampler)),
);
let mut render_pass = render_context
.command_encoder()

View file

@ -10,13 +10,13 @@ use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType,
CachedRenderPipelineId, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
DepthStencilState, FragmentState, MultisampleState, PipelineCache, PrimitiveState,
RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages, ShaderType,
SpecializedRenderPipeline, SpecializedRenderPipelines, StencilFaceState, StencilState,
TextureFormat, TextureSampleType, TextureViewDimension, VertexState,
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, BufferBindingType, CachedRenderPipelineId,
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
FragmentState, MultisampleState, PipelineCache, PrimitiveState, RenderPipelineDescriptor,
SamplerBindingType, Shader, ShaderStages, ShaderType, SpecializedRenderPipeline,
SpecializedRenderPipelines, StencilFaceState, StencilState, TextureFormat,
TextureSampleType, TextureViewDimension, VertexState,
},
renderer::RenderDevice,
texture::{BevyDefault, Image},
@ -224,24 +224,15 @@ fn prepare_skybox_bind_groups(
if let (Some(skybox), Some(view_uniforms)) =
(images.get(&skybox.0), view_uniforms.uniforms.binding())
{
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("skybox_bind_group"),
layout: &pipeline.bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&skybox.texture_view),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&skybox.sampler),
},
BindGroupEntry {
binding: 2,
resource: view_uniforms,
},
],
});
let bind_group = render_device.create_bind_group(
"skybox_bind_group",
&pipeline.bind_group_layout,
&BindGroupEntries::sequential((
&skybox.texture_view,
&skybox.sampler,
view_uniforms,
)),
);
commands.entity(entity).insert(SkyboxBindGroup(bind_group));
}

View file

@ -21,13 +21,13 @@ use bevy_render::{
prelude::{Camera, Projection},
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
ColorTargetState, ColorWrites, Extent3d, FilterMode, FragmentState, MultisampleState,
Operations, PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor,
RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, Shader,
ShaderStages, SpecializedRenderPipeline, SpecializedRenderPipelines, TextureDescriptor,
TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureViewDimension,
BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
BindingType, CachedRenderPipelineId, ColorTargetState, ColorWrites, Extent3d, FilterMode,
FragmentState, MultisampleState, Operations, PipelineCache, PrimitiveState,
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, Sampler,
SamplerBindingType, SamplerDescriptor, Shader, ShaderStages, SpecializedRenderPipeline,
SpecializedRenderPipelines, TextureDescriptor, TextureDimension, TextureFormat,
TextureSampleType, TextureUsages, TextureViewDimension,
},
renderer::{RenderContext, RenderDevice},
texture::{BevyDefault, CachedTexture, TextureCache},
@ -197,45 +197,18 @@ impl ViewNode for TAANode {
};
let view_target = view_target.post_process_write();
let taa_bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("taa_bind_group"),
layout: &pipelines.taa_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(view_target.source),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(
&taa_history_textures.read.default_view,
),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(
&prepass_motion_vectors_texture.default_view,
),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::TextureView(
&prepass_depth_texture.default_view,
),
},
BindGroupEntry {
binding: 4,
resource: BindingResource::Sampler(&pipelines.nearest_sampler),
},
BindGroupEntry {
binding: 5,
resource: BindingResource::Sampler(&pipelines.linear_sampler),
},
],
});
let taa_bind_group = render_context.render_device().create_bind_group(
"taa_bind_group",
&pipelines.taa_bind_group_layout,
&BindGroupEntries::sequential((
view_target.source,
&taa_history_textures.read.default_view,
&prepass_motion_vectors_texture.default_view,
&prepass_depth_texture.default_view,
&pipelines.nearest_sampler,
&pipelines.linear_sampler,
)),
);
{
let mut taa_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {

View file

@ -306,8 +306,7 @@ pub fn get_lut_bindings<'a>(
images: &'a RenderAssets<Image>,
tonemapping_luts: &'a TonemappingLuts,
tonemapping: &Tonemapping,
bindings: [u32; 2],
) -> [BindGroupEntry<'a>; 2] {
) -> (&'a TextureView, &'a Sampler) {
let image = match tonemapping {
// AgX lut texture used when tonemapping doesn't need a texture since it's very small (32x32x32)
Tonemapping::None
@ -320,16 +319,7 @@ pub fn get_lut_bindings<'a>(
Tonemapping::BlenderFilmic => &tonemapping_luts.blender_filmic,
};
let lut_image = images.get(image).unwrap();
[
BindGroupEntry {
binding: bindings[0],
resource: BindingResource::TextureView(&lut_image.texture_view),
},
BindGroupEntry {
binding: bindings[1],
resource: BindingResource::Sampler(&lut_image.sampler),
},
]
(&lut_image.texture_view, &lut_image.sampler)
}
pub fn get_lut_bind_group_layout_entries(bindings: [u32; 2]) -> [BindGroupLayoutEntry; 2] {

View file

@ -7,9 +7,8 @@ use bevy_render::{
render_asset::RenderAssets,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferId, LoadOp,
Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
SamplerDescriptor, TextureViewId,
BindGroup, BindGroupEntries, BufferId, LoadOp, Operations, PipelineCache,
RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor, TextureViewId,
},
renderer::RenderContext,
texture::Image,
@ -88,36 +87,19 @@ impl ViewNode for TonemappingNode {
let tonemapping_luts = world.resource::<TonemappingLuts>();
let mut entries = vec![
BindGroupEntry {
binding: 0,
resource: view_uniforms.binding().unwrap(),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(source),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::Sampler(&sampler),
},
];
let lut_bindings = get_lut_bindings(gpu_images, tonemapping_luts, tonemapping);
entries.extend(get_lut_bindings(
gpu_images,
tonemapping_luts,
tonemapping,
[3, 4],
));
let bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &tonemapping_pipeline.texture_bind_group,
entries: &entries,
});
let bind_group = render_context.render_device().create_bind_group(
None,
&tonemapping_pipeline.texture_bind_group,
&BindGroupEntries::sequential((
view_uniforms,
source,
&sampler,
lut_bindings.0,
lut_bindings.1,
)),
);
let (_, _, bind_group) =
cached_bind_group.insert((view_uniforms_id, source.id(), bind_group));

View file

@ -4,9 +4,8 @@ use bevy_render::{
camera::{CameraOutputMode, ExtractedCamera},
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, LoadOp, Operations,
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
TextureViewId,
BindGroup, BindGroupEntries, LoadOp, Operations, PipelineCache, RenderPassColorAttachment,
RenderPassDescriptor, SamplerDescriptor, TextureViewId,
},
renderer::RenderContext,
view::ViewTarget,
@ -57,23 +56,11 @@ impl ViewNode for UpscalingNode {
.render_device()
.create_sampler(&SamplerDescriptor::default());
let bind_group =
render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &blit_pipeline.texture_bind_group,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(upscaled_texture),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&sampler),
},
],
});
let bind_group = render_context.render_device().create_bind_group(
None,
&blit_pipeline.texture_bind_group,
&BindGroupEntries::sequential((upscaled_texture, &sampler)),
);
let (_, bind_group) = cached_bind_group.insert((upscaled_texture.id(), bind_group));
bind_group

View file

@ -52,7 +52,7 @@ use bevy_render::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor,
BufferUsages, Shader, ShaderStages, ShaderType, VertexAttribute, VertexBufferLayout,
VertexFormat, VertexStepMode,
@ -422,14 +422,11 @@ fn prepare_line_gizmo_bind_group(
) {
if let Some(binding) = line_gizmo_uniforms.uniforms().binding() {
commands.insert_resource(LineGizmoUniformBindgroup {
bindgroup: render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: binding,
}],
label: Some("LineGizmoUniform bindgroup"),
layout: &line_gizmo_uniform_layout.layout,
}),
bindgroup: render_device.create_bind_group(
"LineGizmoUniform bindgroup",
&line_gizmo_uniform_layout.layout,
&BindGroupEntries::single(binding),
),
});
}
}

View file

@ -191,16 +191,11 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode {
return Ok(());
};
let bind_group_1 = render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("deferred_lighting_layout_group_1"),
layout: &deferred_lighting_layout.bind_group_layout_1,
entries: &[BindGroupEntry {
binding: 0,
resource: deferred_lighting_pass_id_binding.clone(),
}],
});
let bind_group_1 = render_context.render_device().create_bind_group(
"deferred_lighting_layout_group_1",
&deferred_lighting_layout.bind_group_layout_1,
&BindGroupEntries::single(deferred_lighting_pass_id_binding),
);
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("deferred_lighting_pass"),

View file

@ -7,8 +7,8 @@ use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_resource::{
BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, SamplerBindingType,
Shader, ShaderStages, TextureSampleType, TextureViewDimension,
BindGroupLayoutEntry, BindingType, Sampler, SamplerBindingType, Shader, ShaderStages,
TextureSampleType, TextureView, TextureViewDimension,
},
texture::{FallbackImageCubemap, Image},
};
@ -65,8 +65,7 @@ pub fn get_bindings<'a>(
environment_map_light: Option<&EnvironmentMapLight>,
images: &'a RenderAssets<Image>,
fallback_image_cubemap: &'a FallbackImageCubemap,
bindings: [u32; 3],
) -> [BindGroupEntry<'a>; 3] {
) -> (&'a TextureView, &'a TextureView, &'a Sampler) {
let (diffuse_map, specular_map) = match (
environment_map_light.and_then(|env_map| images.get(&env_map.diffuse_map)),
environment_map_light.and_then(|env_map| images.get(&env_map.specular_map)),
@ -80,20 +79,7 @@ pub fn get_bindings<'a>(
),
};
[
BindGroupEntry {
binding: bindings[0],
resource: BindingResource::TextureView(diffuse_map),
},
BindGroupEntry {
binding: bindings[1],
resource: BindingResource::TextureView(specular_map),
},
BindGroupEntry {
binding: bindings[2],
resource: BindingResource::Sampler(&fallback_image_cubemap.sampler),
},
]
(diffuse_map, specular_map, &fallback_image_cubemap.sampler)
}
pub fn get_bind_group_layout_entries(bindings: [u32; 3]) -> [BindGroupLayoutEntry; 3] {

View file

@ -35,7 +35,7 @@ use bevy_render::{
RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, BufferBindingType, ColorTargetState, ColorWrites,
CompareFunction, DepthBiasState, DepthStencilState, DynamicUniformBuffer, FragmentState,
FrontFace, MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PushConstantRange,
@ -713,42 +713,22 @@ pub fn prepare_prepass_view_bind_group<M: Material>(
view_uniforms.uniforms.binding(),
globals_buffer.buffer.binding(),
) {
prepass_view_bind_group.no_motion_vectors =
Some(render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: view_binding.clone(),
},
BindGroupEntry {
binding: 1,
resource: globals_binding.clone(),
},
],
label: Some("prepass_view_no_motion_vectors_bind_group"),
layout: &prepass_pipeline.view_layout_no_motion_vectors,
}));
prepass_view_bind_group.no_motion_vectors = Some(render_device.create_bind_group(
"prepass_view_no_motion_vectors_bind_group",
&prepass_pipeline.view_layout_no_motion_vectors,
&BindGroupEntries::sequential((view_binding.clone(), globals_binding.clone())),
));
if let Some(previous_view_proj_binding) = previous_view_proj_uniforms.uniforms.binding() {
prepass_view_bind_group.motion_vectors =
Some(render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: view_binding,
},
BindGroupEntry {
binding: 1,
resource: globals_binding,
},
BindGroupEntry {
binding: 2,
resource: previous_view_proj_binding,
},
],
label: Some("prepass_view_motion_vectors_bind_group"),
layout: &prepass_pipeline.view_layout_motion_vectors,
}));
prepass_view_bind_group.motion_vectors = Some(render_device.create_bind_group(
"prepass_view_motion_vectors_bind_group",
&prepass_pipeline.view_layout_motion_vectors,
&BindGroupEntries::sequential((
view_binding,
globals_binding,
previous_view_proj_binding,
)),
));
}
}
}

View file

@ -1,7 +1,7 @@
use bevy_core_pipeline::prepass::ViewPrepassTextures;
use bevy_render::render_resource::{
BindGroupEntry, BindGroupLayoutEntry, BindingResource, BindingType, ShaderStages,
TextureAspect, TextureSampleType, TextureView, TextureViewDescriptor, TextureViewDimension,
BindGroupLayoutEntry, BindingType, ShaderStages, TextureAspect, TextureSampleType, TextureView,
TextureViewDescriptor, TextureViewDimension,
};
use bevy_utils::default;
use smallvec::SmallVec;
@ -83,51 +83,7 @@ pub fn get_bind_group_layout_entries(
result
}
// Needed so the texture views can live long enough.
pub struct PrepassBindingsSet {
depth_view: Option<TextureView>,
normal_view: Option<TextureView>,
motion_vectors_view: Option<TextureView>,
deferred_view: Option<TextureView>,
}
impl PrepassBindingsSet {
pub fn get_entries(&self, bindings: [u32; 4]) -> SmallVec<[BindGroupEntry; 4]> {
let mut result = SmallVec::<[BindGroupEntry; 4]>::new();
if let Some(ref depth_view) = self.depth_view {
result.push(BindGroupEntry {
binding: bindings[0],
resource: BindingResource::TextureView(depth_view),
});
}
if let Some(ref normal_view) = self.normal_view {
result.push(BindGroupEntry {
binding: bindings[1],
resource: BindingResource::TextureView(normal_view),
});
}
if let Some(ref motion_vectors_view) = self.motion_vectors_view {
result.push(BindGroupEntry {
binding: bindings[2],
resource: BindingResource::TextureView(motion_vectors_view),
});
}
if let Some(ref deferred_view) = self.deferred_view {
result.push(BindGroupEntry {
binding: bindings[3],
resource: BindingResource::TextureView(deferred_view),
});
}
result
}
}
pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> PrepassBindingsSet {
pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> [Option<TextureView>; 4] {
let depth_desc = TextureViewDescriptor {
label: Some("prepass_depth"),
aspect: TextureAspect::DepthOnly,
@ -149,10 +105,5 @@ pub fn get_bindings(prepass_textures: Option<&ViewPrepassTextures>) -> PrepassBi
.and_then(|x| x.deferred.as_ref())
.map(|texture| texture.default_view.clone());
PrepassBindingsSet {
depth_view,
normal_view,
motion_vectors_view,
deferred_view,
}
[depth_view, normal_view, motion_vectors_view, deferred_view]
}

View file

@ -4,8 +4,7 @@ use bevy_math::Mat4;
use bevy_render::{
mesh::morph::MAX_MORPH_WEIGHTS,
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupLayout, BindGroupLayoutDescriptor,
BindingResource, Buffer, TextureView,
BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindingResource, Buffer, TextureView,
},
renderer::RenderDevice,
};
@ -179,11 +178,11 @@ impl MeshLayouts {
// ---------- BindGroup methods ----------
pub fn model_only(&self, render_device: &RenderDevice, model: &BindingResource) -> BindGroup {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[entry::model(0, model.clone())],
layout: &self.model_only,
label: Some("model_only_mesh_bind_group"),
})
render_device.create_bind_group(
"model_only_mesh_bind_group",
&self.model_only,
&[entry::model(0, model.clone())],
)
}
pub fn skinned(
&self,
@ -191,11 +190,11 @@ impl MeshLayouts {
model: &BindingResource,
skin: &Buffer,
) -> BindGroup {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[entry::model(0, model.clone()), entry::skinning(1, skin)],
layout: &self.skinned,
label: Some("skinned_mesh_bind_group"),
})
render_device.create_bind_group(
"skinned_mesh_bind_group",
&self.skinned,
&[entry::model(0, model.clone()), entry::skinning(1, skin)],
)
}
pub fn morphed(
&self,
@ -204,15 +203,15 @@ impl MeshLayouts {
weights: &Buffer,
targets: &TextureView,
) -> BindGroup {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
render_device.create_bind_group(
"morphed_mesh_bind_group",
&self.morphed,
&[
entry::model(0, model.clone()),
entry::weights(2, weights),
entry::targets(3, targets),
],
layout: &self.morphed,
label: Some("morphed_mesh_bind_group"),
})
)
}
pub fn morphed_skinned(
&self,
@ -222,15 +221,15 @@ impl MeshLayouts {
weights: &Buffer,
targets: &TextureView,
) -> BindGroup {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
render_device.create_bind_group(
"morphed_skinned_mesh_bind_group",
&self.morphed_skinned,
&[
entry::model(0, model.clone()),
entry::skinning(1, skin),
entry::weights(2, weights),
entry::targets(3, targets),
],
layout: &self.morphed_skinned,
label: Some("morphed_skinned_mesh_bind_group"),
})
)
}
}

View file

@ -15,9 +15,9 @@ use bevy_render::{
globals::{GlobalsBuffer, GlobalsUniform},
render_asset::RenderAssets,
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, SamplerBindingType,
ShaderStages, ShaderType, TextureFormat, TextureSampleType, TextureViewDimension,
BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,
BufferBindingType, DynamicBindGroupEntries, SamplerBindingType, ShaderStages, ShaderType,
TextureFormat, TextureSampleType, TextureViewDimension,
},
renderer::RenderDevice,
texture::{BevyDefault, FallbackImageCubemap, FallbackImageMsaa, Image},
@ -383,8 +383,8 @@ pub fn prepare_mesh_view_bind_groups(
) {
for (
entity,
view_shadow_bindings,
view_cluster_bindings,
shadow_bindings,
cluster_bindings,
ssao_textures,
prepass_textures,
environment_map,
@ -395,108 +395,58 @@ pub fn prepare_mesh_view_bind_groups(
.image_for_samplecount(1, TextureFormat::bevy_default())
.texture_view
.clone();
let ssao_view = ssao_textures
.map(|t| &t.screen_space_ambient_occlusion_texture.default_view)
.unwrap_or(&fallback_ssao);
let layout = &mesh_pipeline.get_view_layout(
MeshPipelineViewLayoutKey::from(*msaa)
| MeshPipelineViewLayoutKey::from(prepass_textures),
);
let mut entries = vec![
BindGroupEntry {
binding: 0,
resource: view_binding.clone(),
},
BindGroupEntry {
binding: 1,
resource: light_binding.clone(),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(
&view_shadow_bindings.point_light_depth_texture_view,
),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::Sampler(&shadow_samplers.point_light_sampler),
},
BindGroupEntry {
binding: 4,
resource: BindingResource::TextureView(
&view_shadow_bindings.directional_light_depth_texture_view,
),
},
BindGroupEntry {
binding: 5,
resource: BindingResource::Sampler(&shadow_samplers.directional_light_sampler),
},
BindGroupEntry {
binding: 6,
resource: point_light_binding.clone(),
},
BindGroupEntry {
binding: 7,
resource: view_cluster_bindings.light_index_lists_binding().unwrap(),
},
BindGroupEntry {
binding: 8,
resource: view_cluster_bindings.offsets_and_counts_binding().unwrap(),
},
BindGroupEntry {
binding: 9,
resource: globals.clone(),
},
BindGroupEntry {
binding: 10,
resource: fog_binding.clone(),
},
BindGroupEntry {
binding: 11,
resource: BindingResource::TextureView(
ssao_textures
.map(|t| &t.screen_space_ambient_occlusion_texture.default_view)
.unwrap_or(&fallback_ssao),
),
},
];
let mut entries = DynamicBindGroupEntries::new_with_indices((
(0, view_binding.clone()),
(1, light_binding.clone()),
(2, &shadow_bindings.point_light_depth_texture_view),
(3, &shadow_samplers.point_light_sampler),
(4, &shadow_bindings.directional_light_depth_texture_view),
(5, &shadow_samplers.directional_light_sampler),
(6, point_light_binding.clone()),
(7, cluster_bindings.light_index_lists_binding().unwrap()),
(8, cluster_bindings.offsets_and_counts_binding().unwrap()),
(9, globals.clone()),
(10, fog_binding.clone()),
(11, ssao_view),
));
let env_map = environment_map::get_bindings(
environment_map,
&images,
&fallback_cubemap,
[12, 13, 14],
);
entries.extend_from_slice(&env_map);
let env_map_bindings =
environment_map::get_bindings(environment_map, &images, &fallback_cubemap);
entries = entries.extend_with_indices((
(12, env_map_bindings.0),
(13, env_map_bindings.1),
(14, env_map_bindings.2),
));
let tonemapping_luts =
get_lut_bindings(&images, &tonemapping_luts, tonemapping, [15, 16]);
entries.extend_from_slice(&tonemapping_luts);
let label = Some("mesh_view_bind_group");
let lut_bindings = get_lut_bindings(&images, &tonemapping_luts, tonemapping);
entries = entries.extend_with_indices(((15, lut_bindings.0), (16, lut_bindings.1)));
// When using WebGL, we can't have a depth texture with multisampling
let prepass_bindings = if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32")))
|| (cfg!(all(feature = "webgl", target_arch = "wasm32")) && msaa.samples() == 1)
let prepass_bindings;
if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32"))) || msaa.samples() == 1
{
Some(prepass::get_bindings(prepass_textures))
} else {
None
};
// This if statement is here to make the borrow checker happy.
// Ideally we could just have `entries.extend_from_slice(&prepass_bindings.get_entries([17, 18, 19, 20]));`
// in the existing if statement above, but that either doesn't allow `prepass_bindings` to live long enough,
// as its used when creating the bind group at the end of the function, or causes a `cannot move out of` error.
if let Some(prepass_bindings) = &prepass_bindings {
entries.extend_from_slice(&prepass_bindings.get_entries([17, 18, 19, 20]));
prepass_bindings = prepass::get_bindings(prepass_textures);
for (binding, index) in prepass_bindings
.iter()
.map(Option::as_ref)
.zip([17, 18, 19, 20])
.flat_map(|(b, i)| b.map(|b| (b, i)))
{
entries = entries.extend_with_indices(((index, binding),));
}
}
commands.entity(entity).insert(MeshViewBindGroup {
value: render_device.create_bind_group(&BindGroupDescriptor {
entries: &entries,
label,
layout,
}),
value: render_device.create_bind_group("mesh_view_bind_group", layout, &entries),
});
}
}

View file

@ -21,12 +21,11 @@ use bevy_render::{
prelude::Camera,
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::{
AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType,
BufferBindingType, CachedComputePipelineId, ComputePassDescriptor,
ComputePipelineDescriptor, Extent3d, FilterMode, PipelineCache, Sampler,
SamplerBindingType, SamplerDescriptor, Shader, ShaderDefVal, ShaderStages, ShaderType,
SpecializedComputePipeline, SpecializedComputePipelines, StorageTextureAccess,
AddressMode, BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, BufferBindingType, CachedComputePipelineId,
ComputePassDescriptor, ComputePipelineDescriptor, Extent3d, FilterMode, PipelineCache,
Sampler, SamplerBindingType, SamplerDescriptor, Shader, ShaderDefVal, ShaderStages,
ShaderType, SpecializedComputePipeline, SpecializedComputePipelines, StorageTextureAccess,
TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
TextureView, TextureViewDescriptor, TextureViewDimension,
},
@ -776,171 +775,63 @@ fn prepare_ssao_bind_groups(
};
for (entity, ssao_textures, prepass_textures) in &views {
let common_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("ssao_common_bind_group"),
layout: &pipelines.common_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::Sampler(&pipelines.point_clamp_sampler),
},
BindGroupEntry {
binding: 1,
resource: view_uniforms.clone(),
},
],
});
let common_bind_group = render_device.create_bind_group(
"ssao_common_bind_group",
&pipelines.common_bind_group_layout,
&BindGroupEntries::sequential((&pipelines.point_clamp_sampler, view_uniforms.clone())),
);
let preprocess_depth_mip_view_descriptor = TextureViewDescriptor {
format: Some(TextureFormat::R16Float),
dimension: Some(TextureViewDimension::D2),
mip_level_count: Some(1),
..default()
let create_depth_view = |mip_level| {
ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view"),
base_mip_level: mip_level,
format: Some(TextureFormat::R16Float),
dimension: Some(TextureViewDimension::D2),
mip_level_count: Some(1),
..default()
})
};
let preprocess_depth_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("ssao_preprocess_depth_bind_group"),
layout: &pipelines.preprocess_depth_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&prepass_textures.depth.as_ref().unwrap().default_view,
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(
&ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view_0"),
base_mip_level: 0,
..preprocess_depth_mip_view_descriptor
}),
),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(
&ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view_1"),
base_mip_level: 1,
..preprocess_depth_mip_view_descriptor
}),
),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::TextureView(
&ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view_2"),
base_mip_level: 2,
..preprocess_depth_mip_view_descriptor
}),
),
},
BindGroupEntry {
binding: 4,
resource: BindingResource::TextureView(
&ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view_3"),
base_mip_level: 3,
..preprocess_depth_mip_view_descriptor
}),
),
},
BindGroupEntry {
binding: 5,
resource: BindingResource::TextureView(
&ssao_textures
.preprocessed_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("ssao_preprocessed_depth_texture_mip_view_4"),
base_mip_level: 4,
..preprocess_depth_mip_view_descriptor
}),
),
},
],
});
let preprocess_depth_bind_group = render_device.create_bind_group(
"ssao_preprocess_depth_bind_group",
&pipelines.preprocess_depth_bind_group_layout,
&BindGroupEntries::sequential((
&prepass_textures.depth.as_ref().unwrap().default_view,
&create_depth_view(0),
&create_depth_view(1),
&create_depth_view(2),
&create_depth_view(3),
&create_depth_view(4),
)),
);
let gtao_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("ssao_gtao_bind_group"),
layout: &pipelines.gtao_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&ssao_textures.preprocessed_depth_texture.default_view,
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(
&prepass_textures.normal.as_ref().unwrap().default_view,
),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(&pipelines.hilbert_index_lut),
},
BindGroupEntry {
binding: 3,
resource: BindingResource::TextureView(
&ssao_textures.ssao_noisy_texture.default_view,
),
},
BindGroupEntry {
binding: 4,
resource: BindingResource::TextureView(
&ssao_textures.depth_differences_texture.default_view,
),
},
BindGroupEntry {
binding: 5,
resource: globals_uniforms.clone(),
},
],
});
let gtao_bind_group = render_device.create_bind_group(
"ssao_gtao_bind_group",
&pipelines.gtao_bind_group_layout,
&BindGroupEntries::sequential((
&ssao_textures.preprocessed_depth_texture.default_view,
&prepass_textures.normal.as_ref().unwrap().default_view,
&pipelines.hilbert_index_lut,
&ssao_textures.ssao_noisy_texture.default_view,
&ssao_textures.depth_differences_texture.default_view,
globals_uniforms.clone(),
)),
);
let spatial_denoise_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Some("ssao_spatial_denoise_bind_group"),
layout: &pipelines.spatial_denoise_bind_group_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&ssao_textures.ssao_noisy_texture.default_view,
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(
&ssao_textures.depth_differences_texture.default_view,
),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::TextureView(
&ssao_textures
.screen_space_ambient_occlusion_texture
.default_view,
),
},
],
});
let spatial_denoise_bind_group = render_device.create_bind_group(
"ssao_spatial_denoise_bind_group",
&pipelines.spatial_denoise_bind_group_layout,
&BindGroupEntries::sequential((
&ssao_textures.ssao_noisy_texture.default_view,
&ssao_textures.depth_differences_texture.default_view,
&ssao_textures
.screen_space_ambient_occlusion_texture
.default_view,
)),
);
commands.entity(entity).insert(SsaoBindGroups {
common_bind_group,

View file

@ -9,10 +9,7 @@ use crate::{
pub use bevy_render_macros::AsBindGroup;
use encase::ShaderType;
use std::ops::Deref;
use wgpu::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
BindingResource,
};
use wgpu::{BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource};
define_atomic_id!(BindGroupId);
render_resource_wrapper!(ErasedBindGroup, wgpu::BindGroup);
@ -289,11 +286,7 @@ pub trait AsBindGroup {
})
.collect::<Vec<_>>();
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: Self::label(),
layout,
entries: &entries,
});
let bind_group = render_device.create_bind_group(Self::label(), layout, &entries);
Ok(PreparedBindGroup {
bindings,

View file

@ -0,0 +1,282 @@
use bevy_utils::all_tuples_with_size;
use wgpu::{BindGroupEntry, BindingResource};
use super::{Sampler, TextureView};
/// Helper for constructing bindgroups.
///
/// Allows constructing the descriptor's entries as:
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &BindGroupEntries::with_indices((
/// (2, &my_sampler),
/// (3, my_uniform),
/// )),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &[
/// BindGroupEntry {
/// binding: 2,
/// resource: BindingResource::Sampler(&my_sampler),
/// },
/// BindGroupEntry {
/// binding: 3,
/// resource: my_uniform,
/// },
/// ],
/// );
/// ```
///
/// or
///
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &BindGroupEntries::sequential((
/// &my_sampler,
/// my_uniform,
/// )),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &[
/// BindGroupEntry {
/// binding: 0,
/// resource: BindingResource::Sampler(&my_sampler),
/// },
/// BindGroupEntry {
/// binding: 1,
/// resource: my_uniform,
/// },
/// ],
/// );
/// ```
///
/// or
///
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &BindGroupEntries::single(my_uniform),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// render_device.create_bind_group(
/// "my_bind_group",
/// &my_layout,
/// &[
/// BindGroupEntry {
/// binding: 0,
/// resource: my_uniform,
/// },
/// ],
/// );
/// ```
pub struct BindGroupEntries<'b, const N: usize = 1> {
entries: [BindGroupEntry<'b>; N],
}
impl<'b, const N: usize> BindGroupEntries<'b, N> {
#[inline]
pub fn sequential(resources: impl IntoBindingArray<'b, N>) -> Self {
let mut i = 0;
Self {
entries: resources.into_array().map(|resource| {
let binding = i;
i += 1;
BindGroupEntry { binding, resource }
}),
}
}
#[inline]
pub fn with_indices(indexed_resources: impl IntoIndexedBindingArray<'b, N>) -> Self {
Self {
entries: indexed_resources
.into_array()
.map(|(binding, resource)| BindGroupEntry { binding, resource }),
}
}
}
impl<'b> BindGroupEntries<'b, 1> {
pub fn single(resource: impl IntoBinding<'b>) -> [BindGroupEntry<'b>; 1] {
[BindGroupEntry {
binding: 0,
resource: resource.into_binding(),
}]
}
}
impl<'b, const N: usize> std::ops::Deref for BindGroupEntries<'b, N> {
type Target = [BindGroupEntry<'b>];
fn deref(&self) -> &[BindGroupEntry<'b>] {
&self.entries
}
}
pub trait IntoBinding<'a> {
fn into_binding(self) -> BindingResource<'a>;
}
impl<'a> IntoBinding<'a> for &'a TextureView {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
BindingResource::TextureView(self)
}
}
impl<'a> IntoBinding<'a> for &'a [&'a wgpu::TextureView] {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
BindingResource::TextureViewArray(self)
}
}
impl<'a> IntoBinding<'a> for &'a Sampler {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
BindingResource::Sampler(self)
}
}
impl<'a> IntoBinding<'a> for BindingResource<'a> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
self
}
}
impl<'a> IntoBinding<'a> for wgpu::BufferBinding<'a> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
BindingResource::Buffer(self)
}
}
pub trait IntoBindingArray<'b, const N: usize> {
fn into_array(self) -> [BindingResource<'b>; N];
}
macro_rules! impl_to_binding_slice {
($N: expr, $(($T: ident, $I: ident)),*) => {
impl<'b, $($T: IntoBinding<'b>),*> IntoBindingArray<'b, $N> for ($($T,)*) {
#[inline]
fn into_array(self) -> [BindingResource<'b>; $N] {
let ($($I,)*) = self;
[$($I.into_binding(), )*]
}
}
}
}
all_tuples_with_size!(impl_to_binding_slice, 1, 32, T, s);
pub trait IntoIndexedBindingArray<'b, const N: usize> {
fn into_array(self) -> [(u32, BindingResource<'b>); N];
}
macro_rules! impl_to_indexed_binding_slice {
($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => {
impl<'b, $($T: IntoBinding<'b>),*> IntoIndexedBindingArray<'b, $N> for ($((u32, $T),)*) {
#[inline]
fn into_array(self) -> [(u32, BindingResource<'b>); $N] {
let ($(($S, $I),)*) = self;
[$(($S, $I.into_binding())), *]
}
}
}
}
all_tuples_with_size!(impl_to_indexed_binding_slice, 1, 32, T, n, s);
pub struct DynamicBindGroupEntries<'b> {
entries: Vec<BindGroupEntry<'b>>,
}
impl<'b> DynamicBindGroupEntries<'b> {
pub fn sequential<const N: usize>(entries: impl IntoBindingArray<'b, N>) -> Self {
Self {
entries: entries
.into_array()
.into_iter()
.enumerate()
.map(|(ix, resource)| BindGroupEntry {
binding: ix as u32,
resource,
})
.collect(),
}
}
pub fn extend_sequential<const N: usize>(
mut self,
entries: impl IntoBindingArray<'b, N>,
) -> Self {
let start = self.entries.last().unwrap().binding + 1;
self.entries.extend(
entries
.into_array()
.into_iter()
.enumerate()
.map(|(ix, resource)| BindGroupEntry {
binding: start + ix as u32,
resource,
}),
);
self
}
pub fn new_with_indices<const N: usize>(entries: impl IntoIndexedBindingArray<'b, N>) -> Self {
Self {
entries: entries
.into_array()
.into_iter()
.map(|(binding, resource)| BindGroupEntry { binding, resource })
.collect(),
}
}
pub fn extend_with_indices<const N: usize>(
mut self,
entries: impl IntoIndexedBindingArray<'b, N>,
) -> Self {
self.entries.extend(
entries
.into_array()
.into_iter()
.map(|(binding, resource)| BindGroupEntry { binding, resource }),
);
self
}
}
impl<'b> std::ops::Deref for DynamicBindGroupEntries<'b> {
type Target = [BindGroupEntry<'b>];
fn deref(&self) -> &[BindGroupEntry<'b>] {
&self.entries
}
}

View file

@ -1,5 +1,6 @@
mod batched_uniform_buffer;
mod bind_group;
mod bind_group_entries;
mod bind_group_layout;
mod buffer;
mod buffer_vec;
@ -14,6 +15,7 @@ mod texture;
mod uniform_buffer;
pub use bind_group::*;
pub use bind_group_entries::*;
pub use bind_group_layout::*;
pub use buffer::*;
pub use buffer_vec::*;

View file

@ -13,6 +13,8 @@ use wgpu::{
util::BufferInitDescriptor, BindingResource, BufferBinding, BufferDescriptor, BufferUsages,
};
use super::IntoBinding;
/// Stores data to be transferred to the GPU and made accessible to shaders as a uniform buffer.
///
/// Uniform buffers are available to shaders on a read-only basis. Uniform buffers are commonly used to make available to shaders
@ -139,6 +141,16 @@ impl<T: ShaderType + WriteInto> UniformBuffer<T> {
}
}
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a UniformBuffer<T> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
self.buffer()
.expect("Failed to get buffer")
.as_entire_buffer_binding()
.into_binding()
}
}
/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic uniform buffer.
///
/// Dynamic uniform buffers are available to shaders on a read-only basis. Dynamic uniform buffers are commonly used to make
@ -367,3 +379,10 @@ impl<'a> BufferMut for QueueWriteBufferViewWrapper<'a> {
self.buffer_view.write(offset, val);
}
}
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicUniformBuffer<T> {
#[inline]
fn into_binding(self) -> BindingResource<'a> {
self.binding().unwrap()
}
}

View file

@ -3,7 +3,9 @@ use crate::render_resource::{
RenderPipeline, Sampler, Texture,
};
use bevy_ecs::system::Resource;
use wgpu::{util::DeviceExt, BufferAsyncError, BufferBindingType};
use wgpu::{
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BufferAsyncError, BufferBindingType,
};
use super::RenderQueue;
@ -82,8 +84,17 @@ impl RenderDevice {
/// Creates a new [`BindGroup`](wgpu::BindGroup).
#[inline]
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup {
let wgpu_bind_group = self.device.create_bind_group(desc);
pub fn create_bind_group<'a>(
&self,
label: impl Into<wgpu::Label<'a>>,
layout: &'a BindGroupLayout,
entries: &'a [BindGroupEntry<'a>],
) -> BindGroup {
let wgpu_bind_group = self.device.create_bind_group(&BindGroupDescriptor {
label: label.into(),
layout,
entries,
});
BindGroup::from(wgpu_bind_group)
}

View file

@ -1,5 +1,7 @@
use crate::{
render_resource::{PipelineCache, SpecializedRenderPipelines, SurfaceTexture, TextureView},
render_resource::{
BindGroupEntries, PipelineCache, SpecializedRenderPipelines, SurfaceTexture, TextureView,
},
renderer::{RenderAdapter, RenderDevice, RenderInstance},
texture::TextureFormatPixelInfo,
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
@ -413,14 +415,11 @@ pub fn prepare_windows(
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = render_device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("screenshot-to-screen-bind-group"),
layout: &screenshot_pipeline.bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture_view),
}],
});
let bind_group = render_device.create_bind_group(
"screenshot-to-screen-bind-group",
&screenshot_pipeline.bind_group_layout,
&BindGroupEntries::single(&texture_view),
);
let pipeline_id = pipelines.specialize(
&pipeline_cache,
&screenshot_pipeline,

View file

@ -596,14 +596,11 @@ pub fn prepare_mesh2d_bind_group(
) {
if let Some(binding) = mesh2d_uniforms.binding() {
commands.insert_resource(Mesh2dBindGroup {
value: render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: binding,
}],
label: Some("mesh2d_bind_group"),
layout: &mesh2d_pipeline.mesh_layout,
}),
value: render_device.create_bind_group(
"mesh2d_bind_group",
&mesh2d_pipeline.mesh_layout,
&BindGroupEntries::single(binding),
),
});
}
}
@ -626,20 +623,11 @@ pub fn prepare_mesh2d_view_bind_groups(
globals_buffer.buffer.binding(),
) {
for entity in &views {
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: view_binding.clone(),
},
BindGroupEntry {
binding: 1,
resource: globals.clone(),
},
],
label: Some("mesh2d_view_bind_group"),
layout: &mesh2d_pipeline.view_layout,
});
let view_bind_group = render_device.create_bind_group(
"mesh2d_view_bind_group",
&mesh2d_pipeline.view_layout,
&BindGroupEntries::sequential((view_binding.clone(), globals.clone())),
);
commands.entity(entity).insert(Mesh2dViewBindGroup {
value: view_bind_group,

View file

@ -21,7 +21,7 @@ use bevy_render::{
DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline,
TrackedRenderPass,
},
render_resource::*,
render_resource::{BindGroupEntries, *},
renderer::{RenderDevice, RenderQueue},
texture::{
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
@ -623,14 +623,11 @@ pub fn prepare_sprites(
// Clear the sprite instances
sprite_meta.sprite_instance_buffer.clear();
sprite_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: view_binding,
}],
label: Some("sprite_view_bind_group"),
layout: &sprite_pipeline.view_layout,
}));
sprite_meta.view_bind_group = Some(render_device.create_bind_group(
"sprite_view_bind_group",
&sprite_pipeline.view_layout,
&BindGroupEntries::single(view_binding),
));
// Index buffer indices
let mut index = 0;
@ -667,22 +664,14 @@ pub fn prepare_sprites(
.values
.entry(batch_image_handle)
.or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&gpu_image.texture_view,
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&gpu_image.sampler),
},
],
label: Some("sprite_material_bind_group"),
layout: &sprite_pipeline.material_layout,
})
render_device.create_bind_group(
"sprite_material_bind_group",
&sprite_pipeline.material_layout,
&BindGroupEntries::sequential((
&gpu_image.texture_view,
&gpu_image.sampler,
)),
)
});
}

View file

@ -5,7 +5,7 @@ use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_hierarchy::Parent;
use bevy_render::render_phase::PhaseItem;
use bevy_render::view::ViewVisibility;
use bevy_render::{ExtractSchedule, Render};
use bevy_render::{render_resource::BindGroupEntries, ExtractSchedule, Render};
use bevy_window::{PrimaryWindow, Window};
pub use pipeline::*;
pub use render_pass::*;
@ -812,14 +812,11 @@ pub fn prepare_uinodes(
let mut batches: Vec<(Entity, UiBatch)> = Vec::with_capacity(*previous_len);
ui_meta.vertices.clear();
ui_meta.view_bind_group = Some(render_device.create_bind_group(&BindGroupDescriptor {
entries: &[BindGroupEntry {
binding: 0,
resource: view_binding,
}],
label: Some("ui_view_bind_group"),
layout: &ui_pipeline.view_layout,
}));
ui_meta.view_bind_group = Some(render_device.create_bind_group(
"ui_view_bind_group",
&ui_pipeline.view_layout,
&BindGroupEntries::single(view_binding),
));
// Vertex buffer index
let mut index = 0;
@ -851,24 +848,14 @@ pub fn prepare_uinodes(
.values
.entry(batch_image_handle)
.or_insert_with(|| {
render_device.create_bind_group(&BindGroupDescriptor {
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(
&gpu_image.texture_view,
),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(
&gpu_image.sampler,
),
},
],
label: Some("ui_material_bind_group"),
layout: &ui_pipeline.image_layout,
})
render_device.create_bind_group(
"ui_material_bind_group",
&ui_pipeline.image_layout,
&BindGroupEntries::sequential((
&gpu_image.texture_view,
&gpu_image.sampler,
)),
)
});
existing_batch = batches.last_mut();

View file

@ -136,3 +136,38 @@ pub fn all_tuples(input: TokenStream) -> TokenStream {
)*
})
}
#[proc_macro]
pub fn all_tuples_with_size(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as AllTuples);
let len = 1 + input.end - input.start;
let mut ident_tuples = Vec::with_capacity(len);
for i in 0..=len {
let idents = input
.idents
.iter()
.map(|ident| format_ident!("{}{}", ident, i));
if input.idents.len() < 2 {
ident_tuples.push(quote! {
#(#idents)*
});
} else {
ident_tuples.push(quote! {
(#(#idents),*)
});
}
}
let macro_ident = &input.macro_ident;
let invocations = (input.start..=input.end).map(|i| {
let ident_tuples = &ident_tuples[..i];
quote! {
#macro_ident!(#i, #(#ident_tuples),*);
}
});
TokenStream::from(quote! {
#(
#invocations
)*
})
}

View file

@ -107,14 +107,11 @@ fn prepare_bind_group(
render_device: Res<RenderDevice>,
) {
let view = gpu_images.get(&game_of_life_image.0).unwrap();
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &pipeline.texture_bind_group_layout,
entries: &[BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(&view.texture_view),
}],
});
let bind_group = render_device.create_bind_group(
None,
&pipeline.texture_bind_group_layout,
&BindGroupEntries::single(&view.texture_view),
);
commands.insert_resource(GameOfLifeImageBindGroup(bind_group));
}

View file

@ -20,12 +20,12 @@ use bevy::{
NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner,
},
render_resource::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
ColorTargetState, ColorWrites, FragmentState, MultisampleState, Operations,
PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor,
RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages,
ShaderType, TextureFormat, TextureSampleType, TextureViewDimension,
BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
BindingType, CachedRenderPipelineId, ColorTargetState, ColorWrites, FragmentState,
MultisampleState, Operations, PipelineCache, PrimitiveState, RenderPassColorAttachment,
RenderPassDescriptor, RenderPipelineDescriptor, Sampler, SamplerBindingType,
SamplerDescriptor, ShaderStages, ShaderType, TextureFormat, TextureSampleType,
TextureViewDimension,
},
renderer::{RenderContext, RenderDevice},
texture::BevyDefault,
@ -176,30 +176,19 @@ impl ViewNode for PostProcessNode {
// The reason it doesn't work is because each post_process_write will alternate the source/destination.
// The only way to have the correct source/destination for the bind_group
// is to make sure you get it during the node execution.
let bind_group = render_context
.render_device()
.create_bind_group(&BindGroupDescriptor {
label: Some("post_process_bind_group"),
layout: &post_process_pipeline.layout,
// It's important for this to match the BindGroupLayout defined in the PostProcessPipeline
entries: &[
BindGroupEntry {
binding: 0,
// Make sure to use the source view
resource: BindingResource::TextureView(post_process.source),
},
BindGroupEntry {
binding: 1,
// Use the sampler created for the pipeline
resource: BindingResource::Sampler(&post_process_pipeline.sampler),
},
BindGroupEntry {
binding: 2,
// Set the settings binding
resource: settings_binding.clone(),
},
],
});
let bind_group = render_context.render_device().create_bind_group(
"post_process_bind_group",
&post_process_pipeline.layout,
// It's important for this to match the BindGroupLayout defined in the PostProcessPipeline
&BindGroupEntries::sequential((
// Make sure to use the source view
post_process.source,
// Use the sampler created for the pipeline
&post_process_pipeline.sampler,
// Set the settings binding
settings_binding.clone(),
)),
);
// Begin the render pass
let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {

View file

@ -5,11 +5,8 @@ use bevy::{
prelude::*,
reflect::TypePath,
render::{
render_asset::RenderAssets,
render_resource::{AsBindGroupError, PreparedBindGroup, *},
renderer::RenderDevice,
texture::FallbackImage,
RenderApp,
render_asset::RenderAssets, render_resource::*, renderer::RenderDevice,
texture::FallbackImage, RenderApp,
},
};
use std::{num::NonZeroU32, process::exit};
@ -119,20 +116,11 @@ impl AsBindGroup for BindlessMaterial {
textures[id] = &*image.texture_view;
}
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
label: "bindless_material_bind_group".into(),
let bind_group = render_device.create_bind_group(
"bindless_material_bind_group",
layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureViewArray(&textures[..]),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::Sampler(&fallback_image.sampler),
},
],
});
&BindGroupEntries::sequential((&textures[..], &fallback_image.sampler)),
);
Ok(PreparedBindGroup {
bindings: vec![],