Skip to content

Commit ae1e094

Browse files
atlv24mate-h
andauthored
Fix webgl2 (#20286)
# Objective - #19076 (comment) broke webgl2 and bloated binary size by a megabyte - Fixes #20276 ## Solution - Don't embed stbn.ktx2, just load it for now - Gate gen env maps by limit checks - Extract a plugin to do this cleanly - Only load stbn.ktx2 when its needed ## Testing - reflection_probes example - someone please test webgl2 --------- Co-authored-by: Máté Homolya <[email protected]>
1 parent 38d3d37 commit ae1e094

File tree

9 files changed

+189
-72
lines changed

9 files changed

+189
-72
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,13 @@ gltf_convert_coordinates_default = [
579579
# Enable collecting debug information about systems and components to help with diagnostics
580580
debug = ["bevy_internal/debug"]
581581

582+
# Include spatio-temporal blue noise KTX2 file used by generated environment maps, Solari and atmosphere
583+
bluenoise_texture = [
584+
"bevy_internal/bluenoise_texture",
585+
"ktx2",
586+
"bevy_image/zstd",
587+
]
588+
582589
[dependencies]
583590
bevy_internal = { path = "crates/bevy_internal", version = "0.17.0-dev", default-features = false }
584591
tracing = { version = "0.1", default-features = false, optional = true }

crates/bevy_internal/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ statically-linked-dxc = ["bevy_render/statically-linked-dxc"]
6969

7070
# Include tonemapping LUT KTX2 files.
7171
tonemapping_luts = ["bevy_core_pipeline/tonemapping_luts"]
72+
# Include Bluenoise texture for environment map generation.
73+
bluenoise_texture = ["bevy_pbr?/bluenoise_texture"]
7274

7375
# Include SMAA LUT KTX2 Files
7476
smaa_luts = ["bevy_anti_aliasing/smaa_luts"]

crates/bevy_pbr/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ experimental_pbr_pcss = ["bevy_light/experimental_pbr_pcss"]
1818
pbr_specular_textures = []
1919
pbr_clustered_decals = []
2020
pbr_light_textures = []
21+
bluenoise_texture = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
2122
shader_format_glsl = ["bevy_render/shader_format_glsl"]
2223
trace = ["bevy_render/trace"]
2324
# Enables the meshlet renderer for dense high-poly scenes (experimental)
File renamed without changes.

crates/bevy_pbr/src/lib.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,23 @@ pub mod graph {
133133

134134
use crate::{deferred::DeferredPbrLightingPlugin, graph::NodePbr};
135135
use bevy_app::prelude::*;
136-
use bevy_asset::{embedded_asset, load_embedded_asset, AssetApp, AssetPath, Assets, Handle};
136+
use bevy_asset::{AssetApp, AssetPath, Assets, Handle, RenderAssetUsages};
137137
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
138138
use bevy_ecs::prelude::*;
139-
use bevy_image::Image;
139+
#[cfg(feature = "bluenoise_texture")]
140+
use bevy_image::{CompressedImageFormats, ImageType};
141+
use bevy_image::{Image, ImageSampler};
140142
use bevy_render::{
141143
alpha::AlphaMode,
142144
camera::{sort_cameras, Projection},
143145
extract_component::ExtractComponentPlugin,
144146
extract_resource::ExtractResourcePlugin,
145147
load_shader_library,
146148
render_graph::RenderGraph,
147-
render_resource::ShaderRef,
149+
render_resource::{
150+
Extent3d, ShaderRef, TextureDataOrder, TextureDescriptor, TextureDimension, TextureFormat,
151+
TextureUsages,
152+
},
148153
sync_component::SyncComponentPlugin,
149154
ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems,
150155
};
@@ -280,19 +285,42 @@ impl Plugin for PbrPlugin {
280285
},
281286
);
282287

283-
// Load the Spatio-temporal blue noise texture
284-
embedded_asset!(app, "stbn.ktx2");
285-
let bluenoise_texture = load_embedded_asset!(app, "stbn.ktx2");
288+
let has_bluenoise = app
289+
.get_sub_app(RenderApp)
290+
.is_some_and(|render_app| render_app.world().is_resource_added::<Bluenoise>());
291+
292+
if !has_bluenoise {
293+
let mut images = app.world_mut().resource_mut::<Assets<Image>>();
294+
#[cfg(feature = "bluenoise_texture")]
295+
let handle = {
296+
let image = Image::from_buffer(
297+
include_bytes!("bluenoise/stbn.ktx2"),
298+
ImageType::Extension("ktx2"),
299+
CompressedImageFormats::NONE,
300+
false,
301+
ImageSampler::Default,
302+
RenderAssetUsages::RENDER_WORLD,
303+
)
304+
.expect("Failed to decode embedded blue-noise texture");
305+
images.add(image)
306+
};
307+
308+
#[cfg(not(feature = "bluenoise_texture"))]
309+
let handle = { images.add(stbn_placeholder()) };
310+
311+
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
312+
render_app
313+
.world_mut()
314+
.insert_resource(Bluenoise { texture: handle });
315+
}
316+
}
286317

287318
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
288319
return;
289320
};
290321

