Bind group layout entries (#10224)

# Objective

- Follow up to #9694

## Solution

- Same api as #9694 but adapted for `BindGroupLayoutEntry`
- Use the same `ShaderStages` visibilty for all entries by default
- Add `BindingType` helper function that mirror the wgsl equivalent and
that make writing layouts much simpler.

Before:
```rust
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
            binding: 0,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Texture {
                sample_type: TextureSampleType::Float { filterable: true },
                view_dimension: TextureViewDimension::D2,
                multisampled: false,
            },
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 1,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Sampler(SamplerBindingType::Filtering),
            count: None,
        },
        BindGroupLayoutEntry {
            binding: 2,
            visibility: ShaderStages::FRAGMENT,
            ty: BindingType::Buffer {
                ty: bevy::render::render_resource::BufferBindingType::Uniform,
                has_dynamic_offset: false,
                min_binding_size: Some(PostProcessSettings::min_size()),
            },
            count: None,
        },
    ],
});
```
After:
```rust
let layout = render_device.create_bind_group_layout(
    "post_process_bind_group_layout"),
    &BindGroupLayoutEntries::sequential(
        ShaderStages::FRAGMENT,
        (
            texture_2d_f32(),
            sampler(SamplerBindingType::Filtering),
            uniform_buffer(false, Some(PostProcessSettings::min_size())),
        ),
    ),
);
```

Here's a more extreme example in bevy_solari:
86dab7f5da

---

## Changelog

- Added `BindGroupLayoutEntries` and all `BindingType` helper functions.

## Migration Guide

`RenderDevice::create_bind_group_layout()` doesn't take a
`BindGroupLayoutDescriptor` anymore. You need to provide the parameters
separately

```rust
// 0.12
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
    label: Some("post_process_bind_group_layout"),
    entries: &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
});

// 0.13
let layout = render_device.create_bind_group_layout(
	"post_process_bind_group_layout",
    &[
        BindGroupLayoutEntry {
			// ...
        },
    ],
);
```

## TODO

- [x] implement a `Dynamic` variant
- [x] update the `RenderDevice::create_bind_group_layout()` api to match
the one from `RenderDevice::creat_bind_group()`
- [x] docs
This commit is contained in:
IceSentry 2023-11-27 23:00:49 -05:00 committed by GitHub
parent f0a8994f55
commit 6d0c11a28f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1142 additions and 1106 deletions

View file

@ -1,7 +1,14 @@
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::prelude::*;
use bevy_render::{render_resource::*, renderer::RenderDevice, RenderApp};
use bevy_render::{
render_resource::{
binding_types::{sampler, texture_2d},
*,
},
renderer::RenderDevice,
RenderApp,
};
use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
@ -36,28 +43,16 @@ impl FromWorld for BlitPipeline {
fn from_world(render_world: &mut World) -> Self {
let render_device = render_world.resource::<RenderDevice>();
let texture_bind_group =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("blit_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
count: None,
},
],
});
let texture_bind_group = render_device.create_bind_group_layout(
"blit_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: false }),
sampler(SamplerBindingType::NonFiltering),
),
),
);
let sampler = render_device.create_sampler(&SamplerDescriptor::default());

View file

@ -6,7 +6,13 @@ use bevy_ecs::{
world::{FromWorld, World},
};
use bevy_math::Vec4;
use bevy_render::{render_resource::*, renderer::RenderDevice};
use bevy_render::{
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::RenderDevice,
};
#[derive(Component)]
pub struct BloomDownsamplingPipelineIds {
@ -41,44 +47,21 @@ impl FromWorld for BloomDownsamplingPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
// Input texture binding
let texture = BindGroupLayoutEntry {
binding: 0,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
visibility: ShaderStages::FRAGMENT,
count: None,
};
// Sampler binding
let sampler = BindGroupLayoutEntry {
binding: 1,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
visibility: ShaderStages::FRAGMENT,
count: None,
};
// Downsampling settings binding
let settings = BindGroupLayoutEntry {
binding: 2,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(BloomUniforms::min_size()),
},
visibility: ShaderStages::FRAGMENT,
count: None,
};
// Bind group layout
let bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("bloom_downsampling_bind_group_layout_with_settings"),
entries: &[texture, sampler, settings],
});
let bind_group_layout = render_device.create_bind_group_layout(
"bloom_downsampling_bind_group_layout_with_settings",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
// Input texture binding
texture_2d(TextureSampleType::Float { filterable: true }),
// Sampler binding
sampler(SamplerBindingType::Filtering),
// Downsampling settings binding
uniform_buffer::<BloomUniforms>(true),
),
),
);
// Sampler
let sampler = render_device.create_sampler(&SamplerDescriptor {

View file

@ -8,7 +8,14 @@ use bevy_ecs::{
system::{Commands, Query, Res, ResMut, Resource},
world::{FromWorld, World},
};
use bevy_render::{render_resource::*, renderer::RenderDevice, view::ViewTarget};
use bevy_render::{
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::RenderDevice,
view::ViewTarget,
};
#[derive(Component)]
pub struct UpsamplingPipelineIds {
@ -31,41 +38,20 @@ impl FromWorld for BloomUpsamplingPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("bloom_upsampling_bind_group_layout"),
entries: &[
let bind_group_layout = render_device.create_bind_group_layout(
"bloom_upsampling_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
// Input texture
BindGroupLayoutEntry {
binding: 0,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
visibility: ShaderStages::FRAGMENT,
count: None,
},
texture_2d(TextureSampleType::Float { filterable: true }),
// Sampler
BindGroupLayoutEntry {
binding: 1,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
visibility: ShaderStages::FRAGMENT,
count: None,
},
sampler(SamplerBindingType::Filtering),
// BloomUniforms
BindGroupLayoutEntry {
binding: 2,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(BloomUniforms::min_size()),
},
visibility: ShaderStages::FRAGMENT,
count: None,
},
],
});
uniform_buffer::<BloomUniforms>(true),
),
),
);
BloomUpsamplingPipeline { bind_group_layout }
}

View file

@ -11,7 +11,10 @@ use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
prelude::Camera,
render_graph::RenderGraphApp,
render_resource::*,
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::RenderDevice,
texture::BevyDefault,
view::{ExtractedView, ViewTarget},
@ -169,39 +172,18 @@ pub struct CASPipeline {
impl FromWorld for CASPipeline {
fn from_world(render_world: &mut World) -> Self {
let render_device = render_world.resource::<RenderDevice>();
let texture_bind_group =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("sharpening_texture_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
let texture_bind_group = render_device.create_bind_group_layout(
"sharpening_texture_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
// CAS Settings
BindGroupLayoutEntry {
binding: 2,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(CASUniform::min_size()),
},
visibility: ShaderStages::FRAGMENT,
count: None,
},
],
});
uniform_buffer::<CASUniform>(true),
),
),
);
let sampler = render_device.create_sampler(&SamplerDescriptor::default());

View file

@ -8,7 +8,7 @@ use bevy_ecs::prelude::*;
use bevy_math::UVec2;
use bevy_render::{
camera::ExtractedCamera,
render_resource::*,
render_resource::{binding_types::texture_2d, *},
renderer::RenderDevice,
texture::{CachedTexture, TextureCache},
view::ViewTarget,
@ -128,19 +128,13 @@ impl FromWorld for CopyDeferredLightingIdPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("copy_deferred_lighting_id_bind_group_layout"),
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Uint,
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
}],
});
let layout = render_device.create_bind_group_layout(
"copy_deferred_lighting_id_bind_group_layout",
&BindGroupLayoutEntries::single(
ShaderStages::FRAGMENT,
texture_2d(TextureSampleType::Uint),
),
);
let pipeline_id =
world

