Implement view_formats for SurfaceConfiguration (#3409)

Co-authored-by: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com>
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Jinlei Li 2023-01-26 05:04:41 +08:00 committed by GitHub
parent 5ded4ba701
commit 33f94c7c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 172 additions and 34 deletions

View File

@ -165,6 +165,15 @@ let texture = device.create_texture(&wgpu::TextureDescriptor {
});
```
```diff
let config = wgpu::SurfaceConfiguration {
// ...
format: TextureFormat::Rgba8Unorm,
+ view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
};
surface.configure(&device, &config);
```
### Changes
#### General
@ -183,9 +192,10 @@ let texture = device.create_texture(&wgpu::TextureDescriptor {
- Make `ObjectId` structure and invariants idiomatic. By @teoxoy in [#3347](https://github.com/gfx-rs/wgpu/pull/3347)
- Add validation in accordance with WebGPU `GPUSamplerDescriptor` valid usage for `lodMinClamp` and `lodMaxClamp`. By @James2022-rgb in [#3353](https://github.com/gfx-rs/wgpu/pull/3353)
- Remove panics in `Deref` implementations for `QueueWriteBufferView` and `BufferViewMut`. Instead, warnings are logged, since reading from these types is not recommended. By @botahamec in [#3336]
- Implement `view_formats` in TextureDescriptor to match the WebGPU spec. By @jinleili in [#3237](https://github.com/gfx-rs/wgpu/pull/3237)
- Implement `view_formats` in the TextureDescriptor to match the WebGPU spec. By @jinleili in [#3237](https://github.com/gfx-rs/wgpu/pull/3237)
- Show more information in error message for non-aligned buffer bindings in WebGL [#3414](https://github.com/gfx-rs/wgpu/pull/3414)
- Update `TextureView` validation according to the WebGPU spec. By @teoxoy in [#3410](https://github.com/gfx-rs/wgpu/pull/3410)
- Implement `view_formats` in the SurfaceConfiguration to match the WebGPU spec. By @jinleili in [#3409](https://github.com/gfx-rs/wgpu/pull/3409)
#### WebGPU

View File

@ -68,6 +68,15 @@
converter: webidl.converters["long"],
required: true,
},
{
key: "viewFormats",
converter: webidl.createSequenceConverter(
webidl.converters["GPUTextureFormat"],
),
get defaultValue() {
return [];
},
},
];
webidl.converters["GPUCanvasConfiguration"] = webidl
.createDictionaryConverter(

View File

@ -54,6 +54,7 @@ pub struct SurfaceConfigureArgs {
height: u32,
present_mode: Option<wgpu_types::PresentMode>,
alpha_mode: wgpu_types::CompositeAlphaMode,
view_formats: Vec<wgpu_types::TextureFormat>,
}
#[op]
@ -71,13 +72,14 @@ pub fn op_webgpu_surface_configure(
.get::<WebGpuSurface>(args.surface_rid)?;
let surface = surface_resource.0;
let conf = wgpu_types::SurfaceConfiguration {
let conf = wgpu_types::SurfaceConfiguration::<Vec<wgpu_types::TextureFormat>> {
usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
format: args.format,
width: args.width,
height: args.height,
present_mode: args.present_mode.unwrap_or_default(),
alpha_mode: args.alpha_mode,
view_formats: args.view_formats,
};
let err = gfx_select!(device => instance.surface_configure(surface, device, &conf));

View File

@ -1085,6 +1085,7 @@ dictionary GPUCanvasConfiguration {
required GPUTextureFormat format;
GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENT
GPUCanvasAlphaMode alphaMode = "opaque";
sequence<GPUTextureFormat> viewFormats = [];
};
enum GPUDeviceLostReason {

View File

@ -5186,7 +5186,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
surface_id: id::SurfaceId,
device_id: id::DeviceId,
config: &wgt::SurfaceConfiguration,
config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
) -> Option<present::ConfigureSurfaceError> {
use hal::{Adapter as _, Surface as _};
use present::ConfigureSurfaceError as E;
@ -5309,7 +5309,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (adapter_guard, mut token) = hub.adapters.read(&mut token);
let (device_guard, _token) = hub.devices.read(&mut token);
let error = loop {
let error = 'outer: loop {
let device = match device_guard.get(device_id) {
Ok(device) => device,
Err(_) => break DeviceError::Invalid.into(),
@ -5335,6 +5335,31 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
};
let mut hal_view_formats = vec![];
for format in config.view_formats.iter() {
if *format == config.format {
continue;
}
if !caps.formats.contains(&config.format) {
break 'outer E::UnsupportedFormat {
requested: config.format,
available: caps.formats.clone(),
};
}
if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
break 'outer E::InvalidViewFormat(*format, config.format);
}
hal_view_formats.push(*format);
}
if !hal_view_formats.is_empty() {
if let Err(missing_flag) =
device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
{
break 'outer E::MissingDownlevelFlags(missing_flag);
}
}
let num_frames = present::DESIRED_NUM_FRAMES
.clamp(*caps.swap_chain_sizes.start(), *caps.swap_chain_sizes.end());
let mut hal_config = hal::SurfaceConfiguration {
@ -5348,6 +5373,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
depth_or_array_layers: 1,
},
usage: conv::map_texture_usage(config.usage, hal::FormatAspects::COLOR),
view_formats: hal_view_formats,
};
if let Err(error) = validate_surface_configuration(&mut hal_config, &caps) {

View File

@ -40,7 +40,10 @@ pub enum Action<'a> {
desc: crate::device::DeviceDescriptor<'a>,
backend: wgt::Backend,
},
ConfigureSurface(id::SurfaceId, wgt::SurfaceConfiguration),
ConfigureSurface(
id::SurfaceId,
wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
),
CreateBuffer(id::BufferId, crate::resource::BufferDescriptor<'a>),
FreeBuffer(id::BufferId),
DestroyBuffer(id::BufferId),

View File

@ -15,7 +15,7 @@ use std::borrow::Borrow;
use crate::device::trace::Action;
use crate::{
conv,
device::DeviceError,
device::{DeviceError, MissingDownlevelFlags},
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token},
id::{DeviceId, SurfaceId, TextureId, Valid},
init_tracker::TextureInitTracker,
@ -32,7 +32,7 @@ pub const DESIRED_NUM_FRAMES: u32 = 3;
#[derive(Debug)]
pub(crate) struct Presentation {
pub(crate) device_id: Stored<DeviceId>,
pub(crate) config: wgt::SurfaceConfiguration,
pub(crate) config: wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
#[allow(unused)]
pub(crate) num_frames: u32,
pub(crate) acquired_texture: Option<Stored<TextureId>>,
@ -64,6 +64,10 @@ pub enum ConfigureSurfaceError {
Device(#[from] DeviceError),
#[error("invalid surface")]
InvalidSurface,
#[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
#[error(transparent)]
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
#[error("`SurfaceOutput` must be dropped before a new `Surface` is made")]
PreviousOutputExists,
#[error("Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.")]
@ -180,7 +184,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
format: config.format,
dimension: wgt::TextureDimension::D2,
usage: config.usage,
view_formats: vec![],
view_formats: config.view_formats,
},
hal_usage: conv::map_texture_usage(config.usage, config.format.into()),
format_features: wgt::TextureFormatFeatures {

View File

@ -139,6 +139,7 @@ impl<A: hal::Api> Example<A> {
depth_or_array_layers: 1,
},
usage: hal::TextureUses::COLOR_TARGET,
view_formats: vec![],
};
unsafe {
surface.configure(&device, &surface_config).unwrap();

View File

@ -1113,6 +1113,9 @@ pub struct SurfaceConfiguration {
pub extent: wgt::Extent3d,
/// Allowed usage of surface textures,
pub usage: TextureUses,
/// Allows views of swapchain texture to have a different format
/// than the texture does.
pub view_formats: Vec<wgt::TextureFormat>,
}
#[derive(Debug, Clone)]

View File

@ -322,6 +322,10 @@ impl PhysicalDeviceFeatures {
| Df::VIEW_FORMATS
| Df::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES;
dl_flags.set(
Df::SURFACE_VIEW_FORMATS,
caps.supports_extension(vk::KhrSwapchainMutableFormatFn::name()),
);
dl_flags.set(Df::CUBE_ARRAY_TEXTURES, self.core.image_cube_array != 0);
dl_flags.set(Df::ANISOTROPIC_FILTERING, self.core.sampler_anisotropy != 0);
dl_flags.set(
@ -644,6 +648,11 @@ impl PhysicalDeviceCapabilities {
}
}
// Optional `VK_KHR_swapchain_mutable_format`
if self.supports_extension(vk::KhrSwapchainMutableFormatFn::name()) {
extensions.push(vk::KhrSwapchainMutableFormatFn::name());
}
// Optional `VK_EXT_robustness2`
if self.supports_extension(vk::ExtRobustness2Fn::name()) {
extensions.push(vk::ExtRobustness2Fn::name());

View File

@ -546,11 +546,25 @@ impl super::Device {
} else {
vk::ColorSpaceKHR::SRGB_NONLINEAR
};
let info = vk::SwapchainCreateInfoKHR::builder()
.flags(vk::SwapchainCreateFlagsKHR::empty())
let original_format = self.shared.private_caps.map_texture_format(config.format);
let mut raw_flags = vk::SwapchainCreateFlagsKHR::empty();
let mut raw_view_formats: Vec<vk::Format> = vec![];
if !config.view_formats.is_empty() {
raw_flags |= vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT;
raw_view_formats = config
.view_formats
.iter()
.map(|f| self.shared.private_caps.map_texture_format(*f))
.collect();
raw_view_formats.push(original_format);
}
let mut info = vk::SwapchainCreateInfoKHR::builder()
.flags(raw_flags)
.surface(surface.raw)
.min_image_count(config.swap_chain_size)
.image_format(self.shared.private_caps.map_texture_format(config.format))
.image_format(original_format)
.image_color_space(color_space)
.image_extent(vk::Extent2D {
width: config.extent.width,
@ -565,6 +579,12 @@ impl super::Device {
.clipped(true)
.old_swapchain(old_swapchain);
let mut format_list_info = vk::ImageFormatListCreateInfo::builder();
if !raw_view_formats.is_empty() {
format_list_info = format_list_info.view_formats(&raw_view_formats);
info = info.push_next(&mut format_list_info);
}
let result = {
profiling::scope!("vkCreateSwapchainKHR");
unsafe { functor.create_swapchain(&info, None) }

View File

@ -1159,6 +1159,11 @@ bitflags::bitflags! {
///
/// WebGL doesn't support this. WebGPU does.
const UNRESTRICTED_EXTERNAL_TEXTURE_COPIES = 1 << 20;
/// Supports specifying which view formats are allowed when calling create_view on the texture returned by get_current_texture.
///
/// The GLES/WebGL and Vulkan on Android doesn't support this.
const SURFACE_VIEW_FORMATS = 1 << 21;
}
}
@ -4007,7 +4012,7 @@ impl Default for SurfaceCapabilities {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SurfaceConfiguration {
pub struct SurfaceConfiguration<V> {
/// The usage of the swap chain. The only supported usage is `RENDER_ATTACHMENT`.
pub usage: TextureUsages,
/// The texture format of the swap chain. The only formats that are guaranteed are
@ -4024,6 +4029,27 @@ pub struct SurfaceConfiguration {
pub present_mode: PresentMode,
/// Specifies how the alpha channel of the textures should be handled during compositing.
pub alpha_mode: CompositeAlphaMode,
/// Specifies what view formats will be allowed when calling create_view() on texture returned by get_current_texture().
///
/// View formats of the same format as the texture are always allowed.
///
/// Note: currently, only the srgb-ness is allowed to change. (ex: Rgba8Unorm texture + Rgba8UnormSrgb view)
pub view_formats: V,
}
impl<V: Clone> SurfaceConfiguration<V> {
/// Map view_formats of the texture descriptor into another.
pub fn map_view_formats<M>(&self, fun: impl FnOnce(V) -> M) -> SurfaceConfiguration<M> {
SurfaceConfiguration {
usage: self.usage,
format: self.format,
width: self.width,
height: self.height,
present_mode: self.present_mode,
alpha_mode: self.alpha_mode,
view_formats: fun(self.view_formats.clone()),
}
}
}
/// Status of the recieved surface image.

View File

@ -547,6 +547,7 @@ pub fn test<E: Example>(mut params: FrameworkRefTest) {
height: params.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
},
&ctx.adapter,
&ctx.device,

View File

@ -77,6 +77,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: swapchain_capabilities.alpha_modes[0],
view_formats: &[],
};
surface.configure(&device, &config);

View File

@ -15,7 +15,7 @@ struct ViewportDesc {
struct Viewport {
desc: ViewportDesc,
config: wgpu::SurfaceConfiguration,
config: wgpu::SurfaceConfiguration<'static>,
}
impl ViewportDesc {
@ -39,6 +39,7 @@ impl ViewportDesc {
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: caps.alpha_modes[0],
view_formats: &[],
};
self.surface.configure(device, &config);

View File

@ -31,7 +31,7 @@ struct Example {
vertex_count: u32,
sample_count: u32,
rebuild_bundle: bool,
config: wgpu::SurfaceConfiguration,
config: wgpu::SurfaceConfiguration<'static>,
max_sample_count: u32,
}
@ -204,7 +204,10 @@ impl framework::Example for Example {
sample_count,
max_sample_count,
rebuild_bundle: false,
config: config.clone(),
config: wgpu::SurfaceConfiguration {
view_formats: &[],
..config.clone()
},
}
}
@ -242,7 +245,10 @@ impl framework::Example for Example {
device: &wgpu::Device,
_queue: &wgpu::Queue,
) {
self.config = config.clone();
self.config = wgpu::SurfaceConfiguration {
view_formats: &[],
..config.clone()
};
self.multisampled_framebuffer =
Example::create_multisampled_framebuffer(device, config, self.sample_count);
}

View File

@ -734,10 +734,10 @@ impl crate::Context for Context {
surface_data: &Self::SurfaceData,
device: &Self::DeviceId,
_device_data: &Self::DeviceData,
config: &wgt::SurfaceConfiguration,
config: &crate::SurfaceConfiguration,
) {
let global = &self.0;
let error = wgc::gfx_select!(device => global.surface_configure(*surface, *device, config));
let error = wgc::gfx_select!(device => global.surface_configure(*surface, *device, &config.map_view_formats(|v| v.to_vec())));
if let Some(e) = error {
self.handle_error_fatal(e, "Surface::configure");
} else {

View File

@ -1050,7 +1050,7 @@ impl crate::context::Context for Context {
_surface_data: &Self::SurfaceData,
device: &Self::DeviceId,
_device_data: &Self::DeviceData,
config: &wgt::SurfaceConfiguration,
config: &crate::SurfaceConfiguration,
) {
if let wgt::PresentMode::Mailbox | wgt::PresentMode::Immediate = config.present_mode {
panic!("Only FIFO/Auto* is supported on web");
@ -1068,6 +1068,12 @@ impl crate::context::Context for Context {
web_sys::GpuCanvasConfiguration::new(&device.0, map_texture_format(config.format));
mapped.usage(config.usage.bits());
mapped.alpha_mode(alpha_mode);
let mapped_view_formats = config
.view_formats
.iter()
.map(|format| JsValue::from(map_texture_format(*format)))
.collect::<js_sys::Array>();
mapped.view_formats(&mapped_view_formats);
surface.0.configure(&mapped);
}

View File

@ -3,8 +3,8 @@ use std::{any::Any, fmt::Debug, future::Future, num::NonZeroU64, ops::Range, pin
use wgt::{
strict_assert, strict_assert_eq, AdapterInfo, BufferAddress, BufferSize, Color,
DownlevelCapabilities, DynamicOffset, Extent3d, Features, ImageDataLayout,
ImageSubresourceRange, IndexFormat, Limits, ShaderStages, SurfaceConfiguration, SurfaceStatus,
TextureFormat, TextureFormatFeatures,
ImageSubresourceRange, IndexFormat, Limits, ShaderStages, SurfaceStatus, TextureFormat,
TextureFormatFeatures,
};
use crate::{
@ -162,7 +162,7 @@ pub trait Context: Debug + Send + Sized + Sync {
surface_data: &Self::SurfaceData,
device: &Self::DeviceId,
device_data: &Self::DeviceData,
config: &SurfaceConfiguration,
config: &crate::SurfaceConfiguration<'_>,
);
#[allow(clippy::type_complexity)]
fn surface_get_current_texture(
@ -1139,7 +1139,7 @@ pub(crate) trait DynContext: Debug + Send + Sync {
surface_data: &crate::Data,
device: &ObjectId,
device_data: &crate::Data,
config: &SurfaceConfiguration,
config: &crate::SurfaceConfiguration<'_>,
);
fn surface_get_current_texture(
&self,
@ -2044,7 +2044,7 @@ where
surface_data: &crate::Data,
device: &ObjectId,
device_data: &crate::Data,
config: &SurfaceConfiguration,
config: &crate::SurfaceConfiguration<'_>,
) {
let surface = <T::SurfaceId>::from(*surface);
let surface_data = downcast_ref(surface_data);

View File

@ -40,12 +40,11 @@ pub use wgt::{
PresentMode, PresentationTimestamp, PrimitiveState, PrimitiveTopology, PushConstantRange,
QueryType, RenderBundleDepthStencil, SamplerBindingType, SamplerBorderColor, ShaderLocation,
ShaderModel, ShaderStages, StencilFaceState, StencilOperation, StencilState,
StorageTextureAccess, SurfaceCapabilities, SurfaceConfiguration, SurfaceStatus, TextureAspect,
TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures,
TextureSampleType, TextureUsages, TextureViewDimension, VertexAttribute, VertexFormat,
VertexStepMode, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT,
PUSH_CONSTANT_ALIGNMENT, QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE,
VERTEX_STRIDE_ALIGNMENT,
StorageTextureAccess, SurfaceCapabilities, SurfaceStatus, TextureAspect, TextureDimension,
TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType,
TextureUsages, TextureViewDimension, VertexAttribute, VertexFormat, VertexStepMode,
COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,
};
// wasm-only types, we try to keep as many types non-platform
@ -279,6 +278,15 @@ impl Drop for Sampler {
}
}
/// Describes a [`Surface`].
///
/// For use with [`Surface::configure`].
///
/// Corresponds to [WebGPU `GPUCanvasConfiguration`](
/// https://gpuweb.github.io/gpuweb/#canvas-configuration).
pub type SurfaceConfiguration<'a> = wgt::SurfaceConfiguration<&'a [TextureFormat]>;
static_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);
/// Handle to a presentable surface.
///
/// A `Surface` represents a platform-specific surface (e.g. a window) onto which rendered images may
@ -298,7 +306,7 @@ pub struct Surface {
// Because the `Surface::configure` method operates on an immutable reference this type has to
// be wrapped in a mutex and since the configuration is only supplied after the surface has
// been created is is additionally wrapped in an option.
config: Mutex<Option<SurfaceConfiguration>>,
config: Mutex<Option<SurfaceConfiguration<'static>>>,
}
static_assertions::assert_impl_all!(Surface: Send, Sync);
@ -4082,15 +4090,16 @@ impl Surface {
adapter: &Adapter,
width: u32,
height: u32,
) -> Option<wgt::SurfaceConfiguration> {
) -> Option<SurfaceConfiguration<'static>> {
let caps = self.get_capabilities(adapter);
Some(wgt::SurfaceConfiguration {
Some(SurfaceConfiguration {
usage: wgt::TextureUsages::RENDER_ATTACHMENT,
format: *caps.formats.get(0)?,
width,
height,
present_mode: *caps.present_modes.get(0)?,
alpha_mode: wgt::CompositeAlphaMode::Auto,
view_formats: &[],
})
}
@ -4100,7 +4109,7 @@ impl Surface {
///
/// - A old [`SurfaceTexture`] is still alive referencing an old surface.
/// - Texture format requested is unsupported on the surface.
pub fn configure(&self, device: &Device, config: &SurfaceConfiguration) {
pub fn configure(&self, device: &Device, config: &SurfaceConfiguration<'static>) {
DynContext::surface_configure(
&*self.context,
&self.id,