From 6813b2edc5eb5833b11bb7b4f01a3c662d1953e2 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Sun, 7 Nov 2021 14:13:54 +0100 Subject: [PATCH] WebGL2 hacks --- Cargo.toml | 3 ++ crates/bevy_wgpu/src/wgpu_type_converter.rs | 1 + examples/3d/3d_scene_pipelined.rs | 7 +++ examples/3d/load_gltf_pipelined.rs | 7 +++ index.html.template | 15 ++++++ pipelined/bevy_pbr2/src/light.rs | 2 +- pipelined/bevy_pbr2/src/render/light.rs | 24 +++++---- pipelined/bevy_pbr2/src/render/mod.rs | 13 +++-- pipelined/bevy_pbr2/src/render/pbr.wgsl | 50 ++++++++++++++++++- .../src/render_resource/pipeline_cache.rs | 4 +- .../src/render_resource/shader.rs | 1 + wasm_build_example.sh | 14 ++++++ 12 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 index.html.template create mode 100755 wasm_build_example.sh diff --git a/Cargo.toml b/Cargo.toml index bdae6ba4401ed..f442c59d23599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,9 @@ subpixel_glyph_atlas = ["bevy_internal/subpixel_glyph_atlas"] # enable systems that allow for automated testing on CI bevy_ci_testing = ["bevy_internal/bevy_ci_testing"] +[patch.crates-io] +wgpu = { path = "../wgpu/wgpu" } + [dependencies] 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 } diff --git a/crates/bevy_wgpu/src/wgpu_type_converter.rs b/crates/bevy_wgpu/src/wgpu_type_converter.rs index a1e72e36f6f3c..eaaaeb4018a52 100644 --- a/crates/bevy_wgpu/src/wgpu_type_converter.rs +++ b/crates/bevy_wgpu/src/wgpu_type_converter.rs @@ -721,6 +721,7 @@ impl WgpuFrom for wgpu::Limits { max_vertex_buffer_array_stride: val.max_vertex_buffer_array_stride, min_storage_buffer_offset_alignment: val.min_storage_buffer_offset_alignment, min_uniform_buffer_offset_alignment: val.min_uniform_buffer_offset_alignment, + ..Default::default() } } } diff --git a/examples/3d/3d_scene_pipelined.rs b/examples/3d/3d_scene_pipelined.rs index d3c25432568fa..04a3f5402bfbf 100644 --- a/examples/3d/3d_scene_pipelined.rs +++ b/examples/3d/3d_scene_pipelined.rs @@ -13,12 +13,19 @@ use bevy::{ camera::{OrthographicProjection, PerspectiveCameraBundle}, color::Color, mesh::{shape, Mesh}, + view::Msaa, }, + window::WindowDescriptor, PipelinedDefaultPlugins, }; fn main() { App::new() + .insert_resource(Msaa { samples: 1 }) + .insert_resource(WindowDescriptor { + scale_factor_override: Some(1.0), + ..Default::default() + }) .add_plugins(PipelinedDefaultPlugins) .add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default()) diff --git a/examples/3d/load_gltf_pipelined.rs b/examples/3d/load_gltf_pipelined.rs index 8b842095e92b5..61d9f6f4369b5 100644 --- a/examples/3d/load_gltf_pipelined.rs +++ b/examples/3d/load_gltf_pipelined.rs @@ -7,12 +7,19 @@ use bevy::{ render2::{ camera::{OrthographicProjection, PerspectiveCameraBundle}, color::Color, + view::Msaa, }, + window::WindowDescriptor, PipelinedDefaultPlugins, }; fn main() { App::new() + .insert_resource(Msaa { samples: 1 }) + .insert_resource(WindowDescriptor { + scale_factor_override: Some(1.0), + ..Default::default() + }) .insert_resource(AmbientLight { color: Color::WHITE, brightness: 1.0 / 5.0f32, diff --git a/index.html.template b/index.html.template new file mode 100644 index 0000000000000..f447877666a46 --- /dev/null +++ b/index.html.template @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/pipelined/bevy_pbr2/src/light.rs b/pipelined/bevy_pbr2/src/light.rs index f2ea83aa36502..1b6b17644a607 100644 --- a/pipelined/bevy_pbr2/src/light.rs +++ b/pipelined/bevy_pbr2/src/light.rs @@ -148,7 +148,7 @@ pub struct DirectionalLightShadowMap { impl Default for DirectionalLightShadowMap { fn default() -> Self { - Self { size: 4096 } + Self { size: 1024 } } } diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index 9d4964fb5ff54..c4ca0e74a89db 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -137,10 +137,9 @@ pub struct GpuLights { // NOTE: this must be kept in sync with the same constants in pbr.frag pub const MAX_POINT_LIGHTS: usize = 256; +pub const MAX_POINT_LIGHT_SHADOW_MAPS: usize = 1; pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; -// FIXME: How should we handle shadows for clustered forward? Limiting to maximum 10 -// point light shadow maps for now -pub const POINT_SHADOW_LAYERS: u32 = (6 * 10) as u32; +pub const POINT_SHADOW_LAYERS: u32 = (6 * MAX_POINT_LIGHT_SHADOW_MAPS) as u32; pub const DIRECTIONAL_SHADOW_LAYERS: u32 = MAX_DIRECTIONAL_LIGHTS as u32; pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float; @@ -189,6 +188,8 @@ impl FromWorld for ShadowPipeline { min_filter: FilterMode::Linear, mipmap_filter: FilterMode::Nearest, compare: Some(CompareFunction::GreaterEqual), + // compare: Some(CompareFunction::LessEqual), + // compare: None, ..Default::default() }), directional_light_sampler: render_device.create_sampler(&SamplerDescriptor { @@ -199,6 +200,7 @@ impl FromWorld for ShadowPipeline { min_filter: FilterMode::Linear, mipmap_filter: FilterMode::Nearest, compare: Some(CompareFunction::GreaterEqual), + // compare: None, ..Default::default() }), } @@ -646,8 +648,8 @@ 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 - for (light_entity, light) in point_lights.iter() { - if !light.shadows_enabled { + for (i, (light_entity, light)) in point_lights.iter().enumerate().take(MAX_POINT_LIGHTS) { + if !light.shadows_enabled || i >= MAX_POINT_LIGHT_SHADOW_MAPS { continue; } let light_index = *global_light_meta @@ -787,9 +789,11 @@ pub fn prepare_lights( point_light_depth_texture .texture .create_view(&TextureViewDescriptor { - label: Some("point_light_shadow_map_array_texture_view"), + // label: Some("point_light_shadow_map_array_texture_view"), + label: Some("point_light_shadow_map_texture_view"), format: None, - dimension: Some(TextureViewDimension::CubeArray), + // dimension: Some(TextureViewDimension::CubeArray), + dimension: Some(TextureViewDimension::Cube), aspect: TextureAspect::All, base_mip_level: 0, mip_level_count: None, @@ -799,9 +803,11 @@ pub fn prepare_lights( let directional_light_depth_texture_view = directional_light_depth_texture .texture .create_view(&TextureViewDescriptor { - label: Some("directional_light_shadow_map_array_texture_view"), + // label: Some("directional_light_shadow_map_array_texture_view"), + label: Some("directional_light_shadow_map_texture_view"), format: None, - dimension: Some(TextureViewDimension::D2Array), + // dimension: Some(TextureViewDimension::D2Array), + dimension: Some(TextureViewDimension::D2), aspect: TextureAspect::All, base_mip_level: 0, mip_level_count: None, diff --git a/pipelined/bevy_pbr2/src/render/mod.rs b/pipelined/bevy_pbr2/src/render/mod.rs index 9519545aab2ad..b7e952853a4d0 100644 --- a/pipelined/bevy_pbr2/src/render/mod.rs +++ b/pipelined/bevy_pbr2/src/render/mod.rs @@ -204,22 +204,24 @@ impl FromWorld for PbrPipeline { }, count: None, }, - // Point Shadow Texture Cube Array + // Point Shadow Texture Cube [Array] BindGroupLayoutEntry { binding: 2, visibility: ShaderStages::FRAGMENT, ty: BindingType::Texture { multisampled: false, sample_type: TextureSampleType::Depth, - view_dimension: TextureViewDimension::CubeArray, + // view_dimension: TextureViewDimension::CubeArray, + view_dimension: TextureViewDimension::Cube, }, count: None, }, - // Point Shadow Texture Array Sampler + // Point Shadow Texture [Array] Sampler BindGroupLayoutEntry { binding: 3, visibility: ShaderStages::FRAGMENT, ty: BindingType::Sampler { + // comparison: false, comparison: true, filtering: true, }, @@ -232,7 +234,8 @@ impl FromWorld for PbrPipeline { ty: BindingType::Texture { multisampled: false, sample_type: TextureSampleType::Depth, - view_dimension: TextureViewDimension::D2Array, + // view_dimension: TextureViewDimension::D2Array, + view_dimension: TextureViewDimension::D2, }, count: None, }, @@ -242,6 +245,7 @@ impl FromWorld for PbrPipeline { visibility: ShaderStages::FRAGMENT, ty: BindingType::Sampler { comparison: true, + // comparison: false, filtering: true, }, count: None, @@ -590,6 +594,7 @@ impl SpecializedPipeline for PbrPipeline { // depth buffer depth_write_enabled = true; } + shader_defs.push(String::from("NO_CUBE_ARRAY_TEXTURES_SUPPORT")); RenderPipelineDescriptor { vertex: VertexState { shader: PBR_SHADER_HANDLE.typed::(), diff --git a/pipelined/bevy_pbr2/src/render/pbr.wgsl b/pipelined/bevy_pbr2/src/render/pbr.wgsl index 07584d183905b..aa374834ffd2a 100644 --- a/pipelined/bevy_pbr2/src/render/pbr.wgsl +++ b/pipelined/bevy_pbr2/src/render/pbr.wgsl @@ -184,14 +184,25 @@ struct ClusterOffsetsAndCounts { [[group(0), binding(1)]] var lights: Lights; +#ifdef CUBE_ARRAY_TEXTURES_SUPPORT [[group(0), binding(2)]] var point_shadow_textures: texture_depth_cube_array; [[group(0), binding(3)]] var point_shadow_textures_sampler: sampler_comparison; +#endif +#ifdef NO_CUBE_ARRAY_TEXTURES_SUPPORT +[[group(0), binding(2)]] +var point_shadow_textures: texture_depth_cube; +[[group(0), binding(3)]] +var point_shadow_textures_sampler: sampler_comparison; +// var point_shadow_textures_sampler: sampler; +#endif [[group(0), binding(4)]] -var directional_shadow_textures: texture_depth_2d_array; +// var directional_shadow_textures: texture_depth_2d_array; +var directional_shadow_textures: texture_depth_2d; [[group(0), binding(5)]] var directional_shadow_textures_sampler: sampler_comparison; +// var directional_shadow_textures_sampler: sampler; [[group(0), binding(6)]] var point_lights: PointLights; [[group(0), binding(7)]] @@ -516,7 +527,18 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4, 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 CUBE_ARRAY_TEXTURES_SUPPORT return textureSampleCompareLevel(point_shadow_textures, point_shadow_textures_sampler, frag_ls, i32(light_id), depth); +#endif +#ifdef NO_CUBE_ARRAY_TEXTURES_SUPPORT + return textureSampleCompare(point_shadow_textures, point_shadow_textures_sampler, frag_ls, depth); + // let sampled_depth = textureSample(point_shadow_textures, point_shadow_textures_sampler, frag_ls); + // if (sampled_depth >= depth) { + // return 0.0; + // } else { + // return 1.0; + // } +#endif } fn fetch_directional_shadow(light_id: u32, frag_position: vec4, surface_normal: vec3) -> f32 { @@ -547,7 +569,24 @@ fn fetch_directional_shadow(light_id: u32, frag_position: vec4, 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. - return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), depth); + // return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id), depth); + return textureSampleCompareLevel(directional_shadow_textures, directional_shadow_textures_sampler, light_local, depth); + // let sampled_depth = textureSample(directional_shadow_textures, directional_shadow_textures_sampler, light_local, i32(light_id)); + // // No shadow outside the orthographic projection volume + + // if (offset_position_clip.w <= 0.0) { + // return 1.0; + // } + // if (any(offset_position_ndc.xy < vec2(-1.0)) || offset_position_ndc.z < 0.0 + // || any(offset_position_ndc > vec3(1.0))) { + // return 1.0; + // } + + // if (sampled_depth >= depth) { + // return 0.0; + // } else { + // return 1.0; + // } } fn hsv2rgb(hue: f32, saturation: f32, value: f32) -> vec3 { @@ -683,8 +722,15 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4 { let light_id = get_light_id(i); let light = point_lights.data[light_id]; var shadow: f32 = 1.0; +#ifdef CUBE_ARRAY_TEXTURES_SUPPORT if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u || (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { +#endif +#ifdef NO_CUBE_ARRAY_TEXTURES_SUPPORT + if (light_id < 1u + && ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u + || (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u)) { +#endif shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal); } let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, N, V, R, F0, diffuse_color); diff --git a/pipelined/bevy_render2/src/render_resource/pipeline_cache.rs b/pipelined/bevy_render2/src/render_resource/pipeline_cache.rs index 53c9e1a32c135..7d8fbc5868024 100644 --- a/pipelined/bevy_render2/src/render_resource/pipeline_cache.rs +++ b/pipelined/bevy_render2/src/render_resource/pipeline_cache.rs @@ -11,7 +11,7 @@ use crate::{ use bevy_app::EventReader; use bevy_asset::{AssetEvent, Handle}; use bevy_ecs::system::{Res, ResMut}; -use bevy_utils::{HashMap, HashSet}; +use bevy_utils::{tracing::error, HashMap, HashSet}; use std::{collections::hash_map::Entry, hash::Hash, ops::Deref, sync::Arc}; use thiserror::Error; use wgpu::{PipelineLayoutDescriptor, ShaderModule, VertexBufferLayout}; @@ -54,6 +54,7 @@ impl ShaderCache { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { let processed = self.processor.process_shader(shader, shader_defs)?; + error!("Shader code:\n{:?}", processed); let module_descriptor = processed.get_module_descriptor()?; entry.insert(Arc::new( render_device.create_shader_module(&module_descriptor), @@ -249,6 +250,7 @@ impl RenderPipelineCache { continue; } }; + error!("Fragment shader module:\n{:?}", fragment_module); Some(( fragment_module, fragment.entry_point.deref(), diff --git a/pipelined/bevy_render2/src/render_resource/shader.rs b/pipelined/bevy_render2/src/render_resource/shader.rs index 90f80e190af65..d49c096ea9c44 100644 --- a/pipelined/bevy_render2/src/render_resource/shader.rs +++ b/pipelined/bevy_render2/src/render_resource/shader.rs @@ -45,6 +45,7 @@ pub enum Shader { } /// A processed [Shader]. This cannot contain preprocessor directions. It must be "ready to compile" +#[derive(Debug)] pub enum ProcessedShader { Wgsl(Cow<'static, str>), Glsl(Cow<'static, str>, naga::ShaderStage), diff --git a/wasm_build_example.sh b/wasm_build_example.sh new file mode 100755 index 0000000000000..b40a673c7cf7f --- /dev/null +++ b/wasm_build_example.sh @@ -0,0 +1,14 @@ +#!/bin/sh -ex + +export EXAMPLE_NAME="${1}" +cargo build \ + --release \ + --example "${EXAMPLE_NAME}" \ + --target wasm32-unknown-unknown \ + --no-default-features \ + --features bevy_core_pipeline,bevy_gltf2,bevy_sprite2,bevy_render2,bevy_pbr2,bevy_winit,png,x11,jpeg \ +&& wasm-bindgen \ + --out-dir web \ + --target web \ + "target/wasm32-unknown-unknown/release/examples/${EXAMPLE_NAME}.wasm" \ +&& envsubst < index.html.template > "web/${EXAMPLE_NAME}.html"