View file

@ -13,7 +13,10 @@ use bevy_render::{
prelude::Camera,
render_graph::RenderGraphApp,
render_graph::ViewNodeRunner,
render_resource::*,
render_resource::{
binding_types::{sampler, texture_2d},
*,
},
renderer::RenderDevice,
texture::BevyDefault,
view::{ExtractedView, ViewTarget},
@ -131,27 +134,16 @@ impl FromWorld for FxaaPipeline {
fn from_world(render_world: &mut World) -> Self {
let texture_bind_group = render_world
.resource::<RenderDevice>()
.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("fxaa_texture_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
});
.create_bind_group_layout(
"fxaa_texture_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
),
),
);
FxaaPipeline { texture_bind_group }
}

View file

@ -10,13 +10,13 @@ use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_resource::{
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,
binding_types::{sampler, texture_cube, uniform_buffer},
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries,
CachedRenderPipelineId, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
DepthStencilState, FragmentState, MultisampleState, PipelineCache, PrimitiveState,
RenderPipelineDescriptor, SamplerBindingType, Shader, ShaderStages,
SpecializedRenderPipeline, SpecializedRenderPipelines, StencilFaceState, StencilState,
TextureFormat, TextureSampleType, VertexState,
},
renderer::RenderDevice,
texture::{BevyDefault, Image},
@ -80,41 +80,19 @@ struct SkyboxPipeline {
impl SkyboxPipeline {
fn new(render_device: &RenderDevice) -> Self {
let bind_group_layout_descriptor = BindGroupLayoutDescriptor {
label: Some("skybox_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
],
};
Self {
bind_group_layout: render_device
.create_bind_group_layout(&bind_group_layout_descriptor),
bind_group_layout: render_device.create_bind_group_layout(
"skybox_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_cube(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
uniform_buffer::<ViewUniform>(true)
.visibility(ShaderStages::VERTEX_FRAGMENT),
),
),
),
}
}
}

View file

@ -21,13 +21,13 @@ use bevy_render::{
prelude::{Camera, Projection},
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::{
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,
binding_types::{sampler, texture_2d, texture_depth_2d},
BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries, 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,
},
renderer::{RenderContext, RenderDevice},
texture::{BevyDefault, CachedTexture, TextureCache},
@ -266,70 +266,26 @@ impl FromWorld for TaaPipeline {
..SamplerDescriptor::default()
});
let taa_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("taa_bind_group_layout"),
entries: &[
let taa_bind_group_layout = render_device.create_bind_group_layout(
"taa_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
// View target (read)
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
texture_2d(TextureSampleType::Float { filterable: true }),
// TAA History (read)
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
texture_2d(TextureSampleType::Float { filterable: true }),
// Motion Vectors
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
texture_2d(TextureSampleType::Float { filterable: true }),
// Depth
BindGroupLayoutEntry {
binding: 3,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Depth,
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
texture_depth_2d(),
// Nearest sampler
BindGroupLayoutEntry {
binding: 4,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
count: None,
},
sampler(SamplerBindingType::NonFiltering),
// Linear sampler
BindGroupLayoutEntry {
binding: 5,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
});
sampler(SamplerBindingType::Filtering),
),
),
);
TaaPipeline {
taa_bind_group_layout,

View file

@ -7,6 +7,9 @@ use bevy_render::camera::Camera;
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
use bevy_render::extract_resource::{ExtractResource, ExtractResourcePlugin};
use bevy_render::render_asset::RenderAssets;
use bevy_render::render_resource::binding_types::{
sampler, texture_2d, texture_3d, uniform_buffer,
};
use bevy_render::renderer::RenderDevice;
use bevy_render::texture::{CompressedImageFormats, Image, ImageSampler, ImageType};
use bevy_render::view::{ViewTarget, ViewUniform};
@ -248,42 +251,24 @@ impl SpecializedRenderPipeline for TonemappingPipeline {
impl FromWorld for TonemappingPipeline {
fn from_world(render_world: &mut World) -> Self {
let mut entries = vec![
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
count: None,
},
];
entries.extend(get_lut_bind_group_layout_entries([3, 4]));
let mut entries = DynamicBindGroupLayoutEntries::new_with_indices(
ShaderStages::FRAGMENT,
(
(0, uniform_buffer::<ViewUniform>(true)),
(
1,
texture_2d(TextureSampleType::Float { filterable: false }),
),
(2, sampler(SamplerBindingType::NonFiltering)),
),
);
let lut_layout_entries = get_lut_bind_group_layout_entries();
entries =
entries.extend_with_indices(((3, lut_layout_entries[0]), (4, lut_layout_entries[1])));
let tonemap_texture_bind_group = render_world
.resource::<RenderDevice>()
.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("tonemapping_hdr_texture_bind_group_layout"),
entries: &entries,
});
.create_bind_group_layout("tonemapping_hdr_texture_bind_group_layout", &entries);
TonemappingPipeline {
texture_bind_group: tonemap_texture_bind_group,
@ -345,24 +330,10 @@ pub fn get_lut_bindings<'a>(
(&lut_image.texture_view, &lut_image.sampler)
}
pub fn get_lut_bind_group_layout_entries(bindings: [u32; 2]) -> [BindGroupLayoutEntry; 2] {
pub fn get_lut_bind_group_layout_entries() -> [BindGroupLayoutEntryBuilder; 2] {
[
BindGroupLayoutEntry {
binding: bindings[0],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D3,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: bindings[1],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
texture_3d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
]
}

View file

@ -54,10 +54,9 @@ use bevy_render::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{
BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor,
BufferUsages, Shader, ShaderStages, ShaderType, VertexAttribute, VertexBufferLayout,
VertexFormat, VertexStepMode,
binding_types::uniform_buffer, BindGroup, BindGroupEntries, BindGroupLayout,
BindGroupLayoutEntries, Buffer, BufferInitDescriptor, BufferUsages, Shader, ShaderStages,
ShaderType, VertexAttribute, VertexBufferLayout, VertexFormat, VertexStepMode,
},
renderer::RenderDevice,
view::RenderLayers,
@ -120,19 +119,13 @@ impl Plugin for GizmoPlugin {
};
let render_device = render_app.world.resource::<RenderDevice>();
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(LineGizmoUniform::min_size()),
},
count: None,
}],
label: Some("LineGizmoUniform layout"),
});
let layout = render_device.create_bind_group_layout(
"LineGizmoUniform layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX,
uniform_buffer::<LineGizmoUniform>(true),
),
);
render_app.insert_resource(LineGizmoUniformBindgroupLayout { layout });
}

View file

