diff --git a/CHANGELOG.md b/CHANGELOG.md index e03c86f1f6..1c84376c82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -268,6 +268,12 @@ By @cwfitzgerald in [#8609](https://github.com/gfx-rs/wgpu/pull/8609). - `DropCallback`s are now called after dropping all other fields of their parent structs. By @jerzywilczek in [#8353](https://github.com/gfx-rs/wgpu/pull/8353) +### Performance + +#### GLES / OpenGL + +- The GL backend would now try to take advantage of `GL_EXT_multisampled_render_to_texture` extension when applicable to skip the multi-sample resolve operation. By @opstic in [#8536](https://github.com/gfx-rs/wgpu/pull/8536). + ## v27.0.4 (2025-10-23) This release includes `wgpu-hal` version `27.0.4`. All other crates remain at their previous versions. diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index 1b868a537e..29159a3107 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -653,6 +653,10 @@ impl super::Adapter { // that's the only way to get gl_InstanceID to work correctly. features.set(wgt::Features::INDIRECT_FIRST_INSTANCE, supported); } + private_caps.set( + super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE, + extensions.contains("GL_EXT_multisampled_render_to_texture"), + ); let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32; let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32; diff --git a/wgpu-hal/src/gles/command.rs b/wgpu-hal/src/gles/command.rs index ff77a626ef..faf258fb9a 100644 --- a/wgpu-hal/src/gles/command.rs +++ b/wgpu-hal/src/gles/command.rs @@ -557,10 +557,30 @@ impl crate::CommandEncoder for super::CommandEncoder { for (i, cat) in desc.color_attachments.iter().enumerate() { if let Some(cat) = cat.as_ref() { let attachment = glow::COLOR_ATTACHMENT0 + i as u32; + // Try to use the multisampled render-to-texture extension to avoid resolving + if let Some(ref rat) = cat.resolve_target { + if matches!(rat.view.inner, super::TextureInner::Texture { .. }) + && self.private_caps.contains( + super::PrivateCapabilities::MULTISAMPLED_RENDER_TO_TEXTURE, + ) + && !cat.ops.contains(crate::AttachmentOps::STORE) + // Extension specifies that only COLOR_ATTACHMENT0 is valid + && i == 0 + { + self.cmd_buffer.commands.push(C::BindAttachment { + attachment, + view: rat.view.clone(), + depth_slice: None, + sample_count: desc.sample_count, + }); + continue; + } + } self.cmd_buffer.commands.push(C::BindAttachment { attachment, view: cat.target.view.clone(), depth_slice: cat.depth_slice, + sample_count: 1, }); if let Some(ref rat) = cat.resolve_target { self.state @@ -583,6 +603,7 @@ impl crate::CommandEncoder for super::CommandEncoder { attachment, view: dsat.target.view.clone(), depth_slice: None, + sample_count: 1, }); if aspects.contains(crate::FormatAspects::DEPTH) && dsat.depth_ops.contains(crate::AttachmentOps::STORE_DISCARD) diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index c374a1a057..4596d508c3 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -232,6 +232,8 @@ bitflags::bitflags! { /// /// When this is true, instance offset emulation via vertex buffer rebinding and a shader uniform will be disabled. const FULLY_FEATURED_INSTANCING = 1 << 16; + /// Supports direct multisampled rendering to a texture without needing a resolve texture. + const MULTISAMPLED_RENDER_TO_TEXTURE = 1 << 17; } } @@ -919,6 +921,7 @@ enum Command { attachment: u32, view: TextureView, depth_slice: Option, + sample_count: u32, }, ResolveAttachment { attachment: u32, diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index d9bda18f52..a1f09a5c03 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -111,6 +111,7 @@ impl super::Queue { attachment: u32, view: &super::TextureView, depth_slice: Option, + sample_count: u32, ) { match view.inner { super::TextureInner::Renderbuffer { raw } => { @@ -156,13 +157,24 @@ impl super::Queue { } else { unsafe { assert_eq!(view.mip_levels.len(), 1); - gl.framebuffer_texture_2d( - fbo_target, - attachment, - get_2d_target(target, view.array_layers.start), - Some(raw), - view.mip_levels.start as i32, - ) + if sample_count != 1 { + gl.framebuffer_texture_2d_multisample( + fbo_target, + attachment, + get_2d_target(target, view.array_layers.start), + Some(raw), + view.mip_levels.start as i32, + sample_count as i32, + ) + } else { + gl.framebuffer_texture_2d( + fbo_target, + attachment, + get_2d_target(target, view.array_layers.start), + Some(raw), + view.mip_levels.start as i32, + ) + } }; } } @@ -1119,9 +1131,17 @@ impl super::Queue { attachment, ref view, depth_slice, + sample_count, } => { unsafe { - self.set_attachment(gl, glow::DRAW_FRAMEBUFFER, attachment, view, depth_slice) + self.set_attachment( + gl, + glow::DRAW_FRAMEBUFFER, + attachment, + view, + depth_slice, + sample_count, + ) }; } C::ResolveAttachment { @@ -1139,6 +1159,7 @@ impl super::Queue { glow::COLOR_ATTACHMENT0, dst, None, + 1, ) }; unsafe {