diff --git a/docs/src/development-documents/architecture.md b/docs/src/development-documents/architecture.md
index 8f67e2ff..1f62a256 100644
--- a/docs/src/development-documents/architecture.md
+++ b/docs/src/development-documents/architecture.md
@@ -10,6 +10,10 @@ A simplified version is shown below:

+A further simplified version:
+
+
+
Notes:
* wgpu is able to create an interface through which we can reach any device with a GPU.
diff --git a/docs/src/development-documents/figures/simplified-render-stack.drawio.svg b/docs/src/development-documents/figures/simplified-render-stack.drawio.svg
new file mode 100644
index 00000000..c5f5d4c8
--- /dev/null
+++ b/docs/src/development-documents/figures/simplified-render-stack.drawio.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/maplibre-winit/Cargo.toml b/maplibre-winit/Cargo.toml
index 9f67bc6c..26a60e34 100644
--- a/maplibre-winit/Cargo.toml
+++ b/maplibre-winit/Cargo.toml
@@ -20,8 +20,8 @@ winit = { version = "0.27.2", default-features = false, features = ["x11", "wayl
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3.58", features = ["Window"] }
-wasm-bindgen = "0.2.81"
-wasm-bindgen-futures = "0.4.31"
+wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4"
[dependencies]
maplibre = { path = "../maplibre", version = "0.1.0" }
diff --git a/maplibre/Cargo.toml b/maplibre/Cargo.toml
index e7960ceb..86142dfe 100644
--- a/maplibre/Cargo.toml
+++ b/maplibre/Cargo.toml
@@ -55,7 +55,8 @@ geozero = { version = "0.9.7", default-features = false, features = ["with-mvt",
tile-grid = "0.3.0"
# Rendering
-wgpu = "0.14.0"
+wgpu = "0.15.0"
+# wgpu = { git = "https://github.com/gfx-rs/wgpu" }
lyon = { version = "1.0.0", features = [] }
raw-window-handle = "0.5.0"
diff --git a/maplibre/src/raster/resource_system.rs b/maplibre/src/raster/resource_system.rs
index 008535c3..56fa0ab0 100644
--- a/maplibre/src/raster/resource_system.rs
+++ b/maplibre/src/raster/resource_system.rs
@@ -46,7 +46,7 @@ pub fn resource_system(
false,
false,
false,
- true,
+ surface.is_multisampling_supported(settings.msaa),
true,
)
.describe_render_pipeline()
diff --git a/maplibre/src/render/error.rs b/maplibre/src/render/error.rs
index 2d866493..affb0966 100644
--- a/maplibre/src/render/error.rs
+++ b/maplibre/src/render/error.rs
@@ -6,6 +6,8 @@ use crate::render::graph::RenderGraphError;
pub enum RenderError {
#[error("error in surface")]
Surface(#[from] wgpu::SurfaceError),
+ #[error("error during surface creation")]
+ CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
#[error("error in render graph")]
Graph(#[from] RenderGraphError),
#[error("error while requesting device")]
diff --git a/maplibre/src/render/mod.rs b/maplibre/src/render/mod.rs
index 95c99fd4..f4d12afd 100644
--- a/maplibre/src/render/mod.rs
+++ b/maplibre/src/render/mod.rs
@@ -124,11 +124,15 @@ impl RenderResources {
}
}
- pub fn recreate_surface(&mut self, window: &MW, instance: &wgpu::Instance)
+ pub fn recreate_surface(
+ &mut self,
+ window: &MW,
+ instance: &wgpu::Instance,
+ ) -> Result<(), RenderError>
where
MW: MapWindow + HeadedMapWindow,
{
- self.surface.recreate::(window, instance);
+ self.surface.recreate::(window, instance)
}
pub fn surface(&self) -> &Surface {
@@ -160,9 +164,12 @@ impl Renderer {
where
MW: MapWindow + HeadedMapWindow,
{
- let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+ backends: wgpu_settings.backends.unwrap_or(wgpu::Backends::all()),
+ dx12_shader_compiler: Default::default(),
+ });
- let surface: wgpu::Surface = unsafe { instance.create_surface(window.raw()) };
+ let surface: wgpu::Surface = unsafe { instance.create_surface(window.raw())? };
let (adapter, device, queue) = Self::request_device(
&instance,
@@ -202,7 +209,10 @@ impl Renderer {
where
MW: MapWindow,
{
- let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+ backends: wgpu_settings.backends.unwrap_or(wgpu::Backends::all()),
+ dx12_shader_compiler: Default::default(),
+ });
let (adapter, device, queue) = Self::request_device(
&instance,
@@ -300,6 +310,9 @@ impl Renderer {
max_bind_groups: limits
.max_bind_groups
.min(constrained_limits.max_bind_groups),
+ max_bindings_per_bind_group: limits
+ .max_bindings_per_bind_group
+ .min(constrained_limits.max_bindings_per_bind_group),
max_dynamic_uniform_buffers_per_pipeline_layout: limits
.max_dynamic_uniform_buffers_per_pipeline_layout
.min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
@@ -448,7 +461,10 @@ mod tests {
let graph = RenderGraph::default();
let backends = wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::all());
- let instance = wgpu::Instance::new(backends);
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+ backends,
+ dx12_shader_compiler: Default::default(),
+ });
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, backends, None)
.await
.expect("Unable to initialize adapter");
@@ -468,13 +484,14 @@ mod tests {
let render_state = RenderResources::new(Surface::from_image(
&device,
&HeadlessMapWindow {
- size: WindowSize::new(100, 100).unwrap(),
+ size: WindowSize::new(100, 100).expect("invalid headless map size"),
},
&RendererSettings::default(),
));
let world = World::default();
- RenderGraphRunner::run(&graph, &device, &queue, &render_state, &world).unwrap();
+ RenderGraphRunner::run(&graph, &device, &queue, &render_state, &world)
+ .expect("failed to run graph runner");
}
}
@@ -534,7 +551,7 @@ impl Plugin for RenderPlugin {
// Edges
draw_graph
.add_node_edge(input_node_id, draw_graph::node::MAIN_PASS)
- .unwrap();
+ .expect("main pass or draw node does not exist");
graph.add_sub_graph(draw_graph::NAME, draw_graph);
graph.add_node(main_graph::node::MAIN_PASS_DEPENDENCIES, EmptyNode);
@@ -544,7 +561,7 @@ impl Plugin for RenderPlugin {
main_graph::node::MAIN_PASS_DEPENDENCIES,
main_graph::node::MAIN_PASS_DRIVER,
)
- .unwrap();
+ .expect("main pass driver or dependencies do not exist");
// render graph dependency
resources.init::>();
diff --git a/maplibre/src/render/resource/surface.rs b/maplibre/src/render/resource/surface.rs
index b4f33064..c1435714 100644
--- a/maplibre/src/render/resource/surface.rs
+++ b/maplibre/src/render/resource/surface.rs
@@ -3,10 +3,15 @@
use std::{mem::size_of, num::NonZeroU32, sync::Arc};
-use log::debug;
+use wgpu::TextureFormatFeatures;
use crate::{
- render::{eventually::HasChanged, resource::texture::TextureView, settings::RendererSettings},
+ render::{
+ error::RenderError,
+ eventually::HasChanged,
+ resource::texture::TextureView,
+ settings::{Msaa, RendererSettings},
+ },
window::{HeadedMapWindow, MapWindow, WindowSize},
};
@@ -38,8 +43,10 @@ impl BufferDimensions {
pub struct WindowHead {
surface: wgpu::Surface,
size: WindowSize,
- format: wgpu::TextureFormat,
+
+ texture_format: wgpu::TextureFormat,
present_mode: wgpu::PresentMode,
+ texture_format_features: TextureFormatFeatures,
}
impl WindowHead {
@@ -52,20 +59,26 @@ impl WindowHead {
let surface_config = wgpu::SurfaceConfiguration {
alpha_mode: wgpu::CompositeAlphaMode::Auto,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
- format: self.format,
+ format: self.texture_format,
width: self.size.width(),
height: self.size.height(),
present_mode: self.present_mode,
+ view_formats: vec![self.texture_format],
};
self.surface.configure(device, &surface_config);
}
- pub fn recreate_surface(&mut self, window: &MW, instance: &wgpu::Instance)
+ pub fn recreate_surface(
+ &mut self,
+ window: &MW,
+ instance: &wgpu::Instance,
+ ) -> Result<(), RenderError>
where
MW: MapWindow + HeadedMapWindow,
{
- self.surface = unsafe { instance.create_surface(window.raw()) };
+ self.surface = unsafe { instance.create_surface(window.raw())? };
+ Ok(())
}
pub fn surface(&self) -> &wgpu::Surface {
@@ -170,22 +183,25 @@ impl Surface {
{
let size = window.size();
- debug!(
- "supported formats by adapter: {:?}",
- surface.get_supported_formats(adapter)
- );
+ let capabilities = surface.get_capabilities(adapter);
+ log::info!("adapter capabilities on surface: {:?}", capabilities);
- let format = settings
+ let texture_format = settings
.texture_format
- .or_else(|| surface.get_supported_formats(adapter).first().cloned())
+ .or_else(|| capabilities.formats.first().cloned())
.unwrap_or(wgpu::TextureFormat::Rgba8Unorm);
+ log::info!("format description: {:?}", texture_format.describe());
+
+ let texture_format_features = adapter.get_texture_format_features(texture_format);
+ log::info!("format features: {:?}", texture_format_features);
Self {
size,
head: Head::Headed(WindowHead {
surface,
size,
- format,
+ texture_format,
+ texture_format_features,
present_mode: settings.present_mode,
}),
}
@@ -229,6 +245,7 @@ impl Surface {
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
+ view_formats: &[format],
};
let texture = device.create_texture(&texture_descriptor);
@@ -245,7 +262,7 @@ impl Surface {
pub fn surface_format(&self) -> wgpu::TextureFormat {
match &self.head {
- Head::Headed(headed) => headed.format,
+ Head::Headed(headed) => headed.texture_format,
Head::Headless(headless) => headless.texture_format,
}
}
@@ -294,18 +311,23 @@ impl Surface {
}
}
- pub fn recreate(&mut self, window: &MW, instance: &wgpu::Instance)
+ pub fn recreate(
+ &mut self,
+ window: &MW,
+ instance: &wgpu::Instance,
+ ) -> Result<(), RenderError>
where
MW: MapWindow + HeadedMapWindow,
{
match &mut self.head {
Head::Headed(window_head) => {
if window_head.has_changed(&(self.size.width(), self.size.height())) {
- window_head.recreate_surface(window, instance);
+ window_head.recreate_surface(window, instance)?;
}
}
Head::Headless(_) => {}
}
+ Ok(())
}
pub fn head(&self) -> &Head {
@@ -315,6 +337,31 @@ impl Surface {
pub fn head_mut(&mut self) -> &mut Head {
&mut self.head
}
+
+ pub fn is_multisampling_supported(&self, msaa: Msaa) -> bool {
+ match &self.head {
+ Head::Headed(headed) => {
+ let max_sample_count = {
+ let flags = headed.texture_format_features.flags;
+ if flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X8) {
+ 8
+ } else if flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X4) {
+ 4
+ } else if flags.contains(wgpu::TextureFormatFeatureFlags::MULTISAMPLE_X2) {
+ 2
+ } else {
+ 1
+ }
+ };
+ let is_supported = msaa.samples <= max_sample_count;
+ if !is_supported {
+ log::debug!("Multisampling is not supported on surface");
+ }
+ is_supported
+ }
+ Head::Headless(_) => false, // TODO: support multisampling on headless
+ }
+ }
}
impl HasChanged for WindowHead {
diff --git a/maplibre/src/render/resource/texture.rs b/maplibre/src/render/resource/texture.rs
index a06cb5f1..cec9957d 100644
--- a/maplibre/src/render/resource/texture.rs
+++ b/maplibre/src/render/resource/texture.rs
@@ -94,6 +94,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2,
format,
usage,
+ view_formats: &[format],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
Self {
diff --git a/maplibre/src/render/resource/tile_pipeline.rs b/maplibre/src/render/resource/tile_pipeline.rs
index f1477525..26c42107 100644
--- a/maplibre/src/render/resource/tile_pipeline.rs
+++ b/maplibre/src/render/resource/tile_pipeline.rs
@@ -16,7 +16,7 @@ pub struct TilePipeline {
/// Force a write and ignore stencil
debug_stencil: bool,
wireframe: bool,
- multisampling: bool,
+ msaa: bool,
raster: bool,
settings: RendererSettings,
@@ -43,7 +43,7 @@ impl TilePipeline {
update_stencil,
debug_stencil,
wireframe,
- multisampling,
+ msaa: multisampling,
raster,
settings,
vertex_state,
@@ -132,7 +132,7 @@ impl RenderPipeline for TilePipeline {
})
},
multisample: wgpu::MultisampleState {
- count: if self.multisampling {
+ count: if self.msaa {
self.settings.msaa.samples
} else {
1
diff --git a/maplibre/src/render/settings.rs b/maplibre/src/render/settings.rs
index d8b7dbaf..4371db96 100644
--- a/maplibre/src/render/settings.rs
+++ b/maplibre/src/render/settings.rs
@@ -89,13 +89,14 @@ pub struct Msaa {
}
impl Msaa {
- pub fn is_active(&self) -> bool {
+ pub fn is_multisampling(&self) -> bool {
self.samples > 1
}
}
impl Default for Msaa {
fn default() -> Self {
+ // By default we are trying to multisample
Self { samples: 4 }
}
}
diff --git a/maplibre/src/render/systems/resource_system.rs b/maplibre/src/render/systems/resource_system.rs
index d951515b..4f596c4c 100644
--- a/maplibre/src/render/systems/resource_system.rs
+++ b/maplibre/src/render/systems/resource_system.rs
@@ -7,6 +7,7 @@ use crate::{
render::{
eventually::Eventually,
resource::{BackingBufferDescriptor, RenderPipeline, Texture, TilePipeline},
+ settings::Msaa,
shaders,
shaders::{Shader, ShaderTileMetadata},
tile_view_pattern::{TileViewPattern, WgpuTileViewPattern, DEFAULT_TILE_VIEW_PATTERN_SIZE},
@@ -63,7 +64,11 @@ impl System for ResourceSystem {
settings.depth_texture_format,
size.width(),
size.height(),
- settings.msaa,
+ if surface.is_multisampling_supported(settings.msaa) {
+ settings.msaa
+ } else {
+ Msaa { samples: 1 }
+ },
wgpu::TextureUsages::RENDER_ATTACHMENT,
)
},
@@ -72,7 +77,9 @@ impl System for ResourceSystem {
state.multisampling_texture.reinitialize(
|| {
- if settings.msaa.is_active() {
+ if settings.msaa.is_multisampling()
+ && surface.is_multisampling_supported(settings.msaa)
+ {
Some(Texture::new(
Some("multisampling texture"),
device,
@@ -120,7 +127,7 @@ impl System for ResourceSystem {
true,
false,
false,
- true,
+ surface.is_multisampling_supported(settings.msaa),
false,
)
.describe_render_pipeline()
diff --git a/maplibre/src/vector/resource_system.rs b/maplibre/src/vector/resource_system.rs
index 7adca81d..23335b6d 100644
--- a/maplibre/src/vector/resource_system.rs
+++ b/maplibre/src/vector/resource_system.rs
@@ -48,7 +48,7 @@ pub fn resource_system(
false,
false,
false,
- true,
+ surface.is_multisampling_supported(settings.msaa),
false,
)
.describe_render_pipeline()
diff --git a/web/Cargo.toml b/web/Cargo.toml
index 30723ec2..c7bcafb6 100644
--- a/web/Cargo.toml
+++ b/web/Cargo.toml
@@ -41,8 +41,8 @@ web-sys = { version = "0.3.58", features = [
"ErrorEvent"
] }
js-sys = "0.3.58"
-wasm-bindgen = "0.2.81"
-wasm-bindgen-futures = "0.4.31"
+wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4"
console_log = { version = "0.2.0", features = ["color"] }
tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency (remove in a separate PR!)
# For passing Inputs in AsyncProcedureCalls
@@ -55,4 +55,4 @@ image = "*" # FIXME: Remove image, use browser capabilities
flatc-rust = "0.2.0"
[dev-dependencies]
-wasm-bindgen-test = "0.3.31"
+wasm-bindgen-test = "0.3"