@ -18,7 +18,9 @@ use bevy_render::{
},
render_asset::RenderAssets,
render_graph::{NodeRunError, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::{self, Operations, PipelineCache, RenderPassDescriptor},
render_resource::{
binding_types::uniform_buffer, Operations, PipelineCache, RenderPassDescriptor,
},
renderer::{RenderContext, RenderDevice},
texture::Image,
view::{ViewTarget, ViewUniformOffset},
@ -376,19 +378,13 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
impl FromWorld for DeferredLightingLayout {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("deferred_lighting_layout"),
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: render_resource::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(PbrDeferredLightingDepthId::min_size()),
},
count: None,
}],
});
let layout = render_device.create_bind_group_layout(
"deferred_lighting_layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
uniform_buffer::<PbrDeferredLightingDepthId>(false),
),
);
Self {
mesh_pipeline: world.resource::<MeshPipeline>().clone(),
bind_group_layout_1: layout,

View file

@ -6,7 +6,10 @@ use bevy_reflect::Reflect;
use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_resource::*,
render_resource::{
binding_types::{sampler, texture_cube},
*,
},
texture::{FallbackImageCubemap, Image},
};
@ -79,33 +82,10 @@ pub fn get_bindings<'a>(
(diffuse_map, specular_map, &fallback_image_cubemap.sampler)
}
pub fn get_bind_group_layout_entries(bindings: [u32; 3]) -> [BindGroupLayoutEntry; 3] {
pub fn get_bind_group_layout_entries() -> [BindGroupLayoutEntryBuilder; 3] {
[
BindGroupLayoutEntry {
binding: bindings[0],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: bindings[1],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: bindings[2],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
texture_cube(TextureSampleType::Float { filterable: true }),
texture_cube(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
]
}

View file

@ -1,5 +1,6 @@
mod prepass_bindings;
use bevy_render::render_resource::binding_types::uniform_buffer;
pub use prepass_bindings::*;
use bevy_app::{Plugin, PreUpdate};
@ -229,74 +230,33 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
let render_device = world.resource::<RenderDevice>();
let asset_server = world.resource::<AssetServer>();
let view_layout_motion_vectors =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
let view_layout_motion_vectors = render_device.create_bind_group_layout(
"prepass_view_layout_motion_vectors",
&BindGroupLayoutEntries::sequential(
ShaderStages::VERTEX_FRAGMENT,
(
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
uniform_buffer::<ViewUniform>(true),
// Globals
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
uniform_buffer::<GlobalsUniform>(false),
// PreviousViewProjection
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(PreviousViewProjection::min_size()),
},
count: None,
},
],
label: Some("prepass_view_layout_motion_vectors"),
});
uniform_buffer::<PreviousViewProjection>(true),
),
),
);
let view_layout_no_motion_vectors =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
let view_layout_no_motion_vectors = render_device.create_bind_group_layout(
"prepass_view_layout_no_motion_vectors",
&BindGroupLayoutEntries::sequential(
ShaderStages::VERTEX_FRAGMENT,
(
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
uniform_buffer::<ViewUniform>(true),
// Globals
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
],
label: Some("prepass_view_layout_no_motion_vectors"),
});
uniform_buffer::<GlobalsUniform>(false),
),
),
);
let mesh_pipeline = world.resource::<MeshPipeline>();

View file

@ -1,7 +1,10 @@
use bevy_core_pipeline::prepass::ViewPrepassTextures;
use bevy_render::render_resource::{
BindGroupLayoutEntry, BindingType, ShaderStages, TextureAspect, TextureSampleType, TextureView,
TextureViewDescriptor, TextureViewDimension,
binding_types::{
texture_2d, texture_2d_multisampled, texture_depth_2d, texture_depth_2d_multisampled,
},
BindGroupLayoutEntryBuilder, TextureAspect, TextureSampleType, TextureView,
TextureViewDescriptor,
};
use bevy_utils::default;
use smallvec::SmallVec;
@ -9,25 +12,19 @@ use smallvec::SmallVec;
use crate::MeshPipelineViewLayoutKey;
pub fn get_bind_group_layout_entries(
bindings: [u32; 4],
layout_key: MeshPipelineViewLayoutKey,
) -> SmallVec<[BindGroupLayoutEntry; 4]> {
let mut result = SmallVec::<[BindGroupLayoutEntry; 4]>::new();
) -> SmallVec<[BindGroupLayoutEntryBuilder; 4]> {
let mut result = SmallVec::<[BindGroupLayoutEntryBuilder; 4]>::new();
let multisampled = layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED);
if layout_key.contains(MeshPipelineViewLayoutKey::DEPTH_PREPASS) {
result.push(
// Depth texture
BindGroupLayoutEntry {
binding: bindings[0],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled,
sample_type: TextureSampleType::Depth,
view_dimension: TextureViewDimension::D2,
},
count: None,
if multisampled {
texture_depth_2d_multisampled()
} else {
texture_depth_2d()
},
);
}
@ -35,15 +32,10 @@ pub fn get_bind_group_layout_entries(
if layout_key.contains(MeshPipelineViewLayoutKey::NORMAL_PREPASS) {
result.push(
// Normal texture
BindGroupLayoutEntry {
binding: bindings[1],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled,
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
},
count: None,
if multisampled {
texture_2d_multisampled(TextureSampleType::Float { filterable: false })
} else {
texture_2d(TextureSampleType::Float { filterable: false })
},
);
}
@ -51,15 +43,10 @@ pub fn get_bind_group_layout_entries(
if layout_key.contains(MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS) {
result.push(
// Motion Vectors texture
BindGroupLayoutEntry {
binding: bindings[2],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled,
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
},
count: None,
if multisampled {
texture_2d_multisampled(TextureSampleType::Float { filterable: false })
} else {
texture_2d(TextureSampleType::Float { filterable: false })
},
);
}
@ -67,16 +54,7 @@ pub fn get_bind_group_layout_entries(
if layout_key.contains(MeshPipelineViewLayoutKey::DEFERRED_PREPASS) {
result.push(
// Deferred texture
BindGroupLayoutEntry {
binding: bindings[3],
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Uint,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
texture_2d(TextureSampleType::Uint),
);
}

View file

@ -17,50 +17,28 @@ mod layout_entry {
use crate::MeshUniform;
use bevy_render::{
render_resource::{
BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, GpuArrayBuffer,
ShaderStages, TextureSampleType, TextureViewDimension,
binding_types::{texture_3d, uniform_buffer_sized},
BindGroupLayoutEntryBuilder, BufferSize, GpuArrayBuffer, ShaderStages,
TextureSampleType,
},
renderer::RenderDevice,
};
fn buffer(binding: u32, size: u64, visibility: ShaderStages) -> BindGroupLayoutEntry {
BindGroupLayoutEntry {
binding,
visibility,
count: None,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: BufferSize::new(size),
},
}
pub(super) fn model(render_device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
GpuArrayBuffer::<MeshUniform>::binding_layout(render_device)
.visibility(ShaderStages::VERTEX_FRAGMENT)
}
pub(super) fn model(render_device: &RenderDevice, binding: u32) -> BindGroupLayoutEntry {
GpuArrayBuffer::<MeshUniform>::binding_layout(
binding,
ShaderStages::VERTEX_FRAGMENT,
render_device,
)
pub(super) fn skinning() -> BindGroupLayoutEntryBuilder {
uniform_buffer_sized(true, BufferSize::new(JOINT_BUFFER_SIZE as u64))
}
pub(super) fn skinning(binding: u32) -> BindGroupLayoutEntry {
buffer(binding, JOINT_BUFFER_SIZE as u64, ShaderStages::VERTEX)
pub(super) fn weights() -> BindGroupLayoutEntryBuilder {
uniform_buffer_sized(true, BufferSize::new(MORPH_BUFFER_SIZE as u64))
}
pub(super) fn weights(binding: u32) -> BindGroupLayoutEntry {
buffer(binding, MORPH_BUFFER_SIZE as u64, ShaderStages::VERTEX)
}
pub(super) fn targets(binding: u32) -> BindGroupLayoutEntry {
BindGroupLayoutEntry {
binding,
visibility: ShaderStages::VERTEX,
ty: BindingType::Texture {
view_dimension: TextureViewDimension::D3,
sample_type: TextureSampleType::Float { filterable: false },
multisampled: false,
},
count: None,
}
pub(super) fn targets() -> BindGroupLayoutEntryBuilder {
texture_3d(TextureSampleType::Float { filterable: false })
}
}
/// Individual [`BindGroupEntry`]
/// for bind groups.
mod entry {
@ -133,40 +111,52 @@ impl MeshLayouts {
// ---------- create individual BindGroupLayouts ----------
fn model_only_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[layout_entry::model(render_device, 0)],
label: Some("mesh_layout"),
})
render_device.create_bind_group_layout(
"mesh_layout",
&BindGroupLayoutEntries::single(
ShaderStages::empty(),
layout_entry::model(render_device),
),
)
}
fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
layout_entry::model(render_device, 0),
layout_entry::skinning(1),
],
label: Some("skinned_mesh_layout"),
})
render_device.create_bind_group_layout(
"skinned_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(1, layout_entry::skinning()),
),
),
)
}
fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
layout_entry::model(render_device, 0),
layout_entry::weights(2),
layout_entry::targets(3),
],
label: Some("morphed_mesh_layout"),
})
render_device.create_bind_group_layout(
"morphed_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(2, layout_entry::weights()),
(3, layout_entry::targets()),
),
),
)
}
fn morphed_skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
layout_entry::model(render_device, 0),
layout_entry::skinning(1),
layout_entry::weights(2),
layout_entry::targets(3),
],
label: Some("morphed_skinned_mesh_layout"),
})
render_device.create_bind_group_layout(
"morphed_skinned_mesh_layout",
&BindGroupLayoutEntries::with_indices(
ShaderStages::VERTEX,
(
(0, layout_entry::model(render_device)),
(1, layout_entry::skinning()),
(2, layout_entry::weights()),
(3, layout_entry::targets()),
),
),
)
}
// ---------- BindGroup methods ----------

