enable Webgl2 optimisation in pbr under feature (#3291)
# Objective
- 3d examples fail to run in webgl2 because of unsupported texture formats or texture too large
## Solution
- switch to supported formats if a feature is enabled. I choose a feature instead of a build target to not conflict with a potential webgpu support
Very inspired by 6813b2edc5
, and need #3290 to work.
I named the feature `webgl2`, but it's only needed if one want to use PBR in webgl2. Examples using only 2D already work.
Co-authored-by: François <8672791+mockersf@users.noreply.github.com>
This commit is contained in:
parent
a3c53e689d
commit
6c479649bf
@ -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"
|
||||
|
@ -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"]
|
||||
|
||||
|
@ -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" }
|
||||
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -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 {
|
||||
@ -683,10 +702,12 @@ 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;
|
||||
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)
|
||||
@ -694,8 +715,7 @@ pub fn prepare_lights(
|
||||
// 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);
|
||||
let view_translation = GlobalTransform::from_translation(light.transform.translation);
|
||||
|
||||
for (face_index, view_rotation) in cube_face_rotations.iter().enumerate() {
|
||||
let depth_texture_view =
|
||||
@ -741,7 +761,6 @@ pub fn prepare_lights(
|
||||
view_lights.push(view_light_entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i, (light_entity, light)) in directional_lights
|
||||
.iter()
|
||||
@ -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,
|
||||
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
@ -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,
|
||||
},
|
||||
@ -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>(),
|
||||
|
@ -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)]]
|
||||
|
@ -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 {
|
||||
@ -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> {
|
||||
|
@ -18,6 +18,7 @@ bmp = ["image/bmp"]
|
||||
trace = []
|
||||
wgpu_trace = ["wgpu/trace"]
|
||||
ci_limits = []
|
||||
webgl = ["wgpu/webgl"]
|
||||
|
||||
[dependencies]
|
||||
# bevy
|
||||
@ -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"] }
|
||||
|
@ -623,7 +623,7 @@ 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,
|
||||
});
|
||||
|
||||
@ -631,7 +631,7 @@ impl RenderAsset for Mesh {
|
||||
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(),
|
||||
|
@ -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")]
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user