mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Move surface_configure out of Global
This commit is contained in:
parent
21ea8f2979
commit
73d1334ba3
@ -1737,276 +1737,15 @@ impl Global {
|
||||
device_id: DeviceId,
|
||||
config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
|
||||
) -> Option<present::ConfigureSurfaceError> {
|
||||
use present::ConfigureSurfaceError as E;
|
||||
profiling::scope!("surface_configure");
|
||||
let device = self.hub.devices.get(device_id);
|
||||
let surface = self.surfaces.get(surface_id);
|
||||
|
||||
fn validate_surface_configuration(
|
||||
config: &mut hal::SurfaceConfiguration,
|
||||
caps: &hal::SurfaceCapabilities,
|
||||
max_texture_dimension_2d: u32,
|
||||
) -> Result<(), E> {
|
||||
let width = config.extent.width;
|
||||
let height = config.extent.height;
|
||||
|
||||
if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
|
||||
return Err(E::TooLarge {
|
||||
width,
|
||||
height,
|
||||
max_texture_dimension_2d,
|
||||
});
|
||||
}
|
||||
|
||||
if !caps.present_modes.contains(&config.present_mode) {
|
||||
// Automatic present mode checks.
|
||||
//
|
||||
// The "Automatic" modes are never supported by the backends.
|
||||
let fallbacks = match config.present_mode {
|
||||
wgt::PresentMode::AutoVsync => {
|
||||
&[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
|
||||
}
|
||||
// Always end in FIFO to make sure it's always supported
|
||||
wgt::PresentMode::AutoNoVsync => &[
|
||||
wgt::PresentMode::Immediate,
|
||||
wgt::PresentMode::Mailbox,
|
||||
wgt::PresentMode::Fifo,
|
||||
][..],
|
||||
_ => {
|
||||
return Err(E::UnsupportedPresentMode {
|
||||
requested: config.present_mode,
|
||||
available: caps.present_modes.clone(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let new_mode = fallbacks
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|fallback| caps.present_modes.contains(fallback))
|
||||
.unwrap_or_else(|| {
|
||||
unreachable!(
|
||||
"Fallback system failed to choose present mode. \
|
||||
This is a bug. Mode: {:?}, Options: {:?}",
|
||||
config.present_mode, &caps.present_modes
|
||||
);
|
||||
});
|
||||
|
||||
api_log!(
|
||||
"Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
|
||||
config.present_mode
|
||||
);
|
||||
config.present_mode = new_mode;
|
||||
}
|
||||
if !caps.formats.contains(&config.format) {
|
||||
return Err(E::UnsupportedFormat {
|
||||
requested: config.format,
|
||||
available: caps.formats.clone(),
|
||||
});
|
||||
}
|
||||
if !caps
|
||||
.composite_alpha_modes
|
||||
.contains(&config.composite_alpha_mode)
|
||||
{
|
||||
let new_alpha_mode = 'alpha: {
|
||||
// Automatic alpha mode checks.
|
||||
let fallbacks = match config.composite_alpha_mode {
|
||||
wgt::CompositeAlphaMode::Auto => &[
|
||||
wgt::CompositeAlphaMode::Opaque,
|
||||
wgt::CompositeAlphaMode::Inherit,
|
||||
][..],
|
||||
_ => {
|
||||
return Err(E::UnsupportedAlphaMode {
|
||||
requested: config.composite_alpha_mode,
|
||||
available: caps.composite_alpha_modes.clone(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
for &fallback in fallbacks {
|
||||
if caps.composite_alpha_modes.contains(&fallback) {
|
||||
break 'alpha fallback;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!(
|
||||
"Fallback system failed to choose alpha mode. This is a bug. \
|
||||
AlphaMode: {:?}, Options: {:?}",
|
||||
config.composite_alpha_mode, &caps.composite_alpha_modes
|
||||
);
|
||||
};
|
||||
|
||||
api_log!(
|
||||
"Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
|
||||
config.composite_alpha_mode
|
||||
);
|
||||
config.composite_alpha_mode = new_alpha_mode;
|
||||
}
|
||||
if !caps.usage.contains(config.usage) {
|
||||
return Err(E::UnsupportedUsage {
|
||||
requested: config.usage,
|
||||
available: caps.usage,
|
||||
});
|
||||
}
|
||||
if width == 0 || height == 0 {
|
||||
return Err(E::ZeroArea);
|
||||
}
|
||||
Ok(())
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref mut trace) = *device.trace.lock() {
|
||||
trace.add(trace::Action::ConfigureSurface(surface_id, config.clone()));
|
||||
}
|
||||
|
||||
log::debug!("configuring surface with {config:?}");
|
||||
|
||||
let error = 'error: {
|
||||
// User callbacks must not be called while we are holding locks.
|
||||
let user_callbacks;
|
||||
{
|
||||
let device = self.hub.devices.get(device_id);
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
if let Some(ref mut trace) = *device.trace.lock() {
|
||||
trace.add(trace::Action::ConfigureSurface(surface_id, config.clone()));
|
||||
}
|
||||
|
||||
if let Err(e) = device.check_is_valid() {
|
||||
break 'error e.into();
|
||||
}
|
||||
|
||||
let surface = self.surfaces.get(surface_id);
|
||||
|
||||
let caps = match surface.get_capabilities(&device.adapter) {
|
||||
Ok(caps) => caps,
|
||||
Err(_) => break 'error E::UnsupportedQueueFamily,
|
||||
};
|
||||
|
||||
let mut hal_view_formats = Vec::new();
|
||||
for format in config.view_formats.iter() {
|
||||
if *format == config.format {
|
||||
continue;
|
||||
}
|
||||
if !caps.formats.contains(&config.format) {
|
||||
break 'error E::UnsupportedFormat {
|
||||
requested: config.format,
|
||||
available: caps.formats,
|
||||
};
|
||||
}
|
||||
if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
|
||||
break 'error 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 'error E::MissingDownlevelFlags(missing_flag);
|
||||
}
|
||||
}
|
||||
|
||||
let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
|
||||
*caps.maximum_frame_latency.start(),
|
||||
*caps.maximum_frame_latency.end(),
|
||||
);
|
||||
let mut hal_config = hal::SurfaceConfiguration {
|
||||
maximum_frame_latency,
|
||||
present_mode: config.present_mode,
|
||||
composite_alpha_mode: config.alpha_mode,
|
||||
format: config.format,
|
||||
extent: wgt::Extent3d {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
usage: conv::map_texture_usage(
|
||||
config.usage,
|
||||
hal::FormatAspects::COLOR,
|
||||
wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
|
||||
| wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
|
||||
| wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
|
||||
),
|
||||
view_formats: hal_view_formats,
|
||||
};
|
||||
|
||||
if let Err(error) = validate_surface_configuration(
|
||||
&mut hal_config,
|
||||
&caps,
|
||||
device.limits.max_texture_dimension_2d,
|
||||
) {
|
||||
break 'error error;
|
||||
}
|
||||
|
||||
// Wait for all work to finish before configuring the surface.
|
||||
let snatch_guard = device.snatchable_lock.read();
|
||||
let fence = device.fence.read();
|
||||
|
||||
let maintain_result;
|
||||
(user_callbacks, maintain_result) =
|
||||
device.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);
|
||||
|
||||
match maintain_result {
|
||||
// We're happy
|
||||
Ok(wgt::PollStatus::QueueEmpty) => {}
|
||||
Ok(wgt::PollStatus::WaitSucceeded) => {
|
||||
// After the wait, the queue should be empty. It can only be non-empty
|
||||
// if another thread is submitting at the same time.
|
||||
break 'error E::GpuWaitTimeout;
|
||||
}
|
||||
Ok(wgt::PollStatus::Poll) => {
|
||||
unreachable!("Cannot get a Poll result from a Wait action.")
|
||||
}
|
||||
Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
|
||||
// On wasm, you cannot actually successfully wait for the surface.
|
||||
// However WebGL does not actually require you do this, so ignoring
|
||||
// the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
|
||||
}
|
||||
Err(e) => {
|
||||
break 'error e.into();
|
||||
}
|
||||
}
|
||||
|
||||
// All textures must be destroyed before the surface can be re-configured.
|
||||
if let Some(present) = surface.presentation.lock().take() {
|
||||
if present.acquired_texture.is_some() {
|
||||
break 'error E::PreviousOutputExists;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Texture views may still be alive that point to the texture.
|
||||
// this will allow the user to render to the surface texture, long after
|
||||
// it has been removed.
|
||||
//
|
||||
// https://github.com/gfx-rs/wgpu/issues/4105
|
||||
|
||||
let surface_raw = surface.raw(device.backend()).unwrap();
|
||||
match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
|
||||
Ok(()) => (),
|
||||
Err(error) => {
|
||||
break 'error match error {
|
||||
hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
|
||||
E::InvalidSurface
|
||||
}
|
||||
hal::SurfaceError::Device(error) => {
|
||||
E::Device(device.handle_hal_error(error))
|
||||
}
|
||||
hal::SurfaceError::Other(message) => {
|
||||
log::error!("surface configuration failed: {message}");
|
||||
E::InvalidSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut presentation = surface.presentation.lock();
|
||||
*presentation = Some(present::Presentation {
|
||||
device,
|
||||
config: config.clone(),
|
||||
acquired_texture: None,
|
||||
});
|
||||
}
|
||||
|
||||
user_callbacks.fire();
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(error)
|
||||
device.configure_surface(&surface, config)
|
||||
}
|
||||
|
||||
/// Check `device_id` for freeable resources and completed buffer mappings.
|
||||
|
||||
@ -24,6 +24,7 @@ use wgt::{
|
||||
#[cfg(feature = "trace")]
|
||||
use crate::device::trace;
|
||||
use crate::{
|
||||
api_log,
|
||||
binding_model::{self, BindGroup, BindGroupLayout, BindGroupLayoutEntryError},
|
||||
command, conv,
|
||||
device::{
|
||||
@ -39,6 +40,7 @@ use crate::{
|
||||
lock::{rank, Mutex, RwLock},
|
||||
pipeline,
|
||||
pool::ResourcePool,
|
||||
present,
|
||||
resource::{
|
||||
self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet,
|
||||
RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView,
|
||||
@ -4693,6 +4695,274 @@ impl Device {
|
||||
Ok(query_set)
|
||||
}
|
||||
|
||||
pub fn configure_surface(
|
||||
self: &Arc<Self>,
|
||||
surface: &crate::instance::Surface,
|
||||
config: &wgt::SurfaceConfiguration<Vec<TextureFormat>>,
|
||||
) -> Option<present::ConfigureSurfaceError> {
|
||||
use present::ConfigureSurfaceError as E;
|
||||
profiling::scope!("surface_configure");
|
||||
|
||||
fn validate_surface_configuration(
|
||||
config: &mut hal::SurfaceConfiguration,
|
||||
caps: &hal::SurfaceCapabilities,
|
||||
max_texture_dimension_2d: u32,
|
||||
) -> Result<(), E> {
|
||||
let width = config.extent.width;
|
||||
let height = config.extent.height;
|
||||
|
||||
if width > max_texture_dimension_2d || height > max_texture_dimension_2d {
|
||||
return Err(E::TooLarge {
|
||||
width,
|
||||
height,
|
||||
max_texture_dimension_2d,
|
||||
});
|
||||
}
|
||||
|
||||
if !caps.present_modes.contains(&config.present_mode) {
|
||||
// Automatic present mode checks.
|
||||
//
|
||||
// The "Automatic" modes are never supported by the backends.
|
||||
let fallbacks = match config.present_mode {
|
||||
wgt::PresentMode::AutoVsync => {
|
||||
&[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..]
|
||||
}
|
||||
// Always end in FIFO to make sure it's always supported
|
||||
wgt::PresentMode::AutoNoVsync => &[
|
||||
wgt::PresentMode::Immediate,
|
||||
wgt::PresentMode::Mailbox,
|
||||
wgt::PresentMode::Fifo,
|
||||
][..],
|
||||
_ => {
|
||||
return Err(E::UnsupportedPresentMode {
|
||||
requested: config.present_mode,
|
||||
available: caps.present_modes.clone(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let new_mode = fallbacks
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|fallback| caps.present_modes.contains(fallback))
|
||||
.unwrap_or_else(|| {
|
||||
unreachable!(
|
||||
"Fallback system failed to choose present mode. \
|
||||
This is a bug. Mode: {:?}, Options: {:?}",
|
||||
config.present_mode, &caps.present_modes
|
||||
);
|
||||
});
|
||||
|
||||
api_log!(
|
||||
"Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}",
|
||||
config.present_mode
|
||||
);
|
||||
config.present_mode = new_mode;
|
||||
}
|
||||
if !caps.formats.contains(&config.format) {
|
||||
return Err(E::UnsupportedFormat {
|
||||
requested: config.format,
|
||||
available: caps.formats.clone(),
|
||||
});
|
||||
}
|
||||
if !caps
|
||||
.composite_alpha_modes
|
||||
.contains(&config.composite_alpha_mode)
|
||||
{
|
||||
let new_alpha_mode = 'alpha: {
|
||||
// Automatic alpha mode checks.
|
||||
let fallbacks = match config.composite_alpha_mode {
|
||||
wgt::CompositeAlphaMode::Auto => &[
|
||||
wgt::CompositeAlphaMode::Opaque,
|
||||
wgt::CompositeAlphaMode::Inherit,
|
||||
][..],
|
||||
_ => {
|
||||
return Err(E::UnsupportedAlphaMode {
|
||||
requested: config.composite_alpha_mode,
|
||||
available: caps.composite_alpha_modes.clone(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
for &fallback in fallbacks {
|
||||
if caps.composite_alpha_modes.contains(&fallback) {
|
||||
break 'alpha fallback;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!(
|
||||
"Fallback system failed to choose alpha mode. This is a bug. \
|
||||
AlphaMode: {:?}, Options: {:?}",
|
||||
config.composite_alpha_mode, &caps.composite_alpha_modes
|
||||
);
|
||||
};
|
||||
|
||||
api_log!(
|
||||
"Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}",
|
||||
config.composite_alpha_mode
|
||||
);
|
||||
config.composite_alpha_mode = new_alpha_mode;
|
||||
}
|
||||
if !caps.usage.contains(config.usage) {
|
||||
return Err(E::UnsupportedUsage {
|
||||
requested: config.usage,
|
||||
available: caps.usage,
|
||||
});
|
||||
}
|
||||
if width == 0 || height == 0 {
|
||||
return Err(E::ZeroArea);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
log::debug!("configuring surface with {config:?}");
|
||||
|
||||
let error = 'error: {
|
||||
// User callbacks must not be called while we are holding locks.
|
||||
let user_callbacks;
|
||||
{
|
||||
if let Err(e) = self.check_is_valid() {
|
||||
break 'error e.into();
|
||||
}
|
||||
|
||||
let caps = match surface.get_capabilities(&self.adapter) {
|
||||
Ok(caps) => caps,
|
||||
Err(_) => break 'error E::UnsupportedQueueFamily,
|
||||
};
|
||||
|
||||
let mut hal_view_formats = Vec::new();
|
||||
for format in config.view_formats.iter() {
|
||||
if *format == config.format {
|
||||
continue;
|
||||
}
|
||||
if !caps.formats.contains(&config.format) {
|
||||
break 'error E::UnsupportedFormat {
|
||||
requested: config.format,
|
||||
available: caps.formats,
|
||||
};
|
||||
}
|
||||
if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
|
||||
break 'error E::InvalidViewFormat(*format, config.format);
|
||||
}
|
||||
hal_view_formats.push(*format);
|
||||
}
|
||||
|
||||
if !hal_view_formats.is_empty() {
|
||||
if let Err(missing_flag) =
|
||||
self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
|
||||
{
|
||||
break 'error E::MissingDownlevelFlags(missing_flag);
|
||||
}
|
||||
}
|
||||
|
||||
let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
|
||||
*caps.maximum_frame_latency.start(),
|
||||
*caps.maximum_frame_latency.end(),
|
||||
);
|
||||
let mut hal_config = hal::SurfaceConfiguration {
|
||||
maximum_frame_latency,
|
||||
present_mode: config.present_mode,
|
||||
composite_alpha_mode: config.alpha_mode,
|
||||
format: config.format,
|
||||
extent: wgt::Extent3d {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
usage: conv::map_texture_usage(
|
||||
config.usage,
|
||||
hal::FormatAspects::COLOR,
|
||||
wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
|
||||
| wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
|
||||
| wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
|
||||
),
|
||||
view_formats: hal_view_formats,
|
||||
};
|
||||
|
||||
if let Err(error) = validate_surface_configuration(
|
||||
&mut hal_config,
|
||||
&caps,
|
||||
self.limits.max_texture_dimension_2d,
|
||||
) {
|
||||
break 'error error;
|
||||
}
|
||||
|
||||
// Wait for all work to finish before configuring the surface.
|
||||
let snatch_guard = self.snatchable_lock.read();
|
||||
let fence = self.fence.read();
|
||||
|
||||
let maintain_result;
|
||||
(user_callbacks, maintain_result) =
|
||||
self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard);
|
||||
|
||||
match maintain_result {
|
||||
// We're happy
|
||||
Ok(wgt::PollStatus::QueueEmpty) => {}
|
||||
Ok(wgt::PollStatus::WaitSucceeded) => {
|
||||
// After the wait, the queue should be empty. It can only be non-empty
|
||||
// if another thread is submitting at the same time.
|
||||
break 'error E::GpuWaitTimeout;
|
||||
}
|
||||
Ok(wgt::PollStatus::Poll) => {
|
||||
unreachable!("Cannot get a Poll result from a Wait action.")
|
||||
}
|
||||
Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
|
||||
// On wasm, you cannot actually successfully wait for the surface.
|
||||
// However WebGL does not actually require you do this, so ignoring
|
||||
// the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363
|
||||
}
|
||||
Err(e) => {
|
||||
break 'error e.into();
|
||||
}
|
||||
}
|
||||
|
||||
// All textures must be destroyed before the surface can be re-configured.
|
||||
if let Some(present) = surface.presentation.lock().take() {
|
||||
if present.acquired_texture.is_some() {
|
||||
break 'error E::PreviousOutputExists;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Texture views may still be alive that point to the texture.
|
||||
// this will allow the user to render to the surface texture, long after
|
||||
// it has been removed.
|
||||
//
|
||||
// https://github.com/gfx-rs/wgpu/issues/4105
|
||||
|
||||
let surface_raw = surface.raw(self.backend()).unwrap();
|
||||
match unsafe { surface_raw.configure(self.raw(), &hal_config) } {
|
||||
Ok(()) => (),
|
||||
Err(error) => {
|
||||
break 'error match error {
|
||||
hal::SurfaceError::Outdated | hal::SurfaceError::Lost => {
|
||||
E::InvalidSurface
|
||||
}
|
||||
hal::SurfaceError::Device(error) => {
|
||||
E::Device(self.handle_hal_error(error))
|
||||
}
|
||||
hal::SurfaceError::Other(message) => {
|
||||
log::error!("surface configuration failed: {message}");
|
||||
E::InvalidSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut presentation = surface.presentation.lock();
|
||||
*presentation = Some(present::Presentation {
|
||||
device: Arc::clone(self),
|
||||
config: config.clone(),
|
||||
acquired_texture: None,
|
||||
});
|
||||
}
|
||||
|
||||
user_callbacks.fire();
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(error)
|
||||
}
|
||||
|
||||
fn lose(&self, message: &str) {
|
||||
// Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user