View file

@ -1,4 +1,4 @@
use std::array;
use std::{array, num::NonZeroU64};
use bevy_core_pipeline::{
core_3d::ViewTransmissionTexture,
@ -16,15 +16,24 @@ use bevy_render::{
globals::{GlobalsBuffer, GlobalsUniform},
render_asset::RenderAssets,
render_resource::{
BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,
BufferBindingType, DynamicBindGroupEntries, SamplerBindingType, ShaderStages, ShaderType,
TextureFormat, TextureSampleType, TextureViewDimension,
binding_types::{
sampler, storage_buffer_read_only_sized, storage_buffer_sized, texture_2d,
uniform_buffer, uniform_buffer_sized,
},
BindGroup, BindGroupLayout, BindGroupLayoutEntry, BindGroupLayoutEntryBuilder, BindingType,
BufferBindingType, DynamicBindGroupEntries, DynamicBindGroupLayoutEntries,
SamplerBindingType, ShaderStages, TextureFormat, TextureSampleType,
},
renderer::RenderDevice,
texture::{BevyDefault, FallbackImageCubemap, FallbackImageMsaa, FallbackImageZero, Image},
view::{Msaa, ViewUniform, ViewUniforms},
};
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
use bevy_render::render_resource::binding_types::texture_cube;
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]
use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array};
use crate::{
environment_map, prepass, EnvironmentMapLight, FogMeta, GlobalLightMeta, GpuFog, GpuLights,
GpuPointLights, LightMeta, MeshPipeline, MeshPipelineKey, ScreenSpaceAmbientOcclusionTextures,
@ -144,190 +153,141 @@ impl From<Option<&ViewPrepassTextures>> for MeshPipelineViewLayoutKey {
}
}
fn buffer_layout(
buffer_binding_type: BufferBindingType,
has_dynamic_offset: bool,
min_binding_size: Option<NonZeroU64>,
) -> BindGroupLayoutEntryBuilder {
match buffer_binding_type {
BufferBindingType::Uniform => uniform_buffer_sized(has_dynamic_offset, min_binding_size),
BufferBindingType::Storage { read_only } => {
if read_only {
storage_buffer_read_only_sized(has_dynamic_offset, min_binding_size)
} else {
storage_buffer_sized(has_dynamic_offset, min_binding_size)
}
}
}
}
/// Returns the appropriate bind group layout vec based on the parameters
fn layout_entries(
clustered_forward_buffer_binding_type: BufferBindingType,
layout_key: MeshPipelineViewLayoutKey,
) -> Vec<BindGroupLayoutEntry> {
let mut entries = vec![
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
// Lights
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(GpuLights::min_size()),
},
count: None,
},
// Point Shadow Texture Cube Array
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
let mut entries = DynamicBindGroupLayoutEntries::new_with_indices(
ShaderStages::FRAGMENT,
(
// View
(
0,
uniform_buffer::<ViewUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
),
// Lights
(1, uniform_buffer::<GpuLights>(true)),
// Point Shadow Texture Cube Array
(
2,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]
view_dimension: TextureViewDimension::CubeArray,
texture_cube_array(TextureSampleType::Depth),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
view_dimension: TextureViewDimension::Cube,
},
count: None,
},
// Point Shadow Texture Array Sampler
BindGroupLayoutEntry {
binding: 3,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Comparison),
count: None,
},
// Directional Shadow Texture Array
BindGroupLayoutEntry {
binding: 4,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
texture_cube(TextureSampleType::Depth),
),
// Point Shadow Texture Array Sampler
(3, sampler(SamplerBindingType::Comparison)),
// Directional Shadow Texture Array
(
4,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]
view_dimension: TextureViewDimension::D2Array,
texture_2d_array(TextureSampleType::Depth),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
view_dimension: TextureViewDimension::D2,
},
count: None,
},
// Directional Shadow Texture Array Sampler
BindGroupLayoutEntry {
binding: 5,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Comparison),
count: None,
},
// PointLights
BindGroupLayoutEntry {
binding: 6,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: clustered_forward_buffer_binding_type,
has_dynamic_offset: false,
min_binding_size: Some(GpuPointLights::min_size(
texture_2d(TextureSampleType::Depth),
),
// Directional Shadow Texture Array Sampler
(5, sampler(SamplerBindingType::Comparison)),
// PointLights
(
6,
buffer_layout(
clustered_forward_buffer_binding_type,
)),
},
count: None,
},
// ClusteredLightIndexLists
BindGroupLayoutEntry {
binding: 7,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: clustered_forward_buffer_binding_type,
has_dynamic_offset: false,
min_binding_size: Some(ViewClusterBindings::min_size_cluster_light_index_lists(
false,
Some(GpuPointLights::min_size(
clustered_forward_buffer_binding_type,
)),
),
),
// ClusteredLightIndexLists
(
7,
buffer_layout(
clustered_forward_buffer_binding_type,
)),
},
count: None,
},
// ClusterOffsetsAndCounts
BindGroupLayoutEntry {
binding: 8,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: clustered_forward_buffer_binding_type,
has_dynamic_offset: false,
min_binding_size: Some(ViewClusterBindings::min_size_cluster_offsets_and_counts(
false,
Some(ViewClusterBindings::min_size_cluster_light_index_lists(
clustered_forward_buffer_binding_type,
)),
),
),
// ClusterOffsetsAndCounts
(
8,
buffer_layout(
clustered_forward_buffer_binding_type,
)),
},
count: None,
},
// Globals
BindGroupLayoutEntry {
binding: 9,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
// Fog
BindGroupLayoutEntry {
binding: 10,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(GpuFog::min_size()),
},
count: None,
},
// Screen space ambient occlusion texture
BindGroupLayoutEntry {
binding: 11,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
},
count: None,
},
];
false,
Some(ViewClusterBindings::min_size_cluster_offsets_and_counts(
clustered_forward_buffer_binding_type,
)),
),
),
// Globals
(9, uniform_buffer::<GlobalsUniform>(false)),
// Fog
(10, uniform_buffer::<GpuFog>(true)),
// Screen space ambient occlusion texture
(
11,
texture_2d(TextureSampleType::Float { filterable: false }),
),
),
);
// EnvironmentMapLight
let environment_map_entries = environment_map::get_bind_group_layout_entries([12, 13, 14]);
entries.extend_from_slice(&environment_map_entries);
let environment_map_entries = environment_map::get_bind_group_layout_entries();
entries = entries.extend_with_indices((
(12, environment_map_entries[0]),
(13, environment_map_entries[1]),
(14, environment_map_entries[2]),
));
// Tonemapping
let tonemapping_lut_entries = get_lut_bind_group_layout_entries([15, 16]);
entries.extend_from_slice(&tonemapping_lut_entries);
let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
entries = entries.extend_with_indices((
(15, tonemapping_lut_entries[0]),
(16, tonemapping_lut_entries[1]),
));
// Prepass
if cfg!(any(not(feature = "webgl"), not(target_arch = "wasm32")))
|| (cfg!(all(feature = "webgl", target_arch = "wasm32"))
&& !layout_key.contains(MeshPipelineViewLayoutKey::MULTISAMPLED))
{
entries.extend_from_slice(&prepass::get_bind_group_layout_entries(
[17, 18, 19, 20],
layout_key,
));
for (entry, binding) in prepass::get_bind_group_layout_entries(layout_key)
.iter()
.zip([17, 18, 19, 20])
{
entries = entries.extend_with_indices(((binding as u32, *entry),));
}
}
// View Transmission Texture
entries.extend_from_slice(&[
BindGroupLayoutEntry {
binding: 21,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
multisampled: false,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 22,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
]);
entries = entries.extend_with_indices((
(
21,
texture_2d(TextureSampleType::Float { filterable: true }),
),
(22, sampler(SamplerBindingType::Filtering)),
));
entries
entries.to_vec()
}
/// Generates all possible view layouts for the mesh pipeline, based on all combinations of
@ -347,10 +307,8 @@ pub fn generate_view_layouts(
.count();
MeshPipelineViewLayout {
bind_group_layout: render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some(key.label().as_str()),
entries: &entries,
}),
bind_group_layout: render_device
.create_bind_group_layout(key.label().as_str(), &entries),
#[cfg(debug_assertions)]
texture_count,
}

