summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/crates/compositor/src/compositor.rs1
-rw-r--r--rust/crates/gpu/src/context.rs139
-rw-r--r--rust/wasm/src/compositor.rs73
3 files changed, 126 insertions, 87 deletions
diff --git a/rust/crates/compositor/src/compositor.rs b/rust/crates/compositor/src/compositor.rs
index 84bcceae..99f0e7e9 100644
--- a/rust/crates/compositor/src/compositor.rs
+++ b/rust/crates/compositor/src/compositor.rs
@@ -348,7 +348,6 @@ impl Compositor {
) -> Result<(), CompositorError> {
let frame = options.frame;
self.texture_pool.recycle_frame();
- context.configure_surface(options.surface, frame.width, frame.height)?;
let surface_texture = context.acquire_surface_texture(options.surface)?;
let surface_view = surface_texture
.texture
diff --git a/rust/crates/gpu/src/context.rs b/rust/crates/gpu/src/context.rs
index 0fcb2e8c..da835280 100644
--- a/rust/crates/gpu/src/context.rs
+++ b/rust/crates/gpu/src/context.rs
@@ -1,6 +1,9 @@
use wgpu::util::DeviceExt;
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
+use std::cell::RefCell;
+
+#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
use wasm_bindgen::JsCast;
use crate::{FULLSCREEN_SHADER_SOURCE, GpuError};
@@ -17,6 +20,12 @@ impl wgpu::rwh::HasDisplayHandle for WebDisplay {
}
}
+#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
+struct CachedCanvasSurface {
+ surface: wgpu::Surface<'static>,
+ size: (u32, u32),
+}
+
const BLIT_SHADER_SOURCE: &str = include_str!("shaders/blit.wgsl");
const FULLSCREEN_QUAD_POSITIONS: [[f32; 2]; 6] = [
@@ -44,6 +53,8 @@ pub struct GpuContext {
/// fallback path. Used by render_texture_via_gl_canvas to output frames on WebGL.
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
gl_canvas: Option<web_sys::HtmlCanvasElement>,
+ #[cfg(all(feature = "wasm", target_arch = "wasm32"))]
+ gl_surface: RefCell<Option<CachedCanvasSurface>>,
}
impl GpuContext {
@@ -170,6 +181,8 @@ impl GpuContext {
supports_external_texture_copies,
#[cfg(all(feature = "wasm", target_arch = "wasm32"))]
gl_canvas,
+ #[cfg(all(feature = "wasm", target_arch = "wasm32"))]
+ gl_surface: RefCell::new(None),
})
}
@@ -184,17 +197,15 @@ impl GpuContext {
),
GpuError,
> {
- // Temporary fix: force the wasm renderer onto the WebGL backend even when
- // WebGPU is available while a WebGPU bug is being investigated.
- // let instance = wgpu::util::new_instance_with_webgpu_detection(
- // wgpu::InstanceDescriptor::new_without_display_handle(),
- // )
- // .await;
+ let instance = wgpu::util::new_instance_with_webgpu_detection(
+ wgpu::InstanceDescriptor::new_without_display_handle(),
+ )
+ .await;
- // match Self::try_request_device(&instance, None).await {
- // Ok((adapter, device, queue)) => return Ok((instance, adapter, device, queue, None)),
- // Err(_) => {}
- // }
+ match Self::try_request_device(&instance, None).await {
+ Ok((adapter, device, queue)) => return Ok((instance, adapter, device, queue, None)),
+ Err(_) => {}
+ }
let (gl_instance, adapter, device, queue, canvas) = Self::try_gl_fallback().await?;
Ok((gl_instance, adapter, device, queue, Some(canvas)))
}
@@ -369,6 +380,14 @@ impl GpuContext {
height: u32,
) -> Result<(), GpuError> {
self.configure_surface(surface, width, height)?;
+ self.present_texture_to_surface(texture, surface)
+ }
+
+ pub fn present_texture_to_surface(
+ &self,
+ texture: &wgpu::Texture,
+ surface: &wgpu::Surface<'_>,
+ ) -> Result<(), GpuError> {
let surface_texture = self.acquire_surface_texture(surface)?;
let target_view = surface_texture
.texture
@@ -390,16 +409,38 @@ impl GpuContext {
width: u32,
height: u32,
) -> Result<(), GpuError> {
- let Some(config) = surface.get_default_config(&self.adapter, width, height) else {
- return Err(GpuError::UnsupportedSurfaceFormat);
- };
- if config.format != self.texture_format {
- return Err(GpuError::UnsupportedSurfaceFormat);
- }
+ let config = self.build_surface_configuration(surface, width, height)?;
surface.configure(&self.device, &config);
Ok(())
}
+ fn build_surface_configuration(
+ &self,
+ surface: &wgpu::Surface<'_>,
+ width: u32,
+ height: u32,
+ ) -> Result<wgpu::SurfaceConfiguration, GpuError> {
+ let caps = surface.get_capabilities(&self.adapter);
+ if !caps.formats.contains(&self.texture_format) {
+ return Err(GpuError::UnsupportedSurfaceFormat);
+ }
+
+ Ok(wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format: self.texture_format,
+ width,
+ height,
+ present_mode: wgpu::PresentMode::Fifo,
+ alpha_mode: caps
+ .alpha_modes
+ .first()
+ .copied()
+ .unwrap_or(wgpu::CompositeAlphaMode::Auto),
+ view_formats: vec![],
+ desired_maximum_frame_latency: 2,
+ })
+ }
+
pub fn acquire_surface_texture(
&self,
surface: &wgpu::Surface<'_>,
@@ -593,57 +634,29 @@ impl GpuContext {
gl_canvas.set_width(width);
gl_canvas.set_height(height);
- let surface = self
- .instance
- .create_surface(wgpu::SurfaceTarget::Canvas(gl_canvas.clone()))?;
-
- let caps = surface.get_capabilities(&self.adapter);
- let surface_format = if caps.formats.contains(&self.texture_format) {
- self.texture_format
- } else if !caps.formats.is_empty() {
- caps.formats[0]
- } else {
- return Err(GpuError::UnsupportedSurfaceFormat);
+ let mut cached_surface = self.gl_surface.borrow_mut();
+ let cached_surface = match cached_surface.as_mut() {
+ Some(cached_surface) => cached_surface,
+ None => {
+ let surface = self
+ .instance
+ .create_surface(wgpu::SurfaceTarget::Canvas(gl_canvas.clone()))?;
+ cached_surface.replace(CachedCanvasSurface {
+ surface,
+ size: (0, 0),
+ });
+ cached_surface
+ .as_mut()
+ .expect("gl_surface cache should exist after initialization")
+ }
};
- if surface_format != self.texture_format {
- return Err(GpuError::UnsupportedSurfaceFormat);
+ if cached_surface.size != (width, height) {
+ self.configure_surface(&cached_surface.surface, width, height)?;
+ cached_surface.size = (width, height);
}
- let config = wgpu::SurfaceConfiguration {
- usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
- format: surface_format,
- width,
- height,
- present_mode: wgpu::PresentMode::Fifo,
- alpha_mode: caps
- .alpha_modes
- .first()
- .copied()
- .unwrap_or(wgpu::CompositeAlphaMode::Auto),
- view_formats: vec![],
- desired_maximum_frame_latency: 2,
- };
- surface.configure(&self.device, &config);
-
- let surface_texture = self.acquire_surface_texture(&surface)?;
- let surface_view = surface_texture
- .texture
- .create_view(&wgpu::TextureViewDescriptor::default());
-
- let mut encoder = self
- .device
- .create_command_encoder(&wgpu::CommandEncoderDescriptor {
- label: Some("gpu-gl-canvas-blit-encoder"),
- });
- self.encode_texture_blit_to_view(
- &mut encoder,
- texture,
- &surface_view,
- "gpu-gl-canvas-blit",
- );
- self.queue.submit([encoder.finish()]);
- surface_texture.present();
+ self.present_texture_to_surface(texture, &cached_surface.surface)?;
Ok(gl_canvas)
}
diff --git a/rust/wasm/src/compositor.rs b/rust/wasm/src/compositor.rs
index 84467842..7652b3f3 100644
--- a/rust/wasm/src/compositor.rs
+++ b/rust/wasm/src/compositor.rs
@@ -16,6 +16,8 @@ use crate::perf;
struct CompositorRuntime {
canvas: web_sys::HtmlCanvasElement,
compositor: Compositor,
+ surface: wgpu::Surface<'static>,
+ surface_size: (u32, u32),
}
thread_local! {
@@ -44,9 +46,23 @@ pub fn init_compositor(width: u32, height: u32) -> Result<(), JsValue> {
canvas.set_height(height);
let compositor = Compositor::new(&gpu_runtime.context);
+ let surface = gpu_runtime
+ .context
+ .instance()
+ .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
+ gpu_runtime
+ .context
+ .configure_surface(&surface, width, height)
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
COMPOSITOR_RUNTIME.with(|runtime| {
- runtime.replace(Some(CompositorRuntime { canvas, compositor }));
+ runtime.replace(Some(CompositorRuntime {
+ canvas,
+ compositor,
+ surface,
+ surface_size: (width, height),
+ }));
});
Ok(())
@@ -55,16 +71,25 @@ pub fn init_compositor(width: u32, height: u32) -> Result<(), JsValue> {
#[wasm_bindgen(js_name = resizeCompositor)]
pub fn resize_compositor(width: u32, height: u32) -> Result<(), JsValue> {
- COMPOSITOR_RUNTIME.with(|runtime| {
- let mut borrow = runtime.borrow_mut();
- let Some(runtime) = borrow.as_mut() else {
- return Err(JsValue::from_str(
- "Compositor is not initialized. Call initCompositor() first.",
- ));
- };
- runtime.canvas.set_width(width);
- runtime.canvas.set_height(height);
- Ok(())
+ with_gpu_runtime(|gpu_runtime| {
+ COMPOSITOR_RUNTIME.with(|runtime| {
+ let mut borrow = runtime.borrow_mut();
+ let Some(runtime) = borrow.as_mut() else {
+ return Err(JsValue::from_str(
+ "Compositor is not initialized. Call initCompositor() first.",
+ ));
+ };
+ runtime.canvas.set_width(width);
+ runtime.canvas.set_height(height);
+ if runtime.surface_size != (width, height) {
+ gpu_runtime
+ .context
+ .configure_surface(&runtime.surface, width, height)
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
+ runtime.surface_size = (width, height);
+ }
+ Ok(())
+ })
})
}
@@ -144,15 +169,19 @@ pub fn render_frame(options: JsValue) -> Result<(), JsValue> {
));
};
- if gpu_runtime.context.supports_surface_rendering() {
+ if runtime.surface_size != (frame.width, frame.height) {
+ runtime.canvas.set_width(frame.width);
+ runtime.canvas.set_height(frame.height);
let t_surface = perf::now_ms();
- let surface = gpu_runtime
+ gpu_runtime
.context
- .instance()
- .create_surface(wgpu::SurfaceTarget::Canvas(runtime.canvas.clone()))
+ .configure_surface(&runtime.surface, frame.width, frame.height)
.map_err(|error| JsValue::from_str(&error.to_string()))?;
- perf::record("wasm.surfaceCreate", perf::now_ms() - t_surface);
+ perf::record("wasm.surfaceConfigure", perf::now_ms() - t_surface);
+ runtime.surface_size = (frame.width, frame.height);
+ }
+ if gpu_runtime.context.supports_surface_rendering() {
let t_render = perf::now_ms();
let result = runtime
.compositor
@@ -160,17 +189,15 @@ pub fn render_frame(options: JsValue) -> Result<(), JsValue> {
&gpu_runtime.context,
RenderFrameOptions {
frame: &frame,
- surface: &surface,
+ surface: &runtime.surface,
},
)
.map_err(|error| JsValue::from_str(&error.to_string()));
perf::record("wasm.renderFrameToSurface", perf::now_ms() - t_render);
result
} else {
- // WebGL surface-renders to the canvas its GL context was created
- // on; `init_compositor` already pointed `runtime.canvas` at that
- // same canvas, so presenting writes directly to the canvas the
- // UI mounts. No intermediate copy.
+ // WebGL still needs a separate composition pass, but the output
+ // surface is now persistent just like the WebGPU path.
let t_composite = perf::now_ms();
let texture = runtime
.compositor
@@ -181,9 +208,9 @@ pub fn render_frame(options: JsValue) -> Result<(), JsValue> {
let t_present = perf::now_ms();
gpu_runtime
.context
- .render_texture_to_gl_canvas_surface(&texture, frame.width, frame.height)
+ .present_texture_to_surface(&texture, &runtime.surface)
.map_err(|error| JsValue::from_str(&error.to_string()))?;
- perf::record("wasm.presentToGlCanvas", perf::now_ms() - t_present);
+ perf::record("wasm.presentToSurface", perf::now_ms() - t_present);
Ok(())
}