291322
// Extract the required data from the main world
292323
render_app
293-
.insert_resource(Bluenoise {
294-
texture: bluenoise_texture,
295-
})
296324
.add_systems(
297325
RenderStartup,
298326
(
@@ -349,3 +377,26 @@ impl Plugin for PbrPlugin {
349377
app.insert_resource(global_cluster_settings);
350378
}
351379
}
380+
381+
pub fn stbn_placeholder() -> Image {
382+
let format = TextureFormat::Rgba8Unorm;
383+
let data = vec![255, 0, 255, 255];
384+
Image {
385+
data: Some(data),
386+
data_order: TextureDataOrder::default(),
387+
texture_descriptor: TextureDescriptor {
388+
size: Extent3d::default(),
389+
format,
390+
dimension: TextureDimension::D2,
391+
label: None,
392+
mip_level_count: 1,
393+
sample_count: 1,
394+
usage: TextureUsages::TEXTURE_BINDING,
395+
view_formats: &[],
396+
},
397+
sampler: ImageSampler::Default,
398+
texture_view_descriptor: None,
399+
asset_usage: RenderAssetUsages::RENDER_WORLD,
400+
copy_on_resize: false,
401+
}
402+
}

crates/bevy_pbr/src/light_probe/environment_filter.wgsl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#import bevy_render::maths::PI
22
#import bevy_pbr::{
33
lighting,
4-
utils::{sample_cosine_hemisphere, dir_to_cube_uv, sample_cube_dir, hammersley_2d}
4+
utils::{sample_cosine_hemisphere, dir_to_cube_uv, sample_cube_dir, hammersley_2d, rand_vec2f}
55
}
66