View file

@ -20,7 +20,12 @@ use bevy_render::{
globals::{GlobalsBuffer, GlobalsUniform},
prelude::Camera,
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::*,
render_resource::{
binding_types::{
sampler, texture_2d, texture_depth_2d, texture_storage_2d, uniform_buffer,
},
*,
},
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
texture::{CachedTexture, TextureCache},
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms},
@ -345,176 +350,58 @@ impl FromWorld for SsaoPipelines {
..Default::default()
});
let common_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("ssao_common_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
],
});
let common_bind_group_layout = render_device.create_bind_group_layout(
"ssao_common_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
sampler(SamplerBindingType::NonFiltering),
uniform_buffer::<ViewUniform>(true),
),
),
);
let mip_texture_entry = BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::WriteOnly,
format: TextureFormat::R16Float,
view_dimension: TextureViewDimension::D2,
},
count: None,
};
let preprocess_depth_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("ssao_preprocess_depth_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Depth,
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
mip_texture_entry,
BindGroupLayoutEntry {
binding: 2,
..mip_texture_entry
},
BindGroupLayoutEntry {
binding: 3,
..mip_texture_entry
},
BindGroupLayoutEntry {
binding: 4,
..mip_texture_entry
},
BindGroupLayoutEntry {
binding: 5,
..mip_texture_entry
},
],
});
let preprocess_depth_bind_group_layout = render_device.create_bind_group_layout(
"ssao_preprocess_depth_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
texture_depth_2d(),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
),
),
);
let gtao_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("ssao_gtao_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Uint,
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 3,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::WriteOnly,
format: TextureFormat::R16Float,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 4,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::WriteOnly,
format: TextureFormat::R32Uint,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 5,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
],
});
let gtao_bind_group_layout = render_device.create_bind_group_layout(
"ssao_gtao_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
texture_2d(TextureSampleType::Float { filterable: false }),
texture_2d(TextureSampleType::Float { filterable: false }),
texture_2d(TextureSampleType::Uint),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
texture_storage_2d(TextureFormat::R32Uint, StorageTextureAccess::WriteOnly),
uniform_buffer::<GlobalsUniform>(false),
),
),
);
let spatial_denoise_bind_group_layout =
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("ssao_spatial_denoise_bind_group_layout"),
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::COMPUTE,
ty: BindingType::Texture {
sample_type: TextureSampleType::Uint,
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::WriteOnly,
format: TextureFormat::R16Float,
view_dimension: TextureViewDimension::D2,
},
count: None,
},
],
});
let spatial_denoise_bind_group_layout = render_device.create_bind_group_layout(
"ssao_spatial_denoise_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
texture_2d(TextureSampleType::Float { filterable: false }),
texture_2d(TextureSampleType::Uint),
texture_storage_2d(TextureFormat::R16Float, StorageTextureAccess::WriteOnly),
),
),
);
let preprocess_depth_pipeline =
pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {

View file

@ -10,7 +10,7 @@ pub use bevy_render_macros::AsBindGroup;
use bevy_utils::thiserror::Error;
use encase::ShaderType;
use std::ops::Deref;
use wgpu::{BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource};
use wgpu::{BindGroupEntry, BindGroupLayoutEntry, BindingResource};
define_atomic_id!(BindGroupId);
render_resource_wrapper!(ErasedBindGroup, wgpu::BindGroup);
@ -313,10 +313,10 @@ pub trait AsBindGroup {
where
Self: Sized,
{
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Self::label(),
entries: &Self::bind_group_layout_entries(render_device),
})
render_device.create_bind_group_layout(
Self::label(),
&Self::bind_group_layout_entries(render_device),
)
}
/// Returns a vec of bind group layout entries

View file

