Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - enable Webgl2 optimisation in pbr under feature #3291

Closed
wants to merge 13 commits into from
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ bevy_ci_testing = ["bevy_internal/bevy_ci_testing"]
bevy_dylib = { path = "crates/bevy_dylib", version = "0.5.0", default-features = false, optional = true }
bevy_internal = { path = "crates/bevy_internal", version = "0.5.0", default-features = false }

[target.'cfg(target_arch = "wasm32")'.dependencies]
bevy_internal = { path = "crates/bevy_internal", version = "0.5.0", default-features = false, features = ["webgl"] }

[dev-dependencies]
anyhow = "1.0.4"
rand = "0.8.0"
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ x11 = ["bevy_winit/x11"]
# enable rendering of font glyphs using subpixel accuracy
subpixel_glyph_atlas = ["bevy_text/subpixel_glyph_atlas"]

# Optimise for WebGL2
webgl = ["bevy_pbr/webgl", "bevy_render/webgl"]

# enable systems that allow for automated testing on CI
bevy_ci_testing = ["bevy_app/bevy_ci_testing", "bevy_render/ci_limits"]

Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]

[features]
webgl = []

[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.5.0" }
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ pub struct DirectionalLightShadowMap {

impl Default for DirectionalLightShadowMap {
fn default() -> Self {
Self { size: 4096 }
#[cfg(feature = "webgl")]
return Self { size: 2048 };
#[cfg(not(feature = "webgl"))]
return Self { size: 4096 };
}
}

Expand Down
149 changes: 87 additions & 62 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ pub struct GpuLights {
pub const MAX_POINT_LIGHTS: usize = 256;
// FIXME: How should we handle shadows for clustered forward? Limiting to maximum 10
// point light shadow maps for now
#[cfg(feature = "webgl")]
pub const MAX_POINT_LIGHT_SHADOW_MAPS: usize = 1;
#[cfg(not(feature = "webgl"))]
pub const MAX_POINT_LIGHT_SHADOW_MAPS: usize = 10;
pub const MAX_DIRECTIONAL_LIGHTS: usize = 1;
pub const POINT_SHADOW_LAYERS: u32 = (6 * MAX_POINT_LIGHT_SHADOW_MAPS) as u32;
Expand Down Expand Up @@ -587,14 +590,30 @@ pub fn prepare_lights(

global_light_meta.gpu_point_lights.clear();
global_light_meta.entity_to_index.clear();
let n_point_lights = point_lights.iter().count();
if global_light_meta.entity_to_index.capacity() < n_point_lights {
global_light_meta.entity_to_index.reserve(n_point_lights);

let mut point_lights: Vec<_> = point_lights.iter().collect::<Vec<_>>();

// Sort point lights with shadows enabled first, then by a stable key so that the index can be used
// to render at most `MAX_POINT_LIGHT_SHADOW_MAPS` point light shadows.
point_lights.sort_by(|(entity_1, light_1), (entity_2, light_2)| {
light_1
.shadows_enabled
.cmp(&light_2.shadows_enabled)
.reverse()
.then_with(|| entity_1.cmp(entity_2))
});

if global_light_meta.entity_to_index.capacity() < point_lights.len() {
global_light_meta
.entity_to_index
.reserve(point_lights.len());
}

let mut gpu_point_lights = [GpuPointLight::default(); MAX_POINT_LIGHTS];
for (index, (entity, light)) in point_lights.iter().enumerate() {
for (index, &(entity, light)) in point_lights.iter().enumerate() {
let mut flags = PointLightFlags::NONE;
if light.shadows_enabled {
// Lights are sorted, shadow enabled lights are first
if light.shadows_enabled && index < MAX_POINT_LIGHT_SHADOW_MAPS {
flags |= PointLightFlags::SHADOWS_ENABLED;
}
gpu_point_lights[index] = GpuPointLight {
Expand Down Expand Up @@ -683,63 +702,63 @@ pub fn prepare_lights(
};

// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
let mut point_light_count = 0;
for (light_entity, light) in point_lights.iter() {
if point_light_count < MAX_POINT_LIGHT_SHADOW_MAPS && light.shadows_enabled {
point_light_count += 1;
let light_index = *global_light_meta
.entity_to_index
.get(&light_entity)
.unwrap();
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation =
GlobalTransform::from_translation(light.transform.translation);

for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
let depth_texture_view =
point_light_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("point_light_shadow_map_texture_view"),
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: (light_index * 6 + face_index) as u32,
array_layer_count: NonZeroU32::new(1),
});

let view_light_entity = commands
.spawn()
.insert_bundle((
ShadowView {
depth_texture_view,
pass_name: format!(
"shadow pass point light {} {}",
light_index,
face_index_to_name(face_index)
),
},
ExtractedView {
width: point_light_shadow_map.size as u32,
height: point_light_shadow_map.size as u32,
transform: view_translation * *view_rotation,
projection: cube_face_projection,
near: POINT_LIGHT_NEAR_Z,
far: light.range,
},
RenderPhase::<Shadow>::default(),
LightEntity::Point {
light_entity,
face_index,
},
))
.id();
view_lights.push(view_light_entity);
}
for &(light_entity, light) in point_lights
.iter()
// Lights are sorted, shadow enabled lights are first
.take(MAX_POINT_LIGHT_SHADOW_MAPS)
.filter(|(_, light)| light.shadows_enabled)
{
let light_index = *global_light_meta
.entity_to_index
.get(&light_entity)
.unwrap();
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation = GlobalTransform::from_translation(light.transform.translation);

for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
let depth_texture_view =
point_light_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: Some("point_light_shadow_map_texture_view"),
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: (light_index * 6 + face_index) as u32,
array_layer_count: NonZeroU32::new(1),
});

let view_light_entity = commands
.spawn()
.insert_bundle((
ShadowView {
depth_texture_view,
pass_name: format!(
"shadow pass point light {} {}",
light_index,
face_index_to_name(face_index)
),
},
ExtractedView {
width: point_light_shadow_map.size as u32,
height: point_light_shadow_map.size as u32,
transform: view_translation * *view_rotation,
projection: cube_face_projection,
near: POINT_LIGHT_NEAR_Z,
far: light.range,
},
RenderPhase::<Shadow>::default(),
LightEntity::Point {
light_entity,
face_index,
},
))
.id();
view_lights.push(view_light_entity);
}
}

Expand Down Expand Up @@ -830,7 +849,10 @@ pub fn prepare_lights(
.create_view(&TextureViewDescriptor {
label: Some("point_light_shadow_map_array_texture_view"),
format: None,
#[cfg(not(feature = "webgl"))]
dimension: Some(TextureViewDimension::CubeArray),
#[cfg(feature = "webgl")]
dimension: Some(TextureViewDimension::Cube),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
Expand All @@ -842,7 +864,10 @@ pub fn prepare_lights(
.create_view(&TextureViewDescriptor {
label: Some("directional_light_shadow_map_array_texture_view"),
format: None,
#[cfg(not(feature = "webgl"))]
dimension: Some(TextureViewDimension::D2Array),
#[cfg(feature = "webgl")]
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
Expand Down
9 changes: 9 additions & 0 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,10 @@ impl FromWorld for MeshPipeline {
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
#[cfg(not(feature = "webgl"))]
view_dimension: TextureViewDimension::CubeArray,
#[cfg(feature = "webgl")]
view_dimension: TextureViewDimension::Cube,
},
count: None,
},
Expand All @@ -220,7 +223,10 @@ impl FromWorld for MeshPipeline {
ty: BindingType::Texture {
multisampled: false,
sample_type: TextureSampleType::Depth,
#[cfg(not(feature = "webgl"))]
view_dimension: TextureViewDimension::D2Array,
#[cfg(feature = "webgl")]
view_dimension: TextureViewDimension::D2,
},
count: None,
},
Expand Down Expand Up @@ -485,6 +491,9 @@ impl SpecializedPipeline for MeshPipeline {
depth_write_enabled = true;
}

#[cfg(feature = "webgl")]
shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT"));

RenderPipelineDescriptor {
vertex: VertexState {
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_pbr/src/render/mesh_view_bind_group.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,22 @@ struct ClusterOffsetsAndCounts {
var<uniform> view: View;
[[group(0), binding(1)]]
var<uniform> lights: Lights;
#ifdef NO_ARRAY_TEXTURES_SUPPORT
[[group(0), binding(2)]]
var point_shadow_textures: texture_depth_cube;
#else
[[group(0), binding(2)]]
var point_shadow_textures: texture_depth_cube_array;
#endif
[[group(0), binding(3)]]
var point_shadow_textures_sampler: sampler_comparison;
#ifdef NO_ARRAY_TEXTURES_SUPPORT
[[group(0), binding(4)]]
var directional_shadow_textures: texture_depth_2d;
#else
[[group(0), binding(4)]]
var directional_shadow_textures: texture_depth_2d_array;
#endif
[[group(0), binding(5)]]
var directional_shadow_textures_sampler: sampler_comparison;
[[group(0), binding(6)]]
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,11 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v
// a quad (2x2 fragments) being processed not being sampled, and this messing with
// mip-mapping functionality. The shadow maps have no mipmaps so Level just samples
// from LOD 0.
#ifdef NO_ARRAY_TEXTURES_SUPPORT
return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls, depth);
#else
return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth);
#endif
}

fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: vec3<f32>) -> f32 {
Expand Down Expand Up @@ -412,7 +416,11 @@ fn fetch_directional_shadow(light_id: u32, frag_position: vec4<f32>, surface_nor
// do the lookup, using HW PCF and comparison
// NOTE: Due to non-uniform control flow above, we must use the level variant of the texture
// sampler to avoid use of implicit derivatives causing possible undefined behavior.
#ifdef NO_ARRAY_TEXTURES_SUPPORT
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, depth);
#else
return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), depth);
#endif
}

fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3<f32> {
Expand Down
4 changes: 1 addition & 3 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bmp = ["image/bmp"]
trace = []
wgpu_trace = ["wgpu/trace"]
ci_limits = []
webgl = ["wgpu/webgl"]

[dependencies]
# bevy
Expand Down Expand Up @@ -51,6 +52,3 @@ hexasphere = "6.0.0"
parking_lot = "0.11.0"
regex = "1.5"
crevice = { path = "../crevice", version = "0.8.0", features = ["glam"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wgpu = { version = "0.12.0", features = ["spirv", "webgl"] }
4 changes: 2 additions & 2 deletions crates/bevy_render/src/mesh/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,15 +623,15 @@ impl RenderAsset for Mesh {
let vertex_buffer_data = mesh.get_vertex_buffer_data();
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::VERTEX,
label: None,
label: Some("Mesh Vertex Buffer"),
contents: &vertex_buffer_data,
});

let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo {
buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::INDEX,
contents: data,
label: None,
label: Some("Mesh Index Buffer"),
}),
count: mesh.indices().unwrap().len() as u32,
index_format: mesh.indices().unwrap().into(),
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_render/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ pub async fn initialize_renderer(
.await
.expect("Unable to find a GPU! Make sure you have installed required drivers!");

#[cfg(not(target_arch = "wasm32"))]
info!("{:?}", adapter.get_info());

#[cfg(feature = "wgpu_trace")]
Expand Down
8 changes: 7 additions & 1 deletion crates/bevy_render/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,18 @@ pub struct Msaa {
/// smoother edges. Note that WGPU currently only supports 1 or 4 samples.
/// Ultimately we plan on supporting whatever is natively supported on a given device.
/// Check out this issue for more info: <https://github.com/gfx-rs/wgpu/issues/1832>
/// It defaults to 1 in wasm - <https://github.com/gfx-rs/wgpu/issues/2149>
pub samples: u32,
}

impl Default for Msaa {
fn default() -> Self {
Self { samples: 4 }
Self {
#[cfg(feature = "webgl")]
samples: 1,
#[cfg(not(feature = "webgl"))]
samples: 4,
}
}
}

Expand Down