Automatically choose texture format based on adapter (#224)

* Automatically choose texture format based on adapter

* Use compatible_surface properly
This commit is contained in:
Max Ammann 2022-12-14 10:13:05 +01:00 committed by GitHub
parent 516d642079
commit 4fcd142e0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 168 additions and 185 deletions

View File

@ -37,23 +37,18 @@ impl Node for CopySurfaceBufferNode {
Head::Headless(buffered_texture) => { Head::Headless(buffered_texture) => {
let size = surface.size(); let size = surface.size();
command_encoder.copy_texture_to_buffer( command_encoder.copy_texture_to_buffer(
buffered_texture.texture.as_image_copy(), buffered_texture.copy_texture(),
wgpu::ImageCopyBuffer { wgpu::ImageCopyBuffer {
buffer: &buffered_texture.output_buffer, buffer: &buffered_texture.buffer(),
layout: wgpu::ImageDataLayout { layout: wgpu::ImageDataLayout {
offset: 0, offset: 0,
bytes_per_row: Some( bytes_per_row: Some(buffered_texture.bytes_per_row()),
std::num::NonZeroU32::new(
buffered_texture.buffer_dimensions.padded_bytes_per_row as u32,
)
.unwrap(), // TODO: remove unwrap
),
rows_per_image: None, rows_per_image: None,
}, },
}, },
wgpu::Extent3d { wgpu::Extent3d {
width: size.width() as u32, width: size.width(),
height: size.height() as u32, height: size.height(),
depth_or_array_layers: 1, depth_or_array_layers: 1,
}, },
); );

View File

@ -12,6 +12,7 @@ use crate::{
InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer, InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer,
}, },
create_default_render_graph, create_default_render_graph,
error::RenderError,
graph::RenderGraphError, graph::RenderGraphError,
register_default_render_stages, register_default_render_stages,
}, },
@ -30,7 +31,7 @@ pub enum MapError {
#[error("initializing render graph failed")] #[error("initializing render graph failed")]
RenderGraphInit(RenderGraphError), RenderGraphInit(RenderGraphError),
#[error("initializing device failed")] #[error("initializing device failed")]
DeviceInit, DeviceInit(RenderError),
} }
pub enum MapContextState { pub enum MapContextState {
@ -92,7 +93,7 @@ where
.build() .build()
.initialize_renderer::<E::MapWindowConfig>(&self.window) .initialize_renderer::<E::MapWindowConfig>(&self.window)
.await .await
.map_err(|e| MapError::DeviceInit)?; .map_err(|e| MapError::DeviceInit(e))?;
let window_size = self.window.size(); let window_size = self.window.size();

View File

@ -1,22 +1,15 @@
use std::fmt; use thiserror::Error;
use crate::render::graph::RenderGraphError; use crate::render::graph::RenderGraphError;
#[derive(Debug)] #[derive(Error, Debug)]
pub enum RenderError { pub enum RenderError {
Surface(wgpu::SurfaceError), #[error("error in surface")]
Graph(RenderGraphError), Surface(#[from] wgpu::SurfaceError),
Device(wgpu::RequestDeviceError), #[error("error in render graph")]
} Graph(#[from] RenderGraphError),
#[error("error while requesting device")]
impl fmt::Display for RenderError { RequestDevice(#[from] wgpu::RequestDeviceError),
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenderError::Surface(e) => write!(f, "{}", e),
RenderError::Graph(e) => write!(f, "{:?}", e),
RenderError::Device(e) => write!(f, "{}", e),
}
}
} }
impl RenderError { impl RenderError {
@ -30,21 +23,3 @@ impl RenderError {
} }
} }
} }
impl From<RenderGraphError> for RenderError {
fn from(e: RenderGraphError) -> Self {
RenderError::Graph(e)
}
}
impl From<wgpu::SurfaceError> for RenderError {
fn from(e: wgpu::SurfaceError) -> Self {
RenderError::Surface(e)
}
}
impl From<wgpu::RequestDeviceError> for RenderError {
fn from(e: wgpu::RequestDeviceError) -> Self {
RenderError::Device(e)
}
}

View File