@ -0,0 +1,546 @@
use bevy_utils::all_tuples_with_size;
use std::num::NonZeroU32;
use wgpu::{BindGroupLayoutEntry, BindingType, ShaderStages};
/// Helper for constructing bind group layouts.
///
/// Allows constructing the layout's entries as:
/// ```ignore
/// let layout = render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &BindGroupLayoutEntries::with_indices(
/// // The layout entries will only be visible in the fragment stage
/// ShaderStages::FRAGMENT,
/// (
/// // Screen texture
/// (2, tepxture_2d(TextureSampleType::Float { filterable: true })),
/// // Sampler
/// (3, sampler(SamplerBindingType::Filtering)),
/// ),
/// ),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// let layout = render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &[
/// // Screen texture
/// BindGroupLayoutEntry {
/// binding: 2,
/// visibility: ShaderStages::FRAGMENT,
/// ty: BindingType::Texture {
/// sample_type: TextureSampleType::Float { filterable: true },
/// view_dimension: TextureViewDimension::D2,
/// multisampled: false,
/// },
/// count: None,
/// },
/// // Sampler
/// BindGroupLayoutEntry {
/// binding: 3,
/// visibility: ShaderStages::FRAGMENT,
/// ty: BindingType::Sampler(SamplerBindingType::Filtering),
/// count: None,
/// },
/// ],
/// );
/// ```
///
/// or
///
/// ```ignore
/// render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &BindGroupLayoutEntries::sequential(
/// ShaderStages::FRAGMENT,
/// (
/// // Screen texture
/// texture_2d(TextureSampleType::Float { filterable: true }),
/// // Sampler
/// sampler(SamplerBindingType::Filtering),
/// ),
/// ),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// let layout = render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &[
/// // Screen texture
/// BindGroupLayoutEntry {
/// binding: 0,
/// visibility: ShaderStages::FRAGMENT,
/// ty: BindingType::Texture {
/// sample_type: TextureSampleType::Float { filterable: true },
/// view_dimension: TextureViewDimension::D2,
/// multisampled: false,
/// },
/// count: None,
/// },
/// // Sampler
/// BindGroupLayoutEntry {
/// binding: 1,
/// visibility: ShaderStages::FRAGMENT,
/// ty: BindingType::Sampler(SamplerBindingType::Filtering),
/// count: None,
/// },
/// ],
/// );
/// ```
///
/// or
///
/// ```ignore
/// render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &BindGroupLayoutEntries::single(
/// ShaderStages::FRAGMENT,
/// texture_2d(TextureSampleType::Float { filterable: true }),
/// ),
/// );
/// ```
///
/// instead of
///
/// ```ignore
/// let layout = render_device.create_bind_group_layout(
/// "my_bind_group_layout",
/// &[
/// BindGroupLayoutEntry {
/// binding: 0,
/// visibility: ShaderStages::FRAGMENT,
/// ty: BindingType::Texture {
/// sample_type: TextureSampleType::Float { filterable: true },
/// view_dimension: TextureViewDimension::D2,
/// multisampled: false,
/// },
/// count: None,
/// },
/// ],
/// );
/// ```
#[derive(Clone, Copy)]
pub struct BindGroupLayoutEntryBuilder {
ty: BindingType,
visibility: Option<ShaderStages>,
count: Option<NonZeroU32>,
}
impl BindGroupLayoutEntryBuilder {
pub fn visibility(mut self, visibility: ShaderStages) -> Self {
self.visibility = Some(visibility);
self
}
pub fn count(mut self, count: NonZeroU32) -> Self {
self.count = Some(count);
self
}
pub fn build(
&self,
binding: u32,
default_visibility: ShaderStages,
) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
ty: self.ty,
visibility: self.visibility.unwrap_or(default_visibility),
count: self.count,
}
}
}
pub struct BindGroupLayoutEntries<const N: usize> {
entries: [wgpu::BindGroupLayoutEntry; N],
}
impl<const N: usize> BindGroupLayoutEntries<N> {
#[inline]
pub fn sequential(
default_visibility: ShaderStages,
entries_ext: impl IntoBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
let mut i = 0;
Self {
entries: entries_ext.into_array().map(|entry| {
let binding = i;
i += 1;
entry.build(binding, default_visibility)
}),
}
}
#[inline]
pub fn with_indices(
default_visibility: ShaderStages,
indexed_entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
Self {
entries: indexed_entries
.into_array()
.map(|(binding, entry)| entry.build(binding, default_visibility)),
}
}
}
impl BindGroupLayoutEntries<1> {
pub fn single(
visibility: ShaderStages,
resource: impl IntoBindGroupLayoutEntryBuilder,
) -> [BindGroupLayoutEntry; 1] {
[resource
.into_bind_group_layout_entry_builder()
.build(0, visibility)]
}
}
impl<const N: usize> std::ops::Deref for BindGroupLayoutEntries<N> {
type Target = [wgpu::BindGroupLayoutEntry];
fn deref(&self) -> &[wgpu::BindGroupLayoutEntry] {
&self.entries
}
}
pub trait IntoBindGroupLayoutEntryBuilder {
fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder;
}
impl IntoBindGroupLayoutEntryBuilder for BindingType {
fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
BindGroupLayoutEntryBuilder {
ty: self,
visibility: None,
count: None,
}
}
}
impl IntoBindGroupLayoutEntryBuilder for wgpu::BindGroupLayoutEntry {
fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
if self.binding != u32::MAX {
bevy_log::warn!("The BindGroupLayoutEntries api ignores the binding index when converting a raw wgpu::BindGroupLayoutEntry. You can ignore this warning by setting it to u32::MAX.");
}
BindGroupLayoutEntryBuilder {
ty: self.ty,
visibility: Some(self.visibility),
count: self.count,
}
}
}
impl IntoBindGroupLayoutEntryBuilder for BindGroupLayoutEntryBuilder {
fn into_bind_group_layout_entry_builder(self) -> BindGroupLayoutEntryBuilder {
self
}
}
pub trait IntoBindGroupLayoutEntryBuilderArray<const N: usize> {
fn into_array(self) -> [BindGroupLayoutEntryBuilder; N];
}
macro_rules! impl_to_binding_type_slice {
($N: expr, $(($T: ident, $I: ident)),*) => {
impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoBindGroupLayoutEntryBuilderArray<$N> for ($($T,)*) {
#[inline]
fn into_array(self) -> [BindGroupLayoutEntryBuilder; $N] {
let ($($I,)*) = self;
[$($I.into_bind_group_layout_entry_builder(), )*]
}
}
}
}
all_tuples_with_size!(impl_to_binding_type_slice, 1, 32, T, s);
pub trait IntoIndexedBindGroupLayoutEntryBuilderArray<const N: usize> {
fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); N];
}
macro_rules! impl_to_indexed_binding_type_slice {
($N: expr, $(($T: ident, $S: ident, $I: ident)),*) => {
impl<$($T: IntoBindGroupLayoutEntryBuilder),*> IntoIndexedBindGroupLayoutEntryBuilderArray<$N> for ($((u32, $T),)*) {
#[inline]
fn into_array(self) -> [(u32, BindGroupLayoutEntryBuilder); $N] {
let ($(($S, $I),)*) = self;
[$(($S, $I.into_bind_group_layout_entry_builder())), *]
}
}
}
}
all_tuples_with_size!(impl_to_indexed_binding_type_slice, 1, 32, T, n, s);
impl<const N: usize> IntoBindGroupLayoutEntryBuilderArray<N> for [BindGroupLayoutEntry; N] {
fn into_array(self) -> [BindGroupLayoutEntryBuilder; N] {
self.map(|x| x.into_bind_group_layout_entry_builder())
}
}
pub struct DynamicBindGroupLayoutEntries {
default_visibility: ShaderStages,
entries: Vec<BindGroupLayoutEntry>,
}
impl DynamicBindGroupLayoutEntries {
pub fn sequential<const N: usize>(
default_visibility: ShaderStages,
entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
Self {
default_visibility,
entries: entries
.into_array()
.into_iter()
.enumerate()
.map(|(ix, resource)| resource.build(ix as u32, default_visibility))
.collect(),
}
}
pub fn extend_sequential<const N: usize>(
mut self,
entries: impl IntoBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
let start = self.entries.last().unwrap().binding + 1;
self.entries.extend(
entries
.into_array()
.into_iter()
.enumerate()
.map(|(ix, resource)| resource.build(start + ix as u32, self.default_visibility)),
);
self
}
pub fn new_with_indices<const N: usize>(
default_visibility: ShaderStages,
entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
Self {
default_visibility,
entries: entries
.into_array()
.into_iter()
.map(|(binding, resource)| resource.build(binding, default_visibility))
.collect(),
}
}
pub fn extend_with_indices<const N: usize>(
mut self,
entries: impl IntoIndexedBindGroupLayoutEntryBuilderArray<N>,
) -> Self {
self.entries.extend(
entries
.into_array()
.into_iter()
.map(|(binding, resource)| resource.build(binding, self.default_visibility)),
);
self
}
}
impl std::ops::Deref for DynamicBindGroupLayoutEntries {
type Target = [BindGroupLayoutEntry];
fn deref(&self) -> &[BindGroupLayoutEntry] {
&self.entries
}
}
pub mod binding_types {
use crate::render_resource::{
BufferBindingType, SamplerBindingType, TextureSampleType, TextureViewDimension,
};
use encase::ShaderType;
use std::num::NonZeroU64;
use wgpu::{BindingType, StorageTextureAccess, TextureFormat};
use super::*;
pub fn storage_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
storage_buffer_sized(has_dynamic_offset, Some(T::min_size()))
}
pub fn storage_buffer_sized(
has_dynamic_offset: bool,
min_binding_size: Option<NonZeroU64>,
) -> BindGroupLayoutEntryBuilder {
BindingType::Buffer {
ty: BufferBindingType::Storage { read_only: false },
has_dynamic_offset,
min_binding_size,
}
.into_bind_group_layout_entry_builder()
}
pub fn storage_buffer_read_only<T: ShaderType>(
has_dynamic_offset: bool,
) -> BindGroupLayoutEntryBuilder {
storage_buffer_read_only_sized(has_dynamic_offset, Some(T::min_size()))
}
pub fn storage_buffer_read_only_sized(
has_dynamic_offset: bool,
min_binding_size: Option<NonZeroU64>,
) -> BindGroupLayoutEntryBuilder {
BindingType::Buffer {
ty: BufferBindingType::Storage { read_only: true },
has_dynamic_offset,
min_binding_size,
}
.into_bind_group_layout_entry_builder()
}
pub fn uniform_buffer<T: ShaderType>(has_dynamic_offset: bool) -> BindGroupLayoutEntryBuilder {
uniform_buffer_sized(has_dynamic_offset, Some(T::min_size()))
}
pub fn uniform_buffer_sized(
has_dynamic_offset: bool,
min_binding_size: Option<NonZeroU64>,
) -> BindGroupLayoutEntryBuilder {
BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset,
min_binding_size,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_2d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D2,
multisampled: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_2d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D2,
multisampled: true,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_2d_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D2Array,
multisampled: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_2d_array_multisampled(
sample_type: TextureSampleType,
) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D2Array,
multisampled: true,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_depth_2d() -> BindGroupLayoutEntryBuilder {
texture_2d(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
}
pub fn texture_depth_2d_multisampled() -> BindGroupLayoutEntryBuilder {
texture_2d_multisampled(TextureSampleType::Depth).into_bind_group_layout_entry_builder()
}
pub fn texture_cube(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::Cube,
multisampled: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_cube_multisampled(
sample_type: TextureSampleType,
) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::Cube,
multisampled: true,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_cube_array(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::CubeArray,
multisampled: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_cube_array_multisampled(
sample_type: TextureSampleType,
) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::CubeArray,
multisampled: true,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_3d(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D3,
multisampled: false,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_3d_multisampled(sample_type: TextureSampleType) -> BindGroupLayoutEntryBuilder {
BindingType::Texture {
sample_type,
view_dimension: TextureViewDimension::D3,
multisampled: true,
}
.into_bind_group_layout_entry_builder()
}
pub fn sampler(sampler_binding_type: SamplerBindingType) -> BindGroupLayoutEntryBuilder {
BindingType::Sampler(sampler_binding_type).into_bind_group_layout_entry_builder()
}
pub fn texture_storage_2d(
format: TextureFormat,
access: StorageTextureAccess,
) -> BindGroupLayoutEntryBuilder {
BindingType::StorageTexture {
access,
format,
view_dimension: TextureViewDimension::D2,
}
.into_bind_group_layout_entry_builder()
}
pub fn texture_storage_2d_array(
format: TextureFormat,
access: StorageTextureAccess,
) -> BindGroupLayoutEntryBuilder {
BindingType::StorageTexture {
access,
format,
view_dimension: TextureViewDimension::D2Array,
}
.into_bind_group_layout_entry_builder()
}
}

View file

@ -1,4 +1,7 @@
use super::StorageBuffer;
use super::{
binding_types::{storage_buffer_read_only, uniform_buffer_sized},
BindGroupLayoutEntryBuilder, StorageBuffer,
};
use crate::{
render_resource::batched_uniform_buffer::BatchedUniformBuffer,
renderer::{RenderDevice, RenderQueue},
@ -7,7 +10,7 @@ use bevy_ecs::{prelude::Component, system::Resource};
use bevy_utils::nonmax::NonMaxU32;
use encase::{private::WriteInto, ShaderSize, ShaderType};
use std::{marker::PhantomData, mem};
use wgpu::{BindGroupLayoutEntry, BindingResource, BindingType, BufferBindingType, ShaderStages};
use wgpu::BindingResource;
/// Trait for types able to go in a [`GpuArrayBuffer`].
pub trait GpuArrayBufferable: ShaderType + ShaderSize + WriteInto + Clone {}
@ -74,30 +77,16 @@ impl<T: GpuArrayBufferable> GpuArrayBuffer<T> {
}
}
pub fn binding_layout(
binding: u32,
visibility: ShaderStages,
device: &RenderDevice,
) -> BindGroupLayoutEntry {
BindGroupLayoutEntry {
binding,
visibility,
ty: if device.limits().max_storage_buffers_per_shader_stage == 0 {
BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// BatchedUniformBuffer uses a MaxCapacityArray that is runtime-sized, so we use
// None here and let wgpu figure out the size.
min_binding_size: None,
}
} else {
BindingType::Buffer {
ty: BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: Some(T::min_size()),
}
},
count: None,
pub fn binding_layout(device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
if device.limits().max_storage_buffers_per_shader_stage == 0 {
uniform_buffer_sized(
true,
// BatchedUniformBuffer uses a MaxCapacityArray that is runtime-sized, so we use
// None here and let wgpu figure out the size.
None,
)
} else {
storage_buffer_read_only::<T>(false)
}
}

View file

@ -2,6 +2,7 @@ mod batched_uniform_buffer;
mod bind_group;
mod bind_group_entries;
mod bind_group_layout;
mod bind_group_layout_entries;
mod buffer;
mod buffer_vec;
mod gpu_array_buffer;
@ -17,6 +18,7 @@ mod uniform_buffer;
pub use bind_group::*;
pub use bind_group_entries::*;
pub use bind_group_layout::*;
pub use bind_group_layout_entries::*;
pub use buffer::*;
pub use buffer_vec::*;
pub use gpu_array_buffer::*;

View file

@ -4,7 +4,8 @@ use crate::render_resource::{
};
use bevy_ecs::system::Resource;
use wgpu::{
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BufferAsyncError, BufferBindingType,
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BufferAsyncError, BufferBindingType,
};
use super::RenderQueue;
@ -100,11 +101,18 @@ impl RenderDevice {
/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
#[inline]
pub fn create_bind_group_layout(
pub fn create_bind_group_layout<'a>(
&self,
desc: &wgpu::BindGroupLayoutDescriptor,
label: impl Into<wgpu::Label<'a>>,
entries: &'a [BindGroupLayoutEntry],
) -> BindGroupLayout {
BindGroupLayout::from(self.device.create_bind_group_layout(desc))
BindGroupLayout::from(
self.device
.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: label.into(),
entries,
}),
)
}
/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).

View file

@ -15,9 +15,9 @@ use wgpu::{
use crate::{
prelude::{Image, Shader},
render_resource::{
BindGroup, BindGroupLayout, Buffer, CachedRenderPipelineId, FragmentState, PipelineCache,
RenderPipelineDescriptor, SpecializedRenderPipeline, SpecializedRenderPipelines, Texture,
VertexState,
binding_types::texture_2d, BindGroup, BindGroupLayout, BindGroupLayoutEntries, Buffer,
CachedRenderPipelineId, FragmentState, PipelineCache, RenderPipelineDescriptor,
SpecializedRenderPipeline, SpecializedRenderPipelines, Texture, VertexState,
},
renderer::RenderDevice,
texture::TextureFormatPixelInfo,
@ -201,19 +201,13 @@ impl FromWorld for ScreenshotToScreenPipeline {
fn from_world(render_world: &mut World) -> Self {
let device = render_world.resource::<RenderDevice>();
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("screenshot-to-screen-bgl"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
}],
});
let bind_group_layout = device.create_bind_group_layout(
"screenshot-to-screen-bgl",
&BindGroupLayoutEntries::single(
wgpu::ShaderStages::FRAGMENT,
texture_2d(wgpu::TextureSampleType::Float { filterable: false }),
),
);
Self { bind_group_layout }
}

View file

@ -19,7 +19,7 @@ use bevy_render::{
mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout},
render_asset::RenderAssets,
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::*,
render_resource::{binding_types::uniform_buffer, *},
renderer::{RenderDevice, RenderQueue},
texture::{
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
@ -256,41 +256,25 @@ impl FromWorld for Mesh2dPipeline {
)> = SystemState::new(world);
let (render_device, render_queue, default_sampler) = system_state.get_mut(world);
let render_device = render_device.into_inner();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX_FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(GlobalsUniform::min_size()),
},
count: None,
},
],
label: Some("mesh2d_view_layout"),
});
let mesh_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[GpuArrayBuffer::<Mesh2dUniform>::binding_layout(
0,
let view_layout = render_device.create_bind_group_layout(
"mesh2d_view_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::VERTEX_FRAGMENT,
render_device,
)],
label: Some("mesh2d_layout"),
});
(
// View
uniform_buffer::<ViewUniform>(true),
uniform_buffer::<GlobalsUniform>(false),
),
),
);
let mesh_layout = render_device.create_bind_group_layout(
"mesh2d_layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
GpuArrayBuffer::<Mesh2dUniform>::binding_layout(render_device),
),
);
// A 1x1x1 'all 1.0' texture to use as a dummy texture to use in place of optional StandardMaterial textures
let dummy_white_gpu_image = {
let image = Image::default();

View file

@ -21,7 +21,10 @@ use bevy_render::{
DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline,
TrackedRenderPass,
},
render_resource::{BindGroupEntries, *},
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
BindGroupEntries, *,
},
renderer::{RenderDevice, RenderQueue},
texture::{
BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo,
@ -53,41 +56,24 @@ impl FromWorld for SpritePipeline {
)> = SystemState::new(world);
let (render_device, default_sampler, render_queue) = system_state.get_mut(world);
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
}],
label: Some("sprite_view_layout"),
});
let view_layout = render_device.create_bind_group_layout(
"sprite_view_layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
uniform_buffer::<ViewUniform>(true),
),
);
let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
label: Some("sprite_material_layout"),
});
let material_layout = render_device.create_bind_group_layout(
"sprite_material_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
),
),
);
let dummy_white_gpu_image = {
let image = Image::default();
let texture = render_device.create_texture(&image.texture_descriptor);

View file

@ -1,6 +1,9 @@
use bevy_ecs::prelude::*;
use bevy_render::{
render_resource::*,
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::RenderDevice,
texture::BevyDefault,
view::{ViewTarget, ViewUniform},
@ -16,41 +19,24 @@ impl FromWorld for UiPipeline {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
}],
label: Some("ui_view_layout"),
});
let view_layout = render_device.create_bind_group_layout(
"ui_view_layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
uniform_buffer::<ViewUniform>(true),
),
);
let image_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
label: Some("ui_image_layout"),
});
let image_layout = render_device.create_bind_group_layout(
"ui_image_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::FRAGMENT,
(
texture_2d(TextureSampleType::Float { filterable: true }),
sampler(SamplerBindingType::Filtering),
),
),
);
UiPipeline {
view_layout,

View file

@ -17,7 +17,7 @@ use bevy_render::{
extract_component::ExtractComponentPlugin,
render_asset::RenderAssets,
render_phase::*,
render_resource::*,
render_resource::{binding_types::uniform_buffer, *},
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, FallbackImage, Image},
view::*,
@ -223,19 +223,13 @@ impl<M: UiMaterial> FromWorld for UiMaterialPipeline<M> {
let render_device = world.resource::<RenderDevice>();
let ui_layout = M::bind_group_layout(render_device);
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
min_binding_size: Some(ViewUniform::min_size()),
},
count: None,
}],
label: Some("ui_view_layout"),
});
let view_layout = render_device.create_bind_group_layout(
"ui_view_layout",
&BindGroupLayoutEntries::single(
ShaderStages::VERTEX_FRAGMENT,
uniform_buffer::<ViewUniform>(true),
),
);
UiMaterialPipeline {
ui_layout,
view_layout,

View file

@ -9,7 +9,7 @@ use bevy::{
extract_resource::{ExtractResource, ExtractResourcePlugin},
render_asset::RenderAssets,
render_graph::{self, RenderGraph},
render_resource::*,
render_resource::{binding_types::texture_storage_2d, *},
renderer::{RenderContext, RenderDevice},
Render, RenderApp, RenderSet,
},
@ -124,22 +124,13 @@ pub struct GameOfLifePipeline {
impl FromWorld for GameOfLifePipeline {
fn from_world(world: &mut World) -> Self {
let texture_bind_group_layout =
world
.resource::<RenderDevice>()
.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: None,
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::COMPUTE,
ty: BindingType::StorageTexture {
access: StorageTextureAccess::ReadWrite,
format: TextureFormat::Rgba8Unorm,
view_dimension: TextureViewDimension::D2,
},
count: None,
}],
});
let texture_bind_group_layout = world.resource::<RenderDevice>().create_bind_group_layout(
None,
&BindGroupLayoutEntries::single(
ShaderStages::COMPUTE,
texture_storage_2d(TextureFormat::Rgba8Unorm, StorageTextureAccess::ReadWrite),
),
);
let shader = world
.resource::<AssetServer>()
.load("shaders/game_of_life.wgsl");