77
struct FilteringConstants {
@@ -24,13 +24,22 @@ fn sample_environment(dir: vec3f, level: f32) -> vec4f {
2424
}
2525

2626
// Blue noise randomization
27+
#ifdef HAS_BLUE_NOISE
2728
fn sample_noise(pixel_coords: vec2u) -> vec4f {
2829
let noise_size = vec2u(1) << constants.noise_size_bits;
2930
let noise_size_mask = noise_size - vec2u(1u);
3031
let noise_coords = pixel_coords & noise_size_mask;
3132
let uv = vec2f(noise_coords) / vec2f(noise_size);
3233
return textureSampleLevel(blue_noise_texture, input_sampler, uv, 0u, 0.0);
3334
}
35+
#else
36+
// pseudo-random numbers using RNG
37+
fn sample_noise(pixel_coords: vec2u) -> vec4f {
38+
var rng_state: u32 = (pixel_coords.x * 3966231743u) ^ (pixel_coords.y * 3928936651u);
39+
let rnd = rand_vec2f(&rng_state);
40+
return vec4f(rnd, 0.0, 0.0);
41+
}
42+
#endif
3443

3544
// Calculate LOD for environment map lookup using filtered importance sampling
3645
fn calculate_environment_map_lod(pdf: f32, width: f32, samples: f32) -> f32 {

crates/bevy_pbr/src/light_probe/generate.rs

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
//! This module provides realtime filtering via [`bevy_light::GeneratedEnvironmentMapLight`].
1212
//! For prefiltered environment maps, see [`bevy_light::EnvironmentMapLight`].
1313
//! These components are intended to be added to a camera.
14-
use bevy_asset::{load_embedded_asset, AssetServer, Assets};
14+
use bevy_app::{App, Plugin, Update};
15+
use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Assets};
16+
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
1517
use bevy_ecs::{
1618
component::Component,
1719
entity::Entity,
1820
query::{QueryState, With, Without},
1921
resource::Resource,
22+
schedule::IntoScheduleConfigs,
2023
system::{lifetimeless::Read, Commands, Query, Res, ResMut},
2124
world::{FromWorld, World},
2225
};
@@ -25,21 +28,22 @@ use bevy_math::{Quat, UVec2, Vec2};
2528
use bevy_render::{
2629
diagnostic::RecordDiagnostics,
2730
render_asset::{RenderAssetUsages, RenderAssets},
28-
render_graph::{Node, NodeRunError, RenderGraphContext, RenderLabel},
31+
render_graph::{Node, NodeRunError, RenderGraphContext, RenderGraphExt, RenderLabel},
2932
render_resource::{
3033
binding_types::*, AddressMode, BindGroup, BindGroupEntries, BindGroupLayout,
3134
BindGroupLayoutEntries, CachedComputePipelineId, ComputePassDescriptor,
32-
ComputePipelineDescriptor, Extent3d, FilterMode, PipelineCache, Sampler,
35+
ComputePipelineDescriptor, DownlevelFlags, Extent3d, FilterMode, PipelineCache, Sampler,
3336
SamplerBindingType, SamplerDescriptor, ShaderDefVal, ShaderStages, ShaderType,
3437
StorageTextureAccess, Texture, TextureAspect, TextureDescriptor, TextureDimension,
3538
TextureFormat, TextureFormatFeatureFlags, TextureSampleType, TextureUsages, TextureView,
3639
TextureViewDescriptor, TextureViewDimension, UniformBuffer,
3740
},
3841
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
3942
settings::WgpuFeatures,
43+
sync_component::SyncComponentPlugin,
4044
sync_world::RenderEntity,
4145
texture::{CachedTexture, GpuImage, TextureCache},
42-
Extract,
46+
Extract, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
4347
};
4448

4549
// Implementation: generate diffuse and specular cubemaps required by PBR
@@ -58,6 +62,7 @@ use bevy_render::{
5862

5963
use bevy_light::{EnvironmentMapLight, GeneratedEnvironmentMapLight};
6064
use core::cmp::min;
65+
use tracing::info;
6166

6267
use crate::Bluenoise;
6368

@@ -101,6 +106,77 @@ pub struct DownsamplingConfig {
101106
pub combine_bind_group: bool,
102107
}
103108

109+
pub struct EnvironmentMapGenerationPlugin;
110+
111+
impl Plugin for EnvironmentMapGenerationPlugin {
112+
fn build(&self, _: &mut App) {}
113+
fn finish(&self, app: &mut App) {
114+
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
115+
let adapter = render_app.world().resource::<RenderAdapter>();
116+
let device = render_app.world().resource::<RenderDevice>();
117+
118+
// Cubemap SPD requires at least 6 storage textures
119+
let limit_support = device.limits().max_storage_textures_per_shader_stage >= 6
120+
&& device.limits().max_compute_workgroup_storage_size != 0
121+
&& device.limits().max_compute_workgroup_size_x != 0;
122+
123+
let downlevel_support = adapter
124+
.get_downlevel_capabilities()
125+
.flags
126+
.contains(DownlevelFlags::COMPUTE_SHADERS);
127+
128+
if !limit_support || !downlevel_support {
129+
info!("Disabling EnvironmentMapGenerationPlugin because compute is not supported on this platform. This is safe to ignore if you are not using EnvironmentMapGenerationPlugin.");
130+
return;
131+
}
132+
} else {
133+
return;
134+
}
135+
136+
embedded_asset!(app, "environment_filter.wgsl");
137+
embedded_asset!(app, "downsample.wgsl");
138+
embedded_asset!(app, "copy.wgsl");
139+
140+
app.add_plugins(SyncComponentPlugin::<GeneratedEnvironmentMapLight>::default())
141+
.add_systems(Update, generate_environment_map_light);
142+
143+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
144+
return;
145+
};
146+
147+
render_app
148+
.add_render_graph_node::<DownsamplingNode>(Core3d, GeneratorNode::Downsampling)
149+
.add_render_graph_node::<FilteringNode>(Core3d, GeneratorNode::Filtering)
150+
.add_render_graph_edges(
151+
Core3d,
152+
(
153+
Node3d::EndPrepasses,
154+
GeneratorNode::Downsampling,
155+
GeneratorNode::Filtering,
156+
Node3d::StartMainPass,
157+
),
158+
)
159+
.add_systems(
160+
ExtractSchedule,
161+
extract_generated_environment_map_entities.after(generate_environment_map_light),
162+
)
163+
.add_systems(
164+
Render,
165+
prepare_generated_environment_map_bind_groups
166+
.in_set(RenderSystems::PrepareBindGroups),
167+
)
168+
.add_systems(
169+
Render,
170+
prepare_generated_environment_map_intermediate_textures
171+
.in_set(RenderSystems::PrepareResources),
172+
)
173+
.add_systems(
174+
RenderStartup,
175+
initialize_generated_environment_map_resources,
176+
);
177+
}
178+
}
179+
104180
// The number of storage textures required to combine the bind group
105181
const REQUIRED_STORAGE_TEXTURES: u32 = 12;
106182

