Update to wgpu 0.19 and raw-window-handle 0.6 (#11280)

# Objective

Keep core dependencies up to date.

## Solution

Update the dependencies.

wgpu 0.19 only supports raw-window-handle (rwh) 0.6, so bumping that was
included in this.

The rwh 0.6 version bump is just the simplest way of doing it. There
might be a way we can take advantage of wgpu's new safe surface creation
api, but I'm not familiar enough with bevy's window management to
untangle it and my attempt ended up being a mess of lifetimes and rustc
complaining about missing trait impls (that were implemented). Thanks to
@MiniaczQ for the (much simpler) rwh 0.6 version bump code.

Unblocks https://github.com/bevyengine/bevy/pull/9172 and
https://github.com/bevyengine/bevy/pull/10812

~~This might be blocked on cpal and oboe updating their ndk versions to
0.8, as they both currently target ndk 0.7 which uses rwh 0.5.2~~ Tested
on android, and everything seems to work correctly (audio properly stops
when minimized, and plays when re-focusing the app).

---

## Changelog

- `wgpu` has been updated to 0.19! The long awaited arcanization has
been merged (for more info, see
https://gfx-rs.github.io/2023/11/24/arcanization.html), and Vulkan
should now be working again on Intel GPUs.
- Targeting WebGPU now requires that you add the new `webgpu` feature
(setting the `RUSTFLAGS` environment variable to
`--cfg=web_sys_unstable_apis` is still required). This feature currently
overrides the `webgl2` feature if you have both enabled (the `webgl2`
feature is enabled by default), so it is not recommended to add it as a
default feature to libraries without putting it behind a flag that
allows library users to opt out of it! In the future we plan on
supporting wasm binaries that can target both webgl2 and webgpu now that
wgpu added support for doing so (see
https://github.com/bevyengine/bevy/issues/11505).
- `raw-window-handle` has been updated to version 0.6.

## Migration Guide

- `bevy_render::instance_index::get_instance_index()` has been removed
as the webgl2 workaround is no longer required as it was fixed upstream
in wgpu. The `BASE_INSTANCE_WORKAROUND` shaderdef has also been removed.
- WebGPU now requires the new `webgpu` feature to be enabled. The
`webgpu` feature currently overrides the `webgl2` feature so you no
longer need to disable all default features and re-add them all when
targeting `webgpu`, but binaries built with both the `webgpu` and
`webgl2` features will only target the webgpu backend, and will only
work on browsers that support WebGPU.
- Places where you conditionally compiled things for webgl2 need to be
updated because of this change, eg:
- `#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))]`
becomes `#[cfg(any(not(feature = "webgl") ,not(target_arch = "wasm32"),
feature = "webgpu"))]`
- `#[cfg(all(feature = "webgl", target_arch = "wasm32"))]` becomes
`#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature =
"webgpu")))]`
- `if cfg!(all(feature = "webgl", target_arch = "wasm32"))` becomes `if
cfg!(all(feature = "webgl", target_arch = "wasm32", not(feature =
"webgpu")))`
- `create_texture_with_data` now also takes a `TextureDataOrder`. You
can probably just set this to `TextureDataOrder::default()`
- `TextureFormat`'s `block_size` has been renamed to `block_copy_size`
- See the `wgpu` changelog for anything I might've missed:
https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md

---------

Co-authored-by: François <mockersf@gmail.com>
This commit is contained in:
Elabajaba 2024-01-26 13:14:21 -05:00 committed by GitHub
parent dd4d07dc9c
commit 35ac1b152e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 260 additions and 203 deletions

View file

@ -277,9 +277,12 @@ shader_format_spirv = ["bevy_internal/shader_format_spirv"]
# Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs # Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs
pbr_transmission_textures = ["bevy_internal/pbr_transmission_textures"] pbr_transmission_textures = ["bevy_internal/pbr_transmission_textures"]
# Enable some limitations to be able to use WebGL2. If not enabled, it will default to WebGPU in Wasm. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU. # Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.
webgl2 = ["bevy_internal/webgl"] webgl2 = ["bevy_internal/webgl"]
# Enable support for WebGPU in Wasm. When enabled, this feature will override the `webgl2` feature and you won't be able to run Wasm builds with WebGL2, only with WebGPU. Requires the `RUSTFLAGS` environment variable to be set to `--cfg=web_sys_unstable_apis` when building.
webgpu = ["bevy_internal/webgpu"]
# Enables the built-in asset processor for processed assets. # Enables the built-in asset processor for processed assets.
asset_processor = ["bevy_internal/asset_processor"] asset_processor = ["bevy_internal/asset_processor"]

View file

@ -15,6 +15,7 @@ keywords = ["bevy"]
[features] [features]
trace = [] trace = []
webgl = [] webgl = []
webgpu = []
tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"] tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"]
[dependencies] [dependencies]

View file

@ -294,16 +294,24 @@ impl ViewNode for BloomNode {
#[derive(Component)] #[derive(Component)]
struct BloomTexture { struct BloomTexture {
// First mip is half the screen resolution, successive mips are half the previous // First mip is half the screen resolution, successive mips are half the previous
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
texture: CachedTexture, texture: CachedTexture,
// WebGL does not support binding specific mip levels for sampling, fallback to separate textures instead // WebGL does not support binding specific mip levels for sampling, fallback to separate textures instead
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
texture: Vec<CachedTexture>, texture: Vec<CachedTexture>,
mip_count: u32, mip_count: u32,
} }
impl BloomTexture { impl BloomTexture {
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
fn view(&self, base_mip_level: u32) -> TextureView { fn view(&self, base_mip_level: u32) -> TextureView {
self.texture.texture.create_view(&TextureViewDescriptor { self.texture.texture.create_view(&TextureViewDescriptor {
base_mip_level, base_mip_level,
@ -311,7 +319,7 @@ impl BloomTexture {
..Default::default() ..Default::default()
}) })
} }
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
fn view(&self, base_mip_level: u32) -> TextureView { fn view(&self, base_mip_level: u32) -> TextureView {
self.texture[base_mip_level as usize] self.texture[base_mip_level as usize]
.texture .texture
@ -354,9 +362,13 @@ fn prepare_bloom_textures(
view_formats: &[], view_formats: &[],
}; };
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
let texture = texture_cache.get(&render_device, texture_descriptor); let texture = texture_cache.get(&render_device, texture_descriptor);
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
let texture: Vec<CachedTexture> = (0..mip_count) let texture: Vec<CachedTexture> = (0..mip_count)
.map(|mip| { .map(|mip| {
texture_cache.get( texture_cache.get(

View file

@ -69,7 +69,7 @@ impl Node for MainPass2dNode {
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't
// reset for the next render pass so add an empty render pass without a custom viewport // reset for the next render pass so add an empty render pass without a custom viewport
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
if camera.viewport.is_some() { if camera.viewport.is_some() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _reset_viewport_pass_2d = info_span!("reset_viewport_pass_2d").entered(); let _reset_viewport_pass_2d = info_span!("reset_viewport_pass_2d").entered();

View file

@ -60,7 +60,7 @@ impl ViewNode for MainTransparentPass3dNode {
// WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't // WebGL2 quirk: if ending with a render pass with a custom viewport, the viewport isn't
// reset for the next render pass so add an empty render pass without a custom viewport // reset for the next render pass so add an empty render pass without a custom viewport
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
if camera.viewport.is_some() { if camera.viewport.is_some() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _reset_viewport_pass_3d = info_span!("reset_viewport_pass_3d").entered(); let _reset_viewport_pass_3d = info_span!("reset_viewport_pass_3d").entered();

View file

@ -67,7 +67,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
// Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT. // Firefox: WebGL warning: clearBufferu?[fi]v: This attachment is of type FLOAT, but this function is of type UINT.
// Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9 // Appears to be unsupported: https://registry.khronos.org/webgl/specs/latest/2.0/#3.7.9
// For webgl2 we fallback to manually clearing // For webgl2 we fallback to manually clearing
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
if let Some(deferred_texture) = &view_prepass_textures.deferred { if let Some(deferred_texture) = &view_prepass_textures.deferred {
render_context.command_encoder().clear_texture( render_context.command_encoder().clear_texture(
&deferred_texture.texture.texture, &deferred_texture.texture.texture,
@ -80,7 +80,7 @@ impl ViewNode for DeferredGBufferPrepassNode {
.deferred .deferred
.as_ref() .as_ref()
.map(|deferred_texture| { .map(|deferred_texture| {
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
{ {
bevy_render::render_resource::RenderPassColorAttachment { bevy_render::render_resource::RenderPassColorAttachment {
view: &deferred_texture.texture.default_view, view: &deferred_texture.texture.default_view,
@ -91,7 +91,11 @@ impl ViewNode for DeferredGBufferPrepassNode {
}, },
} }
} }
#[cfg(not(all(feature = "webgl", target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
deferred_texture.get_attachment() deferred_texture.get_attachment()
}), }),
); );

View file

@ -10,6 +10,7 @@ keywords = ["bevy"]
[features] [features]
webgl = [] webgl = []
webgpu = []
[dependencies] [dependencies]
# Bevy # Bevy

View file

@ -103,6 +103,14 @@ webgl = [
"bevy_sprite?/webgl", "bevy_sprite?/webgl",
] ]
webgpu = [
"bevy_core_pipeline?/webgpu",
"bevy_pbr?/webgpu",
"bevy_render?/webgpu",
"bevy_gizmos?/webgpu",
"bevy_sprite?/webgpu",
]
# enable systems that allow for automated testing on CI # enable systems that allow for automated testing on CI
bevy_ci_testing = [ bevy_ci_testing = [
"bevy_app/bevy_ci_testing", "bevy_app/bevy_ci_testing",

View file

@ -10,6 +10,7 @@ keywords = ["bevy"]
[features] [features]
webgl = [] webgl = []
webgpu = []
shader_format_glsl = ["naga_oil/glsl"] shader_format_glsl = ["naga_oil/glsl"]
pbr_transmission_textures = [] pbr_transmission_textures = []
@ -39,11 +40,11 @@ smallvec = "1.6"
thread_local = "1.0" thread_local = "1.0"
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
naga_oil = { version = "0.11" } naga_oil = "0.12"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# Omit the `glsl` feature in non-WebAssembly by default. # Omit the `glsl` feature in non-WebAssembly by default.
naga_oil = { version = "0.11", default-features = false, features = [ naga_oil = { version = "0.12", default-features = false, features = [
"test_shader", "test_shader",
] } ] }

View file

@ -43,11 +43,11 @@ pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1;
pub struct PbrDeferredLightingDepthId { pub struct PbrDeferredLightingDepthId {
depth_id: u32, depth_id: u32,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_0: f32, _webgl2_padding_0: f32,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_1: f32, _webgl2_padding_1: f32,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_2: f32, _webgl2_padding_2: f32,
} }
@ -56,11 +56,11 @@ impl PbrDeferredLightingDepthId {
PbrDeferredLightingDepthId { PbrDeferredLightingDepthId {
depth_id: value as u32, depth_id: value as u32,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_0: 0.0, _webgl2_padding_0: 0.0,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_1: 0.0, _webgl2_padding_1: 0.0,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_2: 0.0, _webgl2_padding_2: 0.0,
} }
} }
@ -79,11 +79,11 @@ impl Default for PbrDeferredLightingDepthId {
PbrDeferredLightingDepthId { PbrDeferredLightingDepthId {
depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32, depth_id: DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID as u32,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_0: 0.0, _webgl2_padding_0: 0.0,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_1: 0.0, _webgl2_padding_1: 0.0,
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_webgl2_padding_2: 0.0, _webgl2_padding_2: 0.0,
} }
} }
@ -245,7 +245,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
// Let the shader code know that it's running in a deferred pipeline. // Let the shader code know that it's running in a deferred pipeline.
shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into()); shader_defs.push("DEFERRED_LIGHTING_PIPELINE".into());
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
@ -310,7 +310,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
shader_defs.push("SHADOW_FILTER_METHOD_JIMENEZ_14".into()); shader_defs.push("SHADOW_FILTER_METHOD_JIMENEZ_14".into());
} }
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());
RenderPipelineDescriptor { RenderPipelineDescriptor {

View file

@ -350,7 +350,11 @@ impl CascadeShadowConfigBuilder {
impl Default for CascadeShadowConfigBuilder { impl Default for CascadeShadowConfigBuilder {
fn default() -> Self { fn default() -> Self {
if cfg!(all(feature = "webgl", target_arch = "wasm32")) { if cfg!(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
// Currently only support one cascade in webgl. // Currently only support one cascade in webgl.
Self { Self {
num_cascades: 1, num_cascades: 1,

View file

@ -325,7 +325,7 @@ where
// The main limitation right now is that bind group order is hardcoded in shaders. // The main limitation right now is that bind group order is hardcoded in shaders.
bind_group_layouts.push(self.material_layout.clone()); bind_group_layouts.push(self.material_layout.clone());
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
@ -518,7 +518,11 @@ where
}; };
let mut push_constant_ranges = Vec::with_capacity(1); let mut push_constant_ranges = Vec::with_capacity(1);
if cfg!(all(feature = "webgl", target_arch = "wasm32")) { if cfg!(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
push_constant_ranges.push(PushConstantRange { push_constant_ranges.push(PushConstantRange {
stages: ShaderStages::VERTEX, stages: ShaderStages::VERTEX,
range: 0..4, range: 0..4,

View file

@ -7,8 +7,6 @@
mesh_view_bindings::{view, previous_view_proj}, mesh_view_bindings::{view, previous_view_proj},
} }
#import bevy_render::instance_index::get_instance_index
#ifdef DEFERRED_PREPASS #ifdef DEFERRED_PREPASS
#import bevy_pbr::rgb9e5 #import bevy_pbr::rgb9e5
#endif #endif
@ -74,7 +72,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
vertex.normal, vertex.normal,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
get_instance_index(vertex_no_morph.instance_index) vertex_no_morph.instance_index
); );
#endif // SKINNED #endif // SKINNED
@ -84,7 +82,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
vertex.tangent, vertex.tangent,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
get_instance_index(vertex_no_morph.instance_index) vertex_no_morph.instance_index
); );
#endif // VERTEX_TANGENTS #endif // VERTEX_TANGENTS
#endif // NORMAL_PREPASS_OR_DEFERRED_PREPASS #endif // NORMAL_PREPASS_OR_DEFERRED_PREPASS
@ -107,13 +105,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef VERTEX_OUTPUT_INSTANCE_INDEX #ifdef VERTEX_OUTPUT_INSTANCE_INDEX
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
out.instance_index = get_instance_index(vertex_no_morph.instance_index); out.instance_index = vertex_no_morph.instance_index;
#endif
#ifdef BASE_INSTANCE_WORKAROUND
// Hack: this ensures the push constant is always used, which works around this issue:
// https://github.com/bevyengine/bevy/issues/10509
// This can be removed when wgpu 0.19 is released
out.position.x += min(f32(get_instance_index(0u)), 0.0);
#endif #endif
return out; return out;

View file

@ -205,13 +205,21 @@ pub const MAX_UNIFORM_BUFFER_POINT_LIGHTS: usize = 256;
//NOTE: When running bevy on Adreno GPU chipsets in WebGL, any value above 1 will result in a crash //NOTE: When running bevy on Adreno GPU chipsets in WebGL, any value above 1 will result in a crash
// when loading the wgsl "pbr_functions.wgsl" in the function apply_fog. // when loading the wgsl "pbr_functions.wgsl" in the function apply_fog.
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; pub const MAX_DIRECTIONAL_LIGHTS: usize = 1;
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
pub const MAX_DIRECTIONAL_LIGHTS: usize = 10; pub const MAX_DIRECTIONAL_LIGHTS: usize = 10;
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
pub const MAX_CASCADES_PER_LIGHT: usize = 4; pub const MAX_CASCADES_PER_LIGHT: usize = 4;
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
pub const MAX_CASCADES_PER_LIGHT: usize = 1; pub const MAX_CASCADES_PER_LIGHT: usize = 1;
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
@ -698,13 +706,21 @@ pub fn prepare_lights(
let mut point_lights: Vec<_> = point_lights.iter().collect::<Vec<_>>(); let mut point_lights: Vec<_> = point_lights.iter().collect::<Vec<_>>();
let mut directional_lights: Vec<_> = directional_lights.iter().collect::<Vec<_>>(); let mut directional_lights: Vec<_> = directional_lights.iter().collect::<Vec<_>>();
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
let max_texture_array_layers = render_device.limits().max_texture_array_layers as usize; let max_texture_array_layers = render_device.limits().max_texture_array_layers as usize;
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
let max_texture_cubes = max_texture_array_layers / 6; let max_texture_cubes = max_texture_array_layers / 6;
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
let max_texture_array_layers = 1; let max_texture_array_layers = 1;
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
let max_texture_cubes = 1; let max_texture_cubes = 1;
if !*max_directional_lights_warning_emitted && directional_lights.len() > MAX_DIRECTIONAL_LIGHTS if !*max_directional_lights_warning_emitted && directional_lights.len() > MAX_DIRECTIONAL_LIGHTS
@ -1177,9 +1193,17 @@ pub fn prepare_lights(
.create_view(&TextureViewDescriptor { .create_view(&TextureViewDescriptor {
label: Some("point_light_shadow_map_array_texture_view"), label: Some("point_light_shadow_map_array_texture_view"),
format: None, format: None,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
dimension: Some(TextureViewDimension::CubeArray), dimension: Some(TextureViewDimension::CubeArray),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
))]
dimension: Some(TextureViewDimension::Cube), dimension: Some(TextureViewDimension::Cube),
aspect: TextureAspect::DepthOnly, aspect: TextureAspect::DepthOnly,
base_mip_level: 0, base_mip_level: 0,
@ -1192,9 +1216,13 @@ pub fn prepare_lights(
.create_view(&TextureViewDescriptor { .create_view(&TextureViewDescriptor {
label: Some("directional_light_shadow_map_array_texture_view"), label: Some("directional_light_shadow_map_array_texture_view"),
format: None, format: None,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
dimension: Some(TextureViewDimension::D2Array), dimension: Some(TextureViewDimension::D2Array),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
dimension: Some(TextureViewDimension::D2), dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::DepthOnly, aspect: TextureAspect::DepthOnly,
base_mip_level: 0, base_mip_level: 0,

View file

@ -806,7 +806,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into()); shader_defs.push("VIEW_PROJECTION_ORTHOGRAPHIC".into());
} }
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
shader_defs.push("WEBGL2".into()); shader_defs.push("WEBGL2".into());
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) { if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
@ -899,7 +899,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
} }
let mut push_constant_ranges = Vec::with_capacity(1); let mut push_constant_ranges = Vec::with_capacity(1);
if cfg!(all(feature = "webgl", target_arch = "wasm32")) { if cfg!(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
push_constant_ranges.push(PushConstantRange { push_constant_ranges.push(PushConstantRange {
stages: ShaderStages::VERTEX, stages: ShaderStages::VERTEX,
range: 0..4, range: 0..4,
@ -1180,7 +1184,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
let batch_range = item.batch_range(); let batch_range = item.batch_range();
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
pass.set_push_constants( pass.set_push_constants(
ShaderStages::VERTEX, ShaderStages::VERTEX,
0, 0,

View file

@ -5,7 +5,6 @@
forward_io::{Vertex, VertexOutput}, forward_io::{Vertex, VertexOutput},
view_transformations::position_world_to_clip, view_transformations::position_world_to_clip,
} }
#import bevy_render::instance_index::get_instance_index
#ifdef MORPH_TARGETS #ifdef MORPH_TARGETS
fn morph_vertex(vertex_in: Vertex) -> Vertex { fn morph_vertex(vertex_in: Vertex) -> Vertex {
@ -54,7 +53,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
vertex.normal, vertex.normal,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
get_instance_index(vertex_no_morph.instance_index) vertex_no_morph.instance_index
); );
#endif #endif
#endif #endif
@ -78,7 +77,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
vertex.tangent, vertex.tangent,
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
get_instance_index(vertex_no_morph.instance_index) vertex_no_morph.instance_index
); );
#endif #endif
@ -89,14 +88,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef VERTEX_OUTPUT_INSTANCE_INDEX #ifdef VERTEX_OUTPUT_INSTANCE_INDEX
// Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug.
// See https://github.com/gfx-rs/naga/issues/2416 // See https://github.com/gfx-rs/naga/issues/2416
out.instance_index = get_instance_index(vertex_no_morph.instance_index); out.instance_index = vertex_no_morph.instance_index;
#endif
#ifdef BASE_INSTANCE_WORKAROUND
// Hack: this ensures the push constant is always used, which works around this issue:
// https://github.com/bevyengine/bevy/issues/10509
// This can be removed when wgpu 0.19 is released
out.position.x += min(f32(get_instance_index(0u)), 0.0);
#endif #endif
return out; return out;

View file

@ -6,17 +6,15 @@
mesh_types::MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT, mesh_types::MESH_FLAGS_SIGN_DETERMINANT_MODEL_3X3_BIT,
view_transformations::position_world_to_clip, view_transformations::position_world_to_clip,
} }
#import bevy_render::{ #import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack}
instance_index::get_instance_index,
maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack},
}
fn get_model_matrix(instance_index: u32) -> mat4x4<f32> { fn get_model_matrix(instance_index: u32) -> mat4x4<f32> {
return affine_to_square(mesh[get_instance_index(instance_index)].model); return affine_to_square(mesh[instance_index].model);
} }
fn get_previous_model_matrix(instance_index: u32) -> mat4x4<f32> { fn get_previous_model_matrix(instance_index: u32) -> mat4x4<f32> {
return affine_to_square(mesh[get_instance_index(instance_index)].previous_model); return affine_to_square(mesh[instance_index].previous_model);
} }
fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> { fn mesh_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {

View file

@ -21,9 +21,13 @@ use bevy_render::{
view::{Msaa, ViewUniform, ViewUniforms}, view::{Msaa, ViewUniform, ViewUniforms},
}; };
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
use bevy_render::render_resource::binding_types::texture_cube; use bevy_render::render_resource::binding_types::texture_cube;
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array}; use bevy_render::render_resource::binding_types::{texture_2d_array, texture_cube_array};
use crate::{ use crate::{
@ -182,9 +186,13 @@ fn layout_entries(
// Point Shadow Texture Cube Array // Point Shadow Texture Cube Array
( (
2, 2,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
texture_cube_array(TextureSampleType::Depth), texture_cube_array(TextureSampleType::Depth),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
texture_cube(TextureSampleType::Depth), texture_cube(TextureSampleType::Depth),
), ),
// Point Shadow Texture Array Sampler // Point Shadow Texture Array Sampler
@ -192,9 +200,13 @@ fn layout_entries(
// Directional Shadow Texture Array // Directional Shadow Texture Array
( (
4, 4,
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
texture_2d_array(TextureSampleType::Depth), texture_2d_array(TextureSampleType::Depth),
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
texture_2d(TextureSampleType::Depth), texture_2d(TextureSampleType::Depth),
), ),
// Directional Shadow Texture Array Sampler // Directional Shadow Texture Array Sampler

View file

@ -340,6 +340,7 @@ impl FromWorld for SsaoPipelines {
usage: TextureUsages::TEXTURE_BINDING, usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[], view_formats: &[],
}), }),
TextureDataOrder::default(),
bytemuck::cast_slice(&generate_hilbert_index_lut()), bytemuck::cast_slice(&generate_hilbert_index_lut()),
) )
.create_view(&TextureViewDescriptor::default()); .create_view(&TextureViewDescriptor::default());

View file

@ -32,6 +32,7 @@ tracing-tracy = []
wgpu_trace = ["wgpu/trace"] wgpu_trace = ["wgpu/trace"]
ci_limits = [] ci_limits = []
webgl = ["wgpu/webgl"] webgl = ["wgpu/webgl"]
webgpu = ["wgpu/webgpu"]
[dependencies] [dependencies]
# bevy # bevy
@ -62,12 +63,16 @@ image = { version = "0.24", default-features = false }
codespan-reporting = "0.11.0" codespan-reporting = "0.11.0"
# `fragile-send-sync-non-atomic-wasm` feature means we can't use WASM threads for rendering # `fragile-send-sync-non-atomic-wasm` feature means we can't use WASM threads for rendering
# It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm # It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm
wgpu = { version = "0.18", features = [ wgpu = { version = "0.19.1", default-features = false, features = [
"wgsl",
"dx12",
"metal",
"naga", "naga",
"naga-ir",
"fragile-send-sync-non-atomic-wasm", "fragile-send-sync-non-atomic-wasm",
] } ] }
naga = { version = "0.14.2", features = ["wgsl-in"] } naga = { version = "0.19", features = ["wgsl-in"] }
naga_oil = { version = "0.11", default-features = false, features = [ naga_oil = { version = "0.12", default-features = false, features = [
"test_shader", "test_shader",
] } ] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }

View file

@ -56,7 +56,7 @@ pub struct GlobalsUniform {
/// It wraps to zero when it reaches the maximum value of a u32. /// It wraps to zero when it reaches the maximum value of a u32.
frame_count: u32, frame_count: u32,
/// WebGL2 structs must be 16 byte aligned. /// WebGL2 structs must be 16 byte aligned.
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
_wasm_padding: f32, _wasm_padding: f32,
} }

View file

@ -1,17 +0,0 @@
#define_import_path bevy_render::instance_index
#ifdef BASE_INSTANCE_WORKAROUND
// naga and wgpu should polyfill WGSL instance_index functionality where it is
// not available in GLSL. Until that is done, we can work around it in bevy
// using a push constant which is converted to a uniform by naga and wgpu.
// https://github.com/gfx-rs/wgpu/issues/1573
var<push_constant> base_instance: i32;
fn get_instance_index(instance_index: u32) -> u32 {
return u32(base_instance) + instance_index;
}
#else
fn get_instance_index(instance_index: u32) -> u32 {
return instance_index;
}
#endif

View file

@ -259,11 +259,12 @@ impl Plugin for RenderPlugin {
flags: settings.instance_flags, flags: settings.instance_flags,
gles_minor_version: settings.gles3_minor_version, gles_minor_version: settings.gles3_minor_version,
}); });
// SAFETY: Plugins should be set up on the main thread. // SAFETY: Plugins should be set up on the main thread.
let surface = primary_window.map(|wrapper| unsafe { let surface = primary_window.map(|wrapper| unsafe {
let handle = wrapper.get_handle(); let handle = wrapper.get_handle();
instance instance
.create_surface(&handle) .create_surface(handle)
.expect("Failed to create wgpu surface") .expect("Failed to create wgpu surface")
}); });
@ -332,16 +333,6 @@ impl Plugin for RenderPlugin {
} }
fn finish(&self, app: &mut App) { fn finish(&self, app: &mut App) {
load_internal_asset!(
app,
INSTANCE_INDEX_SHADER_HANDLE,
"instance_index.wgsl",
Shader::from_wgsl_with_defs,
vec![
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
"BASE_INSTANCE_WORKAROUND".into()
]
);
load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl); load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
if let Some(future_renderer_resources) = if let Some(future_renderer_resources) =
app.world.remove_resource::<FutureRendererResources>() app.world.remove_resource::<FutureRendererResources>()

View file

@ -15,7 +15,11 @@ use wgpu::{BindingResource, Limits};
// `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum // `max_uniform_buffer_binding_size`. On macOS this ends up being the minimum
// size of the uniform buffer as well as the size of each chunk of data at a // size of the uniform buffer as well as the size of each chunk of data at a
// dynamic offset. // dynamic offset.
#[cfg(any(not(feature = "webgl"), not(target_arch = "wasm32")))] #[cfg(any(
not(feature = "webgl"),
not(target_arch = "wasm32"),
feature = "webgpu"
))]
const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20; const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20;
// WebGL2 quirk: using uniform buffers larger than 4KB will cause extremely // WebGL2 quirk: using uniform buffers larger than 4KB will cause extremely
@ -23,7 +27,7 @@ const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 20;
// This is due to older shader compilers/GPUs that don't support dynamically // This is due to older shader compilers/GPUs that don't support dynamically
// indexing uniform buffers, and instead emulate it with large switch statements // indexing uniform buffers, and instead emulate it with large switch statements
// over buffer indices that take a long time to compile. // over buffer indices that take a long time to compile.
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12; const MAX_REASONABLE_UNIFORM_BUFFER_BINDING_SIZE: u32 = 1 << 12;
/// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size) /// Similar to [`DynamicUniformBuffer`], except every N elements (depending on size)

View file

@ -32,7 +32,7 @@ pub use uniform_buffer::*;
// TODO: decide where re-exports should go // TODO: decide where re-exports should go
pub use wgpu::{ pub use wgpu::{
util::{BufferInitDescriptor, DrawIndexedIndirect, DrawIndirect}, util::{BufferInitDescriptor, DrawIndexedIndirectArgs, DrawIndirectArgs, TextureDataOrder},
AdapterInfo as WgpuAdapterInfo, AddressMode, BindGroupDescriptor, BindGroupEntry, AdapterInfo as WgpuAdapterInfo, AddressMode, BindGroupDescriptor, BindGroupEntry,
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent,
BlendFactor, BlendOperation, BlendState, BufferAddress, BufferAsyncError, BufferBinding, BlendFactor, BlendOperation, BlendState, BufferAddress, BufferAsyncError, BufferBinding,

View file

@ -277,7 +277,7 @@ impl ShaderCache {
Entry::Occupied(entry) => entry.into_mut(), Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let mut shader_defs = shader_defs.to_vec(); let mut shader_defs = shader_defs.to_vec();
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
{ {
shader_defs.push("NO_ARRAY_TEXTURES_SUPPORT".into()); shader_defs.push("NO_ARRAY_TEXTURES_SUPPORT".into());
shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into()); shader_defs.push("SIXTEEN_BYTE_ALIGNMENT".into());

View file

@ -133,7 +133,7 @@ const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") {
pub async fn initialize_renderer( pub async fn initialize_renderer(
instance: &Instance, instance: &Instance,
options: &WgpuSettings, options: &WgpuSettings,
request_adapter_options: &RequestAdapterOptions<'_>, request_adapter_options: &RequestAdapterOptions<'_, '_>,
) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) { ) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) {
let adapter = instance let adapter = instance
.request_adapter(request_adapter_options) .request_adapter(request_adapter_options)
@ -280,8 +280,8 @@ pub async fn initialize_renderer(
.request_device( .request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
label: options.device_label.as_ref().map(|a| a.as_ref()), label: options.device_label.as_ref().map(|a| a.as_ref()),
features, required_features: features,
limits, required_limits: limits,
}, },
trace_path, trace_path,
) )

View file

@ -5,7 +5,7 @@ use crate::render_resource::{
use bevy_ecs::system::Resource; use bevy_ecs::system::Resource;
use wgpu::{ use wgpu::{
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult,
}; };
use super::RenderQueue; use super::RenderQueue;
@ -61,7 +61,7 @@ impl RenderDevice {
/// ///
/// no-op on the web, device is automatically polled. /// no-op on the web, device is automatically polled.
#[inline] #[inline]
pub fn poll(&self, maintain: wgpu::Maintain) -> bool { pub fn poll(&self, maintain: wgpu::Maintain) -> MaintainResult {
self.device.poll(maintain) self.device.poll(maintain)
} }
@ -161,11 +161,12 @@ impl RenderDevice {
&self, &self,
render_queue: &RenderQueue, render_queue: &RenderQueue,
desc: &wgpu::TextureDescriptor, desc: &wgpu::TextureDescriptor,
order: wgpu::util::TextureDataOrder,
data: &[u8], data: &[u8],
) -> Texture { ) -> Texture {
let wgpu_texture = self let wgpu_texture =
.device self.device
.create_texture_with_data(render_queue.as_ref(), desc, data); .create_texture_with_data(render_queue.as_ref(), desc, order, data);
Texture::from(wgpu_texture) Texture::from(wgpu_texture)
} }

View file

@ -54,8 +54,14 @@ pub struct WgpuSettings {
impl Default for WgpuSettings { impl Default for WgpuSettings {
fn default() -> Self { fn default() -> Self {
let default_backends = if cfg!(all(feature = "webgl", target_arch = "wasm32")) { let default_backends = if cfg!(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
Backends::GL Backends::GL
} else if cfg!(all(feature = "webgpu", target_arch = "wasm32")) {
Backends::BROWSER_WEBGPU
} else { } else {
Backends::all() Backends::all()
}; };
@ -67,8 +73,11 @@ impl Default for WgpuSettings {
let priority = settings_priority_from_env().unwrap_or(WgpuSettingsPriority::Functionality); let priority = settings_priority_from_env().unwrap_or(WgpuSettingsPriority::Functionality);
let limits = if cfg!(all(feature = "webgl", target_arch = "wasm32")) let limits = if cfg!(all(
|| matches!(priority, WgpuSettingsPriority::WebGL2) feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) || matches!(priority, WgpuSettingsPriority::WebGL2)
{ {
wgpu::Limits::downlevel_webgl2_defaults() wgpu::Limits::downlevel_webgl2_defaults()
} else { } else {

View file

@ -98,7 +98,12 @@ fn fallback_image_new(
} }
let texture = if create_texture_with_data { let texture = if create_texture_with_data {
render_device.create_texture_with_data(render_queue, &image.texture_descriptor, &image.data) render_device.create_texture_with_data(
render_queue,
&image.texture_descriptor,
wgpu::util::TextureDataOrder::default(),
&image.data,
)
} else { } else {
render_device.create_texture(&image.texture_descriptor) render_device.create_texture(&image.texture_descriptor)
}; };

View file

@ -793,7 +793,7 @@ impl TextureFormatPixelInfo for TextureFormat {
fn pixel_size(&self) -> usize { fn pixel_size(&self) -> usize {
let info = self; let info = self;
match info.block_dimensions() { match info.block_dimensions() {
(1, 1) => info.block_size(None).unwrap() as usize, (1, 1) => info.block_copy_size(None).unwrap() as usize,
_ => panic!("Using pixel_size for compressed textures is invalid"), _ => panic!("Using pixel_size for compressed textures is invalid"),
} }
} }
@ -831,6 +831,8 @@ impl RenderAsset for Image {
let texture = render_device.create_texture_with_data( let texture = render_device.create_texture_with_data(
render_queue, render_queue,
&self.texture_descriptor, &self.texture_descriptor,
// TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file?
wgpu::util::TextureDataOrder::default(),
&self.data, &self.data,
); );

View file

@ -160,7 +160,7 @@ pub fn ktx2_buffer_to_image(
texture_format_info.block_dimensions().1, texture_format_info.block_dimensions().1,
); );
// Texture is not a depth or stencil format, it is possible to pass `None` and unwrap // Texture is not a depth or stencil format, it is possible to pass `None` and unwrap
let block_bytes = texture_format_info.block_size(None).unwrap(); let block_bytes = texture_format_info.block_copy_size(None).unwrap();
let transcoder = LowLevelUastcTranscoder::new(); let transcoder = LowLevelUastcTranscoder::new();
for (level, level_data) in levels.iter().enumerate() { for (level, level_data) in levels.iter().enumerate() {
@ -240,7 +240,7 @@ pub fn ktx2_buffer_to_image(
texture_format_info.block_dimensions().1 as usize, texture_format_info.block_dimensions().1 as usize,
); );
// Texture is not a depth or stencil format, it is possible to pass `None` and unwrap // Texture is not a depth or stencil format, it is possible to pass `None` and unwrap
let block_bytes = texture_format_info.block_size(None).unwrap() as usize; let block_bytes = texture_format_info.block_copy_size(None).unwrap() as usize;
let mut wgpu_data = vec![Vec::default(); (layer_count * face_count) as usize]; let mut wgpu_data = vec![Vec::default(); (layer_count * face_count) as usize];
for (level, level_data) in levels.iter().enumerate() { for (level, level_data) in levels.iter().enumerate() {

View file

@ -16,7 +16,9 @@ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::PoisonError, sync::PoisonError,
}; };
use wgpu::{BufferUsages, TextureFormat, TextureUsages, TextureViewDescriptor}; use wgpu::{
BufferUsages, SurfaceTargetUnsafe, TextureFormat, TextureUsages, TextureViewDescriptor,
};
pub mod screenshot; pub mod screenshot;
@ -193,7 +195,8 @@ fn extract_windows(
} }
struct SurfaceData { struct SurfaceData {
surface: wgpu::Surface, // TODO: what lifetime should this be?
surface: wgpu::Surface<'static>,
format: TextureFormat, format: TextureFormat,
} }
@ -253,12 +256,16 @@ pub fn prepare_windows(
.surfaces .surfaces
.entry(window.entity) .entry(window.entity)
.or_insert_with(|| { .or_insert_with(|| {
let surface_target = SurfaceTargetUnsafe::RawHandle {
raw_display_handle: window.handle.display_handle,
raw_window_handle: window.handle.window_handle,
};
// SAFETY: The window handles in ExtractedWindows will always be valid objects to create surfaces on // SAFETY: The window handles in ExtractedWindows will always be valid objects to create surfaces on
let surface = unsafe { let surface = unsafe {
// NOTE: On some OSes this MUST be called from the main thread. // NOTE: On some OSes this MUST be called from the main thread.
// As of wgpu 0.15, only fallible if the given window is a HTML canvas and obtaining a WebGPU or WebGL2 context fails. // As of wgpu 0.15, only fallible if the given window is a HTML canvas and obtaining a WebGPU or WebGL2 context fails.
render_instance render_instance
.create_surface(&window.handle.get_handle()) .create_surface_unsafe(surface_target)
.expect("Failed to create wgpu surface") .expect("Failed to create wgpu surface")
}; };
let caps = surface.get_capabilities(&render_adapter); let caps = surface.get_capabilities(&render_adapter);
@ -293,6 +300,12 @@ pub fn prepare_windows(
PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync, PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync,
PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync,
}, },
// TODO: Expose this as a setting somewhere
// 2 is wgpu's default/what we've been using so far.
// 1 is the minimum, but may cause lower framerates due to the cpu waiting for the gpu to finish
// all work for the previous frame before starting work on the next frame, which then means the gpu
// has to wait for the cpu to finish to start on the next frame.
desired_maximum_frame_latency: 2,
alpha_mode: match window.alpha_mode { alpha_mode: match window.alpha_mode {
CompositeAlphaMode::Auto => wgpu::CompositeAlphaMode::Auto, CompositeAlphaMode::Auto => wgpu::CompositeAlphaMode::Auto,
CompositeAlphaMode::Opaque => wgpu::CompositeAlphaMode::Opaque, CompositeAlphaMode::Opaque => wgpu::CompositeAlphaMode::Opaque,
@ -347,6 +360,7 @@ pub fn prepare_windows(
let may_erroneously_timeout = || { let may_erroneously_timeout = || {
render_instance render_instance
.enumerate_adapters(wgpu::Backends::VULKAN) .enumerate_adapters(wgpu::Backends::VULKAN)
.iter()
.any(|adapter| { .any(|adapter| {
let name = adapter.get_info().name; let name = adapter.get_info().name;
name.starts_with("Radeon") name.starts_with("Radeon")

View file

@ -10,6 +10,7 @@ keywords = ["bevy"]
[features] [features]
webgl = [] webgl = []
webgpu = []
[dependencies] [dependencies]
# bevy # bevy

View file

@ -511,7 +511,11 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
false => TextureFormat::bevy_default(), false => TextureFormat::bevy_default(),
}; };
let mut push_constant_ranges = Vec::with_capacity(1); let mut push_constant_ranges = Vec::with_capacity(1);
if cfg!(all(feature = "webgl", target_arch = "wasm32")) { if cfg!(all(
feature = "webgl",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
push_constant_ranges.push(PushConstantRange { push_constant_ranges.push(PushConstantRange {
stages: ShaderStages::VERTEX, stages: ShaderStages::VERTEX,
range: 0..4, range: 0..4,
@ -688,7 +692,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
let batch_range = item.batch_range(); let batch_range = item.batch_range();
#[cfg(all(feature = "webgl", target_arch = "wasm32"))] #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
pass.set_push_constants( pass.set_push_constants(
ShaderStages::VERTEX, ShaderStages::VERTEX,
0, 0,

View file

@ -4,13 +4,10 @@
mesh2d_view_bindings::view, mesh2d_view_bindings::view,
mesh2d_bindings::mesh, mesh2d_bindings::mesh,
} }
#import bevy_render::{ #import bevy_render::maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack}
instance_index::get_instance_index,
maths::{affine_to_square, mat2x4_f32_to_mat3x3_unpack},
}
fn get_model_matrix(instance_index: u32) -> mat4x4<f32> { fn get_model_matrix(instance_index: u32) -> mat4x4<f32> {
return affine_to_square(mesh[get_instance_index(instance_index)].model); return affine_to_square(mesh[instance_index].model);
} }
fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> { fn mesh2d_position_local_to_world(model: mat4x4<f32>, vertex_position: vec4<f32>) -> vec4<f32> {
@ -31,8 +28,8 @@ fn mesh2d_position_local_to_clip(model: mat4x4<f32>, vertex_position: vec4<f32>)
fn mesh2d_normal_local_to_world(vertex_normal: vec3<f32>, instance_index: u32) -> vec3<f32> { fn mesh2d_normal_local_to_world(vertex_normal: vec3<f32>, instance_index: u32) -> vec3<f32> {
return mat2x4_f32_to_mat3x3_unpack( return mat2x4_f32_to_mat3x3_unpack(
mesh[get_instance_index(instance_index)].inverse_transpose_model_a, mesh[instance_index].inverse_transpose_model_a,
mesh[get_instance_index(instance_index)].inverse_transpose_model_b, mesh[instance_index].inverse_transpose_model_b,
) * vertex_normal; ) * vertex_normal;
} }

View file

@ -27,7 +27,7 @@ bevy_input = { path = "../bevy_input", version = "0.12.0" }
# other # other
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }
raw-window-handle = "0.5" raw-window-handle = "0.6"
smol_str = "0.2" smol_str = "0.2"
[lints] [lints]

View file

@ -1,6 +1,7 @@
use bevy_ecs::prelude::Component; use bevy_ecs::prelude::Component;
use raw_window_handle::{ use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
RawWindowHandle, WindowHandle,
}; };
/// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads. /// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads.
@ -17,7 +18,7 @@ pub struct RawHandleWrapper {
} }
impl RawHandleWrapper { impl RawHandleWrapper {
/// Returns a [`HasRawWindowHandle`] + [`HasRawDisplayHandle`] impl, which exposes [`RawWindowHandle`] and [`RawDisplayHandle`]. /// Returns a [`HasWindowHandle`] + [`HasDisplayHandle`] impl, which exposes [`WindowHandle`] and [`DisplayHandle`].
/// ///
/// # Safety /// # Safety
/// ///
@ -46,26 +47,26 @@ unsafe impl Sync for RawHandleWrapper {}
/// In many cases, this should only be constructed on the main thread. /// In many cases, this should only be constructed on the main thread.
pub struct ThreadLockedRawWindowHandleWrapper(RawHandleWrapper); pub struct ThreadLockedRawWindowHandleWrapper(RawHandleWrapper);
// SAFETY: the caller has validated that this is a valid context to get [`RawHandleWrapper`] impl HasWindowHandle for ThreadLockedRawWindowHandleWrapper {
// as otherwise an instance of this type could not have been constructed fn window_handle(&self) -> Result<WindowHandle, HandleError> {
// NOTE: we cannot simply impl HasRawWindowHandle for RawHandleWrapper, // SAFETY: the caller has validated that this is a valid context to get [`RawHandleWrapper`]
// as the `raw_window_handle` method is safe. We cannot guarantee that all calls // as otherwise an instance of this type could not have been constructed
// of this method are correct (as it may be off the main thread on an incompatible platform), // NOTE: we cannot simply impl HasRawWindowHandle for RawHandleWrapper,
// and so exposing a safe method to get a [`RawWindowHandle`] directly would be UB. // as the `raw_window_handle` method is safe. We cannot guarantee that all calls
unsafe impl HasRawWindowHandle for ThreadLockedRawWindowHandleWrapper { // of this method are correct (as it may be off the main thread on an incompatible platform),
fn raw_window_handle(&self) -> RawWindowHandle { // and so exposing a safe method to get a [`RawWindowHandle`] directly would be UB.
self.0.window_handle Ok(unsafe { WindowHandle::borrow_raw(self.0.window_handle) })
} }
} }
// SAFETY: the caller has validated that this is a valid context to get [`RawDisplayHandle`] impl HasDisplayHandle for ThreadLockedRawWindowHandleWrapper {
// as otherwise an instance of this type could not have been constructed fn display_handle(&self) -> Result<DisplayHandle, HandleError> {
// NOTE: we cannot simply impl HasRawDisplayHandle for RawHandleWrapper, // SAFETY: the caller has validated that this is a valid context to get [`RawDisplayHandle`]
// as the `raw_display_handle` method is safe. We cannot guarantee that all calls // as otherwise an instance of this type could not have been constructed
// of this method are correct (as it may be off the main thread on an incompatible platform), // NOTE: we cannot simply impl HasRawDisplayHandle for RawHandleWrapper,
// and so exposing a safe method to get a [`RawDisplayHandle`] directly would be UB. // as the `raw_display_handle` method is safe. We cannot guarantee that all calls
unsafe impl HasRawDisplayHandle for ThreadLockedRawWindowHandleWrapper { // of this method are correct (as it may be off the main thread on an incompatible platform),
fn raw_display_handle(&self) -> RawDisplayHandle { // and so exposing a safe method to get a [`RawDisplayHandle`] directly would be UB.
self.0.display_handle Ok(unsafe { DisplayHandle::borrow_raw(self.0.display_handle) })
} }
} }

View file

@ -28,19 +28,18 @@ bevy_utils = { path = "../bevy_utils", version = "0.12.0" }
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0" } bevy_tasks = { path = "../bevy_tasks", version = "0.12.0" }
# other # other
# feature rwh_05 refers to window_raw_handle@v0.5, # feature rwh_06 refers to window_raw_handle@v0.6
# updating to rwh_06 is blocked until wgpu 0.19 release lands. winit = { version = "0.29", default-features = false, features = ["rwh_06"] }
winit = { version = "0.29", default-features = false, features = ["rwh_05"] }
accesskit_winit = { version = "0.17", default-features = false, features = [ accesskit_winit = { version = "0.17", default-features = false, features = [
"rwh_05", "rwh_06",
] } ] }
approx = { version = "0.5", default-features = false } approx = { version = "0.5", default-features = false }
raw-window-handle = "0.5" raw-window-handle = "0.6"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
winit = { version = "0.29", default-features = false, features = [ winit = { version = "0.29", default-features = false, features = [
"android-native-activity", "android-native-activity",
"rwh_05", "rwh_06",
] } ] }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]

View file

@ -797,7 +797,7 @@ pub fn winit_runner(mut app: App) {
.world .world
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>(); .query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
if let Ok((entity, window)) = query.get_single(&app.world) { if let Ok((entity, window)) = query.get_single(&app.world) {
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
let window = window.clone(); let window = window.clone();
let ( let (
@ -820,8 +820,8 @@ pub fn winit_runner(mut app: App) {
); );
let wrapper = RawHandleWrapper { let wrapper = RawHandleWrapper {
window_handle: winit_window.raw_window_handle(), window_handle: winit_window.window_handle().unwrap().as_raw(),
display_handle: winit_window.raw_display_handle(), display_handle: winit_window.display_handle().unwrap().as_raw(),
}; };
app.world.entity_mut(entity).insert(wrapper); app.world.entity_mut(entity).insert(wrapper);

View file

@ -12,8 +12,8 @@ use bevy_utils::{
EntityHashMap, EntityHashMap,
}; };
use bevy_window::{RawHandleWrapper, Window, WindowClosed, WindowCreated}; use bevy_window::{RawHandleWrapper, Window, WindowClosed, WindowCreated};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use winit::{ use winit::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
event_loop::EventLoopWindowTarget, event_loop::EventLoopWindowTarget,
@ -74,8 +74,8 @@ pub(crate) fn create_windows<'a>(
commands commands
.entity(entity) .entity(entity)
.insert(RawHandleWrapper { .insert(RawHandleWrapper {
window_handle: winit_window.raw_window_handle(), window_handle: winit_window.window_handle().unwrap().as_raw(),
display_handle: winit_window.raw_display_handle(), display_handle: winit_window.display_handle().unwrap().as_raw(),
}) })
.insert(CachedWindow { .insert(CachedWindow {
window: window.clone(), window: window.clone(),

View file

@ -238,8 +238,7 @@ ruby -run -ehttpd examples/wasm
Bevy support for WebGPU is being worked on, but is currently experimental. Bevy support for WebGPU is being worked on, but is currently experimental.
To build for WebGPU, you'll need to disable default features and add all those you need, making sure to omit the `webgl2` feature. To build for WebGPU, you'll need to enable the `webgpu` feature. This will override the `webgl2` feature, and builds with the `webgpu` feature enabled won't be able to run on browsers that don't support WebGPU.
WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example: WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example:
```sh ```sh

View file

@ -34,7 +34,7 @@ The default feature set enables most of the expected features of a game engine,
|png|PNG image format support| |png|PNG image format support|
|tonemapping_luts|Include tonemapping Look Up Tables KTX2 files. If everything is pink, you need to enable this feature or change the `Tonemapping` method on your `Camera2dBundle` or `Camera3dBundle`.| |tonemapping_luts|Include tonemapping Look Up Tables KTX2 files. If everything is pink, you need to enable this feature or change the `Tonemapping` method on your `Camera2dBundle` or `Camera3dBundle`.|
|vorbis|OGG/VORBIS audio format support| |vorbis|OGG/VORBIS audio format support|
|webgl2|Enable some limitations to be able to use WebGL2. If not enabled, it will default to WebGPU in Wasm. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.| |webgl2|Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.|
|x11|X11 display server support| |x11|X11 display server support|
|zstd|For KTX2 supercompression| |zstd|For KTX2 supercompression|
@ -80,6 +80,7 @@ The default feature set enables most of the expected features of a game engine,
|trace_tracy_memory|Tracing support, with memory profiling, exposing a port for Tracy| |trace_tracy_memory|Tracing support, with memory profiling, exposing a port for Tracy|
|wav|WAV audio format support| |wav|WAV audio format support|
|wayland|Wayland display server support| |wayland|Wayland display server support|
|webgpu|Enable support for WebGPU in Wasm. When enabled, this feature will override the `webgl2` feature and you won't be able to run Wasm builds with WebGL2, only with WebGPU. Requires the `RUSTFLAGS` environment variable to be set to `--cfg=web_sys_unstable_apis` when building.|
|webp|WebP image format support| |webp|WebP image format support|
|wgpu_trace|Save a trace of all wgpu calls| |wgpu_trace|Save a trace of all wgpu calls|
|zlib|For KTX2 supercompression| |zlib|For KTX2 supercompression|

View file

@ -562,8 +562,7 @@ ruby -run -ehttpd examples/wasm
Bevy support for WebGPU is being worked on, but is currently experimental. Bevy support for WebGPU is being worked on, but is currently experimental.
To build for WebGPU, you'll need to disable default features and add all those you need, making sure to omit the `webgl2` feature. To build for WebGPU, you'll need to enable the `webgpu` feature. This will override the `webgl2` feature, and builds with the `webgpu` feature enabled won't be able to run on browsers that don't support WebGPU.
WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example: WebGPU depends on unstable APIs so you will also need to pass the `web_sys_unstable_apis` flag to your builds. For example:
```sh ```sh

View file

@ -44,7 +44,7 @@ fn main() {
assert!(!cli.examples.is_empty(), "must have at least one example"); assert!(!cli.examples.is_empty(), "must have at least one example");
let mut default_features = true; let default_features = true;
let mut features: Vec<&str> = cli.features.iter().map(|f| f.as_str()).collect(); let mut features: Vec<&str> = cli.features.iter().map(|f| f.as_str()).collect();
if let Some(frames) = cli.frames { if let Some(frames) = cli.frames {
let mut file = File::create("ci_testing_config.ron").unwrap(); let mut file = File::create("ci_testing_config.ron").unwrap();
@ -56,30 +56,7 @@ fn main() {
match cli.api { match cli.api {
WebApi::Webgl2 => (), WebApi::Webgl2 => (),
WebApi::Webgpu => { WebApi::Webgpu => {
features.push("animation"); features.push("webgpu");
features.push("bevy_asset");
features.push("bevy_audio");
features.push("bevy_gilrs");
features.push("bevy_scene");
features.push("bevy_winit");
features.push("bevy_core_pipeline");
features.push("bevy_pbr");
features.push("bevy_gltf");
features.push("bevy_render");
features.push("bevy_sprite");
features.push("bevy_text");
features.push("bevy_ui");
features.push("png");
features.push("hdr");
features.push("ktx2");
features.push("zstd");
features.push("vorbis");
features.push("x11");
features.push("bevy_gizmos");
features.push("android_shared_stdcxx");
features.push("tonemapping_luts");
features.push("default_font");
default_features = false;
} }
} }