View file

@ -17,12 +17,8 @@ use bevy::{
NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner,
},
render_resource::{
BindGroupEntries, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
BindingType, CachedRenderPipelineId, ColorTargetState, ColorWrites, FragmentState,
MultisampleState, Operations, PipelineCache, PrimitiveState, RenderPassColorAttachment,
RenderPassDescriptor, RenderPipelineDescriptor, Sampler, SamplerBindingType,
SamplerDescriptor, ShaderStages, ShaderType, TextureFormat, TextureSampleType,
TextureViewDimension,
binding_types::{sampler, texture_2d, uniform_buffer},
*,
},
renderer::{RenderContext, RenderDevice},
texture::BevyDefault,
@ -227,40 +223,21 @@ impl FromWorld for PostProcessPipeline {
let render_device = world.resource::<RenderDevice>();
// We need to define the bind group layout used for our pipeline
let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("post_process_bind_group_layout"),
entries: &[
// The screen texture
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
// The sampler that will be used to sample the screen texture
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
// The settings uniform that will control the effect
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: bevy::render::render_resource::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: Some(PostProcessSettings::min_size()),
},
count: None,
},
],
});
let layout = render_device.create_bind_group_layout(
"post_process_bind_group_layout",
&BindGroupLayoutEntries::sequential(
// The layout entries will only be visible in the fragment stage
ShaderStages::FRAGMENT,
(
// The screen texture
texture_2d(TextureSampleType::Float { filterable: true }),
// The sampler that will be used to sample the screen texture
sampler(SamplerBindingType::Filtering),
// The settings uniform that will control the effect
uniform_buffer::<PostProcessSettings>(false),
),
),
);
// We can create the sampler here since it won't change at runtime and doesn't depend on the view
let sampler = render_device.create_sampler(&SamplerDescriptor::default());