@@ -289,6 +365,10 @@ pub fn initialize_generated_environment_map_resources(
289365
if combine_bind_group {
290366
shader_defs.push(ShaderDefVal::Int("COMBINE_BIND_GROUP".into(), 1));
291367
}
368+
#[cfg(feature = "bluenoise_texture")]
369+
{
370+
shader_defs.push(ShaderDefVal::Int("HAS_BLUE_NOISE".into(), 1));
371+
}
292372

293373
let downsampling_shader = load_embedded_asset!(asset_server.as_ref(), "downsample.wgsl");
294374
let env_filter_shader = load_embedded_asset!(asset_server.as_ref(), "environment_filter.wgsl");
@@ -333,7 +413,7 @@ pub fn initialize_generated_environment_map_resources(
333413
layout: vec![layouts.radiance.clone()],
334414
push_constant_ranges: vec![],
335415
shader: env_filter_shader.clone(),
336-
shader_defs: vec![],
416+
shader_defs: shader_defs.clone(),
337417
entry_point: Some("generate_radiance_map".into()),
338418
zero_initialize_workgroup_memory: false,
339419
});
@@ -344,7 +424,7 @@ pub fn initialize_generated_environment_map_resources(
344424
layout: vec![layouts.irradiance.clone()],
345425
push_constant_ranges: vec![],
346426
shader: env_filter_shader,
347-
shader_defs: vec![],
427+
shader_defs: shader_defs.clone(),
348428
entry_point: Some("generate_irradiance_map".into()),
349429
zero_initialize_workgroup_memory: false,
350430
});
@@ -639,6 +719,15 @@ pub fn prepare_generated_environment_map_bind_groups(
639719
(first, second)
640720
};
641721

722+
// create a 2d array view of the bluenoise texture
723+
let stbn_texture_view = stbn_texture
724+
.texture
725+
.clone()
726+
.create_view(&TextureViewDescriptor {
727+
dimension: Some(TextureViewDimension::D2Array),
728+
..Default::default()
729+
});
730+
642731
// Create radiance map bind groups for each mip level
643732
let num_mips = mip_count as usize;
644733
let mut radiance_bind_groups = Vec::with_capacity(num_mips);
@@ -672,7 +761,7 @@ pub fn prepare_generated_environment_map_bind_groups(
672761
&samplers.linear,
673762
&mip_storage_view,
674763
&radiance_constants_buffer,
675-
&stbn_texture.texture_view,
764+
&stbn_texture_view,
676765
)),
677766
);
678767

@@ -709,7 +798,7 @@ pub fn prepare_generated_environment_map_bind_groups(
709798
&samplers.linear,
710799
&irradiance_map,
711800
&irradiance_constants_buffer,
712-
&stbn_texture.texture_view,
801+
&stbn_texture_view,
713802
)),
714803
);
715804

0 commit comments

Comments
 (0)