@ -57,6 +57,7 @@ pub use stages::register_default_render_stages;
use crate::{ use crate::{
render::{ render::{
error::RenderError,
graph::{EmptyNode, RenderGraph, RenderGraphError}, graph::{EmptyNode, RenderGraph, RenderGraphError},
main_pass::{MainPassDriverNode, MainPassNode}, main_pass::{MainPassDriverNode, MainPassNode},
}, },
@ -142,7 +143,7 @@ pub struct Renderer {
pub instance: wgpu::Instance, pub instance: wgpu::Instance,
pub device: Arc<wgpu::Device>, // TODO: Arc is needed for headless rendering. Is there a simpler solution? pub device: Arc<wgpu::Device>, // TODO: Arc is needed for headless rendering. Is there a simpler solution?
pub queue: wgpu::Queue, pub queue: wgpu::Queue,
pub adapter_info: wgpu::AdapterInfo, pub adapter: wgpu::Adapter,
pub wgpu_settings: WgpuSettings, pub wgpu_settings: WgpuSettings,
pub settings: RendererSettings, pub settings: RendererSettings,
@ -157,30 +158,27 @@ impl Renderer {
window: &MW, window: &MW,
wgpu_settings: WgpuSettings, wgpu_settings: WgpuSettings,
settings: RendererSettings, settings: RendererSettings,
) -> Result<Self, wgpu::RequestDeviceError> ) -> Result<Self, RenderError>
where where
MW: MapWindow + HeadedMapWindow, MW: MapWindow + HeadedMapWindow,
{ {
let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all())); let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
let surface = Surface::from_window(&instance, window, &settings); let surface: wgpu::Surface = unsafe { instance.create_surface(window.raw()) };
let compatible_surface = match &surface.head() { let (adapter, device, queue) = Self::request_device(
Head::Headed(window_head) => Some(window_head.surface()),
Head::Headless(_) => None,
};
let (device, queue, adapter_info) = Self::request_device(
&instance, &instance,
&wgpu_settings, &wgpu_settings,
&wgpu::RequestAdapterOptions { &wgpu::RequestAdapterOptions {
power_preference: wgpu_settings.power_preference, power_preference: wgpu_settings.power_preference,
force_fallback_adapter: false, force_fallback_adapter: false,
compatible_surface, compatible_surface: Some(&surface),
}, },
) )
.await?; .await?;
let surface = Surface::from_surface(surface, &adapter, window, &settings);
match surface.head() { match surface.head() {
Head::Headed(window) => window.configure(&device), Head::Headed(window) => window.configure(&device),
Head::Headless(_) => {} Head::Headless(_) => {}
@ -190,7 +188,7 @@ impl Renderer {
instance, instance,
device: Arc::new(device), device: Arc::new(device),
queue, queue,
adapter_info, adapter,
wgpu_settings, wgpu_settings,
settings, settings,
state: RenderState::new(surface), state: RenderState::new(surface),
@ -201,13 +199,13 @@ impl Renderer {
window: &MW, window: &MW,
wgpu_settings: WgpuSettings, wgpu_settings: WgpuSettings,
settings: RendererSettings, settings: RendererSettings,
) -> Result<Self, wgpu::RequestDeviceError> ) -> Result<Self, RenderError>
where where
MW: MapWindow, MW: MapWindow,
{ {
let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all())); let instance = wgpu::Instance::new(wgpu_settings.backends.unwrap_or(wgpu::Backends::all()));
let (device, queue, adapter_info) = Self::request_device( let (adapter, device, queue) = Self::request_device(
&instance, &instance,
&wgpu_settings, &wgpu_settings,
&wgpu::RequestAdapterOptions { &wgpu::RequestAdapterOptions {
@ -224,7 +222,7 @@ impl Renderer {
instance, instance,
device: Arc::new(device), device: Arc::new(device),
queue, queue,
adapter_info, adapter,
wgpu_settings, wgpu_settings,
settings, settings,
state: RenderState::new(surface), state: RenderState::new(surface),
@ -240,7 +238,7 @@ impl Renderer {
instance: &wgpu::Instance, instance: &wgpu::Instance,
settings: &WgpuSettings, settings: &WgpuSettings,
request_adapter_options: &wgpu::RequestAdapterOptions<'_>, request_adapter_options: &wgpu::RequestAdapterOptions<'_>,
) -> Result<(wgpu::Device, wgpu::Queue, wgpu::AdapterInfo), wgpu::RequestDeviceError> { ) -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue), wgpu::RequestDeviceError> {
let adapter = instance let adapter = instance
.request_adapter(request_adapter_options) .request_adapter(request_adapter_options)
.await .await
@ -384,7 +382,7 @@ impl Renderer {
trace_path, trace_path,
) )
.await?; .await?;
Ok((device, queue, adapter_info)) Ok((adapter, device, queue))
} }
pub fn instance(&self) -> &wgpu::Instance { pub fn instance(&self) -> &wgpu::Instance {

View File

@ -1,9 +1,9 @@
//! Utilities for handling surfaces which can be either headless or headed. A headed surface has //! Utilities for handling surfaces which can be either headless or headed. A headed surface has
//! a handle to a window. A headless surface renders to a texture. //! a handle to a window. A headless surface renders to a texture.
use std::{mem::size_of, sync::Arc}; use std::{mem::size_of, num::NonZeroU32, sync::Arc};
use wgpu::CompositeAlphaMode; use log::debug;
use crate::{ use crate::{
render::{eventually::HasChanged, resource::texture::TextureView, settings::RendererSettings}, render::{eventually::HasChanged, resource::texture::TextureView, settings::RendererSettings},
@ -11,41 +11,54 @@ use crate::{
}; };
pub struct BufferDimensions { pub struct BufferDimensions {
pub width: usize, pub width: u32,
pub height: usize, pub height: u32,
pub unpadded_bytes_per_row: usize, pub unpadded_bytes_per_row: NonZeroU32,
pub padded_bytes_per_row: usize, pub padded_bytes_per_row: NonZeroU32,
} }
impl BufferDimensions { impl BufferDimensions {
fn new(width: usize, height: usize) -> Self { fn new(size: WindowSize) -> Self {
let bytes_per_pixel = size_of::<u32>(); let bytes_per_pixel = size_of::<u32>() as u32;
let unpadded_bytes_per_row = width * bytes_per_pixel; let unpadded_bytes_per_row = size.width() * bytes_per_pixel;
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align; let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding; let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
Self { Self {
width, width: size.width(),
height, height: size.height(),
unpadded_bytes_per_row, unpadded_bytes_per_row: NonZeroU32::new(unpadded_bytes_per_row)
padded_bytes_per_row, .expect("can not be zero"), // expect is fine because this can never happen
padded_bytes_per_row: NonZeroU32::new(padded_bytes_per_row).expect("can not be zero"),
} }
} }
} }
pub struct WindowHead { pub struct WindowHead {
surface: wgpu::Surface, surface: wgpu::Surface,
surface_config: wgpu::SurfaceConfiguration, size: WindowSize,
format: wgpu::TextureFormat,
present_mode: wgpu::PresentMode,
} }
impl WindowHead { impl WindowHead {
pub fn resize_and_configure(&mut self, width: u32, height: u32, device: &wgpu::Device) { pub fn resize_and_configure(&mut self, width: u32, height: u32, device: &wgpu::Device) {
self.surface_config.height = width; self.size = WindowSize::new(width, height).unwrap();
self.surface_config.width = height; self.configure(device);
self.surface.configure(device, &self.surface_config);
} }
pub fn configure(&self, device: &wgpu::Device) { pub fn configure(&self, device: &wgpu::Device) {
self.surface.configure(device, &self.surface_config); let surface_config = wgpu::SurfaceConfiguration {
alpha_mode: wgpu::CompositeAlphaMode::Auto,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: self.format,
width: self.size.width(),
height: self.size.height(),
present_mode: self.present_mode,
};
self.surface.configure(device, &surface_config);
} }
pub fn recreate_surface<MW>(&mut self, window: &MW, instance: &wgpu::Instance) pub fn recreate_surface<MW>(&mut self, window: &MW, instance: &wgpu::Instance)
@ -54,15 +67,17 @@ impl WindowHead {
{ {
self.surface = unsafe { instance.create_surface(window.raw()) }; self.surface = unsafe { instance.create_surface(window.raw()) };
} }
pub fn surface(&self) -> &wgpu::Surface { pub fn surface(&self) -> &wgpu::Surface {
&self.surface &self.surface
} }
} }
pub struct BufferedTextureHead { pub struct BufferedTextureHead {
pub texture: wgpu::Texture, texture: wgpu::Texture,
pub output_buffer: wgpu::Buffer, texture_format: wgpu::TextureFormat,
pub buffer_dimensions: BufferDimensions, output_buffer: wgpu::Buffer,
buffer_dimensions: BufferDimensions,
} }
#[cfg(feature = "headless")] #[cfg(feature = "headless")]
@ -95,17 +110,33 @@ impl BufferedTextureHead {
let mut png_writer = png_encoder let mut png_writer = png_encoder
.write_header() .write_header()
.unwrap() // TODO: Remove unwrap .unwrap() // TODO: Remove unwrap
.into_stream_writer_with_size(self.buffer_dimensions.unpadded_bytes_per_row) .into_stream_writer_with_size(
self.buffer_dimensions.unpadded_bytes_per_row.get() as usize
)
.unwrap(); // TODO: Remove unwrap .unwrap(); // TODO: Remove unwrap
// from the padded_buffer we write just the unpadded bytes into the image // from the padded_buffer we write just the unpadded bytes into the image
for chunk in padded_buffer.chunks(self.buffer_dimensions.padded_bytes_per_row) { for chunk in
padded_buffer.chunks(self.buffer_dimensions.padded_bytes_per_row.get() as usize)
{
png_writer png_writer
.write_all(&chunk[..self.buffer_dimensions.unpadded_bytes_per_row]) .write_all(&chunk[..self.buffer_dimensions.unpadded_bytes_per_row.get() as usize])
.unwrap(); // TODO: Remove unwrap .unwrap(); // TODO: Remove unwrap
} }
png_writer.finish().unwrap(); // TODO: Remove unwrap png_writer.finish().unwrap(); // TODO: Remove unwrap
} }
pub fn copy_texture(&self) -> wgpu::ImageCopyTexture<'_> {
self.texture.as_image_copy()
}
pub fn buffer(&self) -> &wgpu::Buffer {
&self.output_buffer
}
pub fn bytes_per_row(&self) -> NonZeroU32 {
self.buffer_dimensions.padded_bytes_per_row
}
} }
pub enum Head { pub enum Head {
@ -119,8 +150,9 @@ pub struct Surface {
} }
impl Surface { impl Surface {
pub fn from_window<MW>( pub fn from_surface<MW>(
instance: &wgpu::Instance, surface: wgpu::Surface,
adapter: &wgpu::Adapter,
window: &MW, window: &MW,
settings: &RendererSettings, settings: &RendererSettings,
) -> Self ) -> Self
@ -128,22 +160,24 @@ impl Surface {
MW: MapWindow + HeadedMapWindow, MW: MapWindow + HeadedMapWindow,
{ {
let size = window.size(); let size = window.size();
let surface_config = wgpu::SurfaceConfiguration {
alpha_mode: CompositeAlphaMode::Auto,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: settings.texture_format,
width: size.width(),
height: size.height(),
present_mode: settings.present_mode,
};
let surface = unsafe { instance.create_surface(window.raw()) }; debug!(
"supported formats by adapter: {:?}",
surface.get_supported_formats(adapter)
);
let format = settings
.texture_format
.or_else(|| surface.get_supported_formats(adapter).first().cloned())
.unwrap_or(wgpu::TextureFormat::Rgba8Unorm);
Self { Self {
size, size,
head: Head::Headed(WindowHead { head: Head::Headed(WindowHead {
surface, surface,
surface_config, size,
format,
present_mode: settings.present_mode,
}), }),
} }
} }
@ -159,17 +193,22 @@ impl Surface {
// So we calculate padded_bytes_per_row by rounding unpadded_bytes_per_row // So we calculate padded_bytes_per_row by rounding unpadded_bytes_per_row
// up to the next multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. // up to the next multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT.
// https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding // https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding
let buffer_dimensions = let buffer_dimensions = BufferDimensions::new(size);
BufferDimensions::new(size.width() as usize, size.height() as usize);
// The output buffer lets us retrieve the data as an array // The output buffer lets us retrieve the data as an array
let output_buffer = device.create_buffer(&wgpu::BufferDescriptor { let output_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("BufferedTextureHead buffer"), label: Some("BufferedTextureHead buffer"),
size: (buffer_dimensions.padded_bytes_per_row * buffer_dimensions.height) as u64, size: (buffer_dimensions.padded_bytes_per_row.get() * buffer_dimensions.height) as u64,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
}); });
let texture = device.create_texture(&wgpu::TextureDescriptor { // FIXME: Is this a sane default?
let format = settings
.texture_format
.unwrap_or(wgpu::TextureFormat::Rgba8Unorm);
let texture_descriptor = wgpu::TextureDescriptor {
label: Some("Surface texture"), label: Some("Surface texture"),
size: wgpu::Extent3d { size: wgpu::Extent3d {
width: size.width(), width: size.width(),
@ -179,20 +218,29 @@ impl Surface {
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: settings.texture_format, format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
}); };
let texture = device.create_texture(&texture_descriptor);
Self { Self {
size, size,
head: Head::Headless(Arc::new(BufferedTextureHead { head: Head::Headless(Arc::new(BufferedTextureHead {
texture, texture,
texture_format: format,
output_buffer, output_buffer,
buffer_dimensions, buffer_dimensions,
})), })),
} }
} }
pub fn surface_format(&self) -> wgpu::TextureFormat {
match &self.head {
Head::Headed(headed) => headed.format,
Head::Headless(headless) => headless.texture_format,
}
}
#[tracing::instrument(name = "create_view", skip_all)] #[tracing::instrument(name = "create_view", skip_all)]
pub fn create_view(&self, device: &wgpu::Device) -> TextureView { pub fn create_view(&self, device: &wgpu::Device) -> TextureView {
match &self.head { match &self.head {
@ -261,9 +309,10 @@ impl Surface {
} }
impl HasChanged for WindowHead { impl HasChanged for WindowHead {
/// Tuple of width and height
type Criteria = (u32, u32); type Criteria = (u32, u32);
fn has_changed(&self, criteria: &Self::Criteria) -> bool { fn has_changed(&self, criteria: &Self::Criteria) -> bool {
self.surface_config.width != criteria.0 || self.surface_config.height != criteria.1 self.size.width() != criteria.0 || self.size.height() != criteria.1
} }
} }

View File

@ -103,7 +103,8 @@ impl Default for Msaa {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct RendererSettings { pub struct RendererSettings {
pub msaa: Msaa, pub msaa: Msaa,
pub texture_format: TextureFormat, /// Explicitly set a texture format or let the renderer automatically choose one
pub texture_format: Option<TextureFormat>,
pub depth_texture_format: TextureFormat, pub depth_texture_format: TextureFormat,
/// Present mode for surfaces if a surface is used. /// Present mode for surfaces if a surface is used.
pub present_mode: PresentMode, pub present_mode: PresentMode,
@ -113,26 +114,7 @@ impl Default for RendererSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
msaa: Msaa::default(), msaa: Msaa::default(),
// WebGPU texture_format: None,
#[cfg(all(target_arch = "wasm32", not(feature = "web-webgl")))]
texture_format: wgpu::TextureFormat::Bgra8Unorm,
// WebGL
#[cfg(all(target_arch = "wasm32", feature = "web-webgl"))]
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
// Vulkan Android
#[cfg(target_os = "android")]
texture_format: wgpu::TextureFormat::Rgba8Unorm,
/// MacOS and iOS (Metal).
#[cfg(any(target_os = "macos", target_os = "ios"))]
texture_format: wgpu::TextureFormat::Bgra8UnormSrgb,
/// For Vulkan/OpenGL
#[cfg(not(any(
target_os = "android",
target_os = "macos",
any(target_os = "macos", target_os = "ios"),
target_arch = "wasm32"
)))]
texture_format: TextureFormat::Bgra8UnormSrgb,
depth_texture_format: TextureFormat::Depth24PlusStencil8, depth_texture_format: TextureFormat::Depth24PlusStencil8,
present_mode: PresentMode::AutoVsync, present_mode: PresentMode::AutoVsync,

View File

@ -63,7 +63,7 @@ impl Stage for ResourceStage {
Some(Texture::new( Some(Texture::new(
Some("multisampling texture"), Some("multisampling texture"),
device, device,
settings.texture_format, surface.surface_format(),
size.width(), size.width(),
size.height(), size.height(),
settings.msaa, settings.msaa,
@ -96,7 +96,7 @@ impl Stage for ResourceStage {
state.tile_pipeline.initialize(|| { state.tile_pipeline.initialize(|| {
let tile_shader = shaders::TileShader { let tile_shader = shaders::TileShader {
format: settings.texture_format, format: surface.surface_format(),
}; };
let pipeline = TilePipeline::new( let pipeline = TilePipeline::new(
@ -120,7 +120,7 @@ impl Stage for ResourceStage {
state.mask_pipeline.initialize(|| { state.mask_pipeline.initialize(|| {
let mask_shader = shaders::TileMaskShader { let mask_shader = shaders::TileMaskShader {
format: settings.texture_format, format: surface.surface_format(),
draw_colors: false, draw_colors: false,
}; };

View File

@ -1,5 +1,7 @@
//! Utilities for the window system. //! Utilities for the window system.
use std::num::NonZeroU32;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one. /// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one.
@ -31,23 +33,31 @@ pub trait MapWindowConfig: 'static {
/// Window size with a width and an height in pixels. /// Window size with a width and an height in pixels.
#[derive(Clone, Copy, Eq, PartialEq)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct WindowSize { pub struct WindowSize {
width: u32, width: NonZeroU32,
height: u32, height: NonZeroU32,
} }
impl WindowSize { impl WindowSize {
pub fn new(width: u32, height: u32) -> Option<Self> { pub fn new(width: u32, height: u32) -> Option<Self> {
if width == 0 || height == 0 { Some(Self {
return None; width: NonZeroU32::new(width)?,
} height: NonZeroU32::new(height)?,
})
Some(Self { width, height })
} }
pub fn width(&self) -> u32 { pub fn width(&self) -> u32 {
self.width.get()
}
pub fn width_non_zero(&self) -> NonZeroU32 {
self.width self.width
} }
pub fn height(&self) -> u32 { pub fn height(&self) -> u32 {
self.height.get()
}
pub fn height_non_zero(&self) -> NonZeroU32 {
self.height self.height
} }
} }

View File

@ -2,11 +2,10 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
error::Error,
fmt::{Display, Formatter}, fmt::{Display, Formatter},
}; };
use js_sys::{Error as JSError, TypeError}; use js_sys::TypeError;
use maplibre::io::apc::{CallError, ProcedureError}; use maplibre::io::apc::{CallError, ProcedureError};
use thiserror::Error; use thiserror::Error;
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
@ -34,7 +33,7 @@ impl From<JsValue> for WebError {
.as_string() else { return WebError::InvalidMessage; }; .as_string() else { return WebError::InvalidMessage; };
WebError::TypeError(message.into()) WebError::TypeError(message.into())
} else if let Some(error) = value.dyn_ref::<JSError>() { } else if let Some(error) = value.dyn_ref::<js_sys::Error>() {
let Some(message) = error let Some(message) = error
.message() .message()
.as_string() else { return WebError::InvalidMessage; }; .as_string() else { return WebError::InvalidMessage; };
@ -48,41 +47,25 @@ impl From<JsValue> for WebError {
/// Wraps several unrelated errors and implements Into<JSValue>. This should be used in Rust /// Wraps several unrelated errors and implements Into<JSValue>. This should be used in Rust
/// functions called from JS-land as return error type. /// functions called from JS-land as return error type.
#[derive(Debug)] #[derive(Error, Debug)]
pub enum WrappedError { pub enum JSError {
ProcedureError(ProcedureError), ProcedureError(#[from] ProcedureError),
CallError(CallError), CallError(#[from] CallError),
WebError(WebError), WebError(#[from] WebError),
} }
impl Display for WrappedError { impl Display for JSError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Error from Rust: {:?}", self) match self {
JSError::ProcedureError(inner) => inner.fmt(f),
JSError::CallError(inner) => inner.fmt(f),
JSError::WebError(inner) => inner.fmt(f),
}
} }
} }
impl Error for WrappedError {} impl From<JSError> for JsValue {
fn from(val: JSError) -> Self {
impl From<WrappedError> for JsValue {
fn from(val: WrappedError) -> Self {
JsValue::from_str(&val.to_string()) JsValue::from_str(&val.to_string())
} }
} }
impl From<CallError> for WrappedError {
fn from(e: CallError) -> Self {
WrappedError::CallError(e)
}
}
impl From<ProcedureError> for WrappedError {
fn from(e: ProcedureError) -> Self {
WrappedError::ProcedureError(e)
}
}
impl From<WebError> for WrappedError {
fn from(e: WebError) -> Self {
WrappedError::WebError(e)
}
}

View File

@ -11,7 +11,7 @@ use maplibre::{
use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig}; use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use crate::{error::WrappedError, platform::http_client::WHATWGFetchHttpClient}; use crate::{error::JSError, platform::http_client::WHATWGFetchHttpClient};
mod error; mod error;
mod platform; mod platform;
@ -65,7 +65,7 @@ type CurrentEnvironment = WinitEnvironment<
pub type MapType = Map<CurrentEnvironment>; pub type MapType = Map<CurrentEnvironment>;
#[wasm_bindgen] #[wasm_bindgen]
pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), WrappedError> { pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), JSError> {
let mut kernel_builder = KernelBuilder::new() let mut kernel_builder = KernelBuilder::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string())) .with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_http_client(WHATWGFetchHttpClient::new()); .with_http_client(WHATWGFetchHttpClient::new());

View File

@ -2,11 +2,11 @@ use maplibre::io::apc::CallError;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
use crate::{platform::multithreaded::pool::Work, WrappedError}; use crate::{platform::multithreaded::pool::Work, JSError};
/// Entry point invoked by the worker. /// Entry point invoked by the worker.
#[wasm_bindgen] #[wasm_bindgen]
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), WrappedError> { pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JSError> {
let work = unsafe { Box::from_raw(ptr as *mut Work) }; let work = unsafe { Box::from_raw(ptr as *mut Work) };
JsFuture::from(work.execute()) JsFuture::from(work.execute())
.await .await

View File

@ -103,15 +103,8 @@ impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
} }
} }
type NewWorker = Box<dyn Fn() -> Result<Worker, WebError>>;
pub type ReceivedType = RefCell<Vec<Message<UsedTransferables>>>; pub type ReceivedType = RefCell<Vec<Message<UsedTransferables>>>;
#[derive(Error, Debug)]
pub enum PassingAPCError {
#[error("creating a worker failed")]
Worker,
}
pub struct PassingAsyncProcedureCall { pub struct PassingAsyncProcedureCall {
workers: Vec<Worker>, workers: Vec<Worker>,

View File

@ -12,7 +12,7 @@ use thiserror::Error;
use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen::{prelude::*, JsCast};
use crate::{ use crate::{
error::WrappedError, error::JSError,
platform::singlethreaded::{ platform::singlethreaded::{
apc::{MessageTag, ReceivedType}, apc::{MessageTag, ReceivedType},
transferables::FlatBufferTransferable, transferables::FlatBufferTransferable,
@ -23,10 +23,7 @@ use crate::{
/// Entry point invoked by the worker. /// Entry point invoked by the worker.
#[wasm_bindgen] #[wasm_bindgen]
pub async fn singlethreaded_worker_entry( pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JSError> {
procedure_ptr: u32,
input: String,
) -> Result<(), WrappedError> {
let procedure: AsyncProcedure<UsedContext> = unsafe { mem::transmute(procedure_ptr) }; let procedure: AsyncProcedure<UsedContext> = unsafe { mem::transmute(procedure_ptr) };
let input = let input =
@ -50,7 +47,7 @@ pub struct DeserializeMessage;
pub unsafe fn singlethreaded_main_entry( pub unsafe fn singlethreaded_main_entry(
received_ptr: *const ReceivedType, received_ptr: *const ReceivedType,
in_transfer: js_sys::Array, in_transfer: js_sys::Array,
) -> Result<(), WrappedError> { ) -> Result<(), JSError> {
let tag = in_transfer let tag = in_transfer
.get(0) .get(0)
.as_f64() .as_f64()