Skip to content

Commit 0c3ca5c

Browse files
authored
Fix Metal Mipmap Behvior (#3610)
1 parent a502282 commit 0c3ca5c

File tree

16 files changed

+156
-108
lines changed

16 files changed

+156
-108
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Bottom level categories:
3939
-->
4040

4141
## Unreleased
42+
4243
### Major changes
4344

4445
#### TextureFormat info API
@@ -81,6 +82,21 @@ The following `Features` have been renamed.
8182

8283
By @teoxoy in [#3534](https://github.com/gfx-rs/wgpu/pull/3534)
8384

85+
#### Anisotropic Filtering
86+
87+
Anisotropic filtering has been brought in line with the spec. The anisotropic clamp is now a u16 (was a `Option<u8>`) which must be at least 1.
88+
89+
If the anisotropy clamp is not 1, all the filters in a sampler must be `Linear`.
90+
91+
```diff
92+
SamplerDescriptor {
93+
- anisotropic_clamp: None,
94+
+ anisotropic_clamp: 1,
95+
}
96+
```
97+
98+
By @cwfitzgerald in [#3610](https://github.com/gfx-rs/wgpu/pull/3610).
99+
84100
#### General
85101

86102
- Change type of `mip_level_count` and `array_layer_count` (members of `TextureViewDescriptor` and `ImageSubresourceRange`) from `Option<NonZeroU32>` to `Option<u32>`. By @teoxoy in [#3445](https://github.com/gfx-rs/wgpu/pull/3445)
@@ -113,6 +129,9 @@ By @teoxoy in [#3534](https://github.com/gfx-rs/wgpu/pull/3534)
113129

114130
### Bug Fixes
115131

132+
#### Metal
133+
- Fix incorrect mipmap being sampled when using `MinLod <= 0.0` and `MaxLod >= 32.0` or when the fragment shader samples different Lods in the same quad. By @cwfitzgerald in [#3610](https://github.com/gfx-rs/wgpu/pull/3610).
134+
116135
#### DX12
117136

118137
- Fix DXC validation issues when using a custom `dxil_path`. By @Elabajaba in [#3434](https://github.com/gfx-rs/wgpu/pull/3434)

deno_webgpu/sampler.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub struct CreateSamplerArgs {
4040
lod_min_clamp: f32,
4141
lod_max_clamp: f32,
4242
compare: Option<wgpu_types::CompareFunction>,
43-
max_anisotropy: u8,
43+
max_anisotropy: u16,
4444
}
4545

4646
#[op]
@@ -67,7 +67,7 @@ pub fn op_webgpu_create_sampler(
6767
lod_min_clamp: args.lod_min_clamp,
6868
lod_max_clamp: args.lod_max_clamp,
6969
compare: args.compare,
70-
anisotropy_clamp: std::num::NonZeroU8::new(args.max_anisotropy),
70+
anisotropy_clamp: args.max_anisotropy,
7171
border_color: None, // native-only
7272
};
7373

wgpu-core/src/device/mod.rs

+52-24
Original file line numberDiff line numberDiff line change
@@ -1310,36 +1310,64 @@ impl<A: HalApi> Device<A> {
13101310
self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
13111311
}
13121312

1313-
if desc.lod_min_clamp < 0.0 || desc.lod_max_clamp < desc.lod_min_clamp {
1314-
return Err(resource::CreateSamplerError::InvalidLodClamp(
1315-
desc.lod_min_clamp..desc.lod_max_clamp,
1313+
if desc.lod_min_clamp < 0.0 {
1314+
return Err(resource::CreateSamplerError::InvalidLodMinClamp(
1315+
desc.lod_min_clamp,
13161316
));
13171317
}
1318+
if desc.lod_max_clamp < desc.lod_min_clamp {
1319+
return Err(resource::CreateSamplerError::InvalidLodMaxClamp {
1320+
lod_min_clamp: desc.lod_min_clamp,
1321+
lod_max_clamp: desc.lod_max_clamp,
1322+
});
1323+
}
13181324

1319-
let lod_clamp = if desc.lod_min_clamp > 0.0 || desc.lod_max_clamp < 32.0 {
1320-
Some(desc.lod_min_clamp..desc.lod_max_clamp)
1321-
} else {
1322-
None
1323-
};
1325+
if desc.anisotropy_clamp < 1 {
1326+
return Err(resource::CreateSamplerError::InvalidAnisotropy(
1327+
desc.anisotropy_clamp,
1328+
));
1329+
}
13241330

1325-
let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp {
1326-
let clamp = clamp.get();
1327-
let valid_clamp =
1328-
clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two_u32(clamp as u32);
1329-
if !valid_clamp {
1330-
return Err(resource::CreateSamplerError::InvalidClamp(clamp));
1331+
if desc.anisotropy_clamp != 1 {
1332+
if !matches!(desc.min_filter, wgt::FilterMode::Linear) {
1333+
return Err(
1334+
resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1335+
filter_type: resource::SamplerFilterErrorType::MinFilter,
1336+
filter_mode: desc.min_filter,
1337+
anisotropic_clamp: desc.anisotropy_clamp,
1338+
},
1339+
);
13311340
}
1332-
if self
1333-
.downlevel
1334-
.flags
1335-
.contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
1336-
{
1337-
std::num::NonZeroU8::new(clamp)
1338-
} else {
1339-
None
1341+
if !matches!(desc.mag_filter, wgt::FilterMode::Linear) {
1342+
return Err(
1343+
resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1344+
filter_type: resource::SamplerFilterErrorType::MagFilter,
1345+
filter_mode: desc.mag_filter,
1346+
anisotropic_clamp: desc.anisotropy_clamp,
1347+
},
1348+
);
1349+
}
1350+
if !matches!(desc.mipmap_filter, wgt::FilterMode::Linear) {
1351+
return Err(
1352+
resource::CreateSamplerError::InvalidFilterModeWithAnisotropy {
1353+
filter_type: resource::SamplerFilterErrorType::MipmapFilter,
1354+
filter_mode: desc.mipmap_filter,
1355+
anisotropic_clamp: desc.anisotropy_clamp,
1356+
},
1357+
);
13401358
}
1359+
}
1360+
1361+
let anisotropy_clamp = if self
1362+
.downlevel
1363+
.flags
1364+
.contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING)
1365+
{
1366+
// Clamp anisotropy clamp to [1, 16] per the wgpu-hal interface
1367+
desc.anisotropy_clamp.min(16)
13411368
} else {
1342-
None
1369+
// If it isn't supported, set this unconditionally to 1
1370+
1
13431371
};
13441372

13451373
//TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS
@@ -1350,7 +1378,7 @@ impl<A: HalApi> Device<A> {
13501378
mag_filter: desc.mag_filter,
13511379
min_filter: desc.min_filter,
13521380
mipmap_filter: desc.mipmap_filter,
1353-
lod_clamp,
1381+
lod_clamp: desc.lod_min_clamp..desc.lod_max_clamp,
13541382
compare: desc.compare,
13551383
anisotropy_clamp,
13561384
border_color: desc.border_color,

wgpu-core/src/resource.rs

+35-24
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
use smallvec::SmallVec;
1212
use thiserror::Error;
1313

14-
use std::{borrow::Borrow, num::NonZeroU8, ops::Range, ptr::NonNull};
14+
use std::{borrow::Borrow, ops::Range, ptr::NonNull};
1515

1616
/// The status code provided to the buffer mapping callback.
1717
///
@@ -689,30 +689,13 @@ pub struct SamplerDescriptor<'a> {
689689
pub lod_max_clamp: f32,
690690
/// If this is enabled, this is a comparison sampler using the given comparison function.
691691
pub compare: Option<wgt::CompareFunction>,
692-
/// Valid values: 1, 2, 4, 8, and 16.
693-
pub anisotropy_clamp: Option<NonZeroU8>,
692+
/// Must be at least 1. If this is not 1, all filter modes must be linear.
693+
pub anisotropy_clamp: u16,
694694
/// Border color to use when address_mode is
695695
/// [`AddressMode::ClampToBorder`](wgt::AddressMode::ClampToBorder)
696696
pub border_color: Option<wgt::SamplerBorderColor>,
697697
}
698698

699-
impl Default for SamplerDescriptor<'_> {
700-
fn default() -> Self {
701-
Self {
702-
label: None,
703-
address_modes: Default::default(),
704-
mag_filter: Default::default(),
705-
min_filter: Default::default(),
706-
mipmap_filter: Default::default(),
707-
lod_min_clamp: 0.0,
708-
lod_max_clamp: std::f32::MAX,
709-
compare: None,
710-
anisotropy_clamp: None,
711-
border_color: None,
712-
}
713-
}
714-
}
715-
716699
#[derive(Debug)]
717700
pub struct Sampler<A: hal::Api> {
718701
pub(crate) raw: A::Sampler,
@@ -724,14 +707,42 @@ pub struct Sampler<A: hal::Api> {
724707
pub(crate) filtering: bool,
725708
}
726709

710+
#[derive(Copy, Clone)]
711+
pub enum SamplerFilterErrorType {
712+
MagFilter,
713+
MinFilter,
714+
MipmapFilter,
715+
}
716+
717+
impl std::fmt::Debug for SamplerFilterErrorType {
718+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
719+
match *self {
720+
SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
721+
SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
722+
SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
723+
}
724+
}
725+
}
726+
727727
#[derive(Clone, Debug, Error)]
728728
pub enum CreateSamplerError {
729729
#[error(transparent)]
730730
Device(#[from] DeviceError),
731-
#[error("Invalid lod clamp lod_min_clamp:{} lod_max_clamp:{}, must satisfy lod_min_clamp >= 0 and lod_max_clamp >= lod_min_clamp ", .0.start, .0.end)]
732-
InvalidLodClamp(Range<f32>),
733-
#[error("Invalid anisotropic clamp {0}, must be one of 1, 2, 4, 8 or 16")]
734-
InvalidClamp(u8),
731+
#[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
732+
InvalidLodMinClamp(f32),
733+
#[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
734+
InvalidLodMaxClamp {
735+
lod_min_clamp: f32,
736+
lod_max_clamp: f32,
737+
},
738+
#[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
739+
InvalidAnisotropy(u16),
740+
#[error("Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.")]
741+
InvalidFilterModeWithAnisotropy {
742+
filter_type: SamplerFilterErrorType,
743+
filter_mode: wgt::FilterMode,
744+
anisotropic_clamp: u16,
745+
},
735746
#[error("Cannot create any more samplers")]
736747
TooManyObjects,
737748
/// AddressMode::ClampToBorder requires feature ADDRESS_MODE_CLAMP_TO_BORDER.

wgpu-hal/examples/halmark/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,9 @@ impl<A: hal::Api> Example<A> {
355355
mag_filter: wgt::FilterMode::Linear,
356356
min_filter: wgt::FilterMode::Nearest,
357357
mipmap_filter: wgt::FilterMode::Nearest,
358-
lod_clamp: None,
358+
lod_clamp: 0.0..32.0,
359359
compare: None,
360-
anisotropy_clamp: None,
360+
anisotropy_clamp: 1,
361361
border_color: None,
362362
};
363363
let sampler = unsafe { device.create_sampler(&sampler_desc).unwrap() };

wgpu-hal/src/dx12/device.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -583,13 +583,14 @@ impl crate::Device<super::Api> for super::Device {
583583
Some(_) => d3d12_ty::D3D12_FILTER_REDUCTION_TYPE_COMPARISON,
584584
None => d3d12_ty::D3D12_FILTER_REDUCTION_TYPE_STANDARD,
585585
};
586-
let filter = conv::map_filter_mode(desc.min_filter) << d3d12_ty::D3D12_MIN_FILTER_SHIFT
586+
let mut filter = conv::map_filter_mode(desc.min_filter) << d3d12_ty::D3D12_MIN_FILTER_SHIFT
587587
| conv::map_filter_mode(desc.mag_filter) << d3d12_ty::D3D12_MAG_FILTER_SHIFT
588588
| conv::map_filter_mode(desc.mipmap_filter) << d3d12_ty::D3D12_MIP_FILTER_SHIFT
589-
| reduction << d3d12_ty::D3D12_FILTER_REDUCTION_TYPE_SHIFT
590-
| desc
591-
.anisotropy_clamp
592-
.map_or(0, |_| d3d12_ty::D3D12_FILTER_ANISOTROPIC);
589+
| reduction << d3d12_ty::D3D12_FILTER_REDUCTION_TYPE_SHIFT;
590+
591+
if desc.anisotropy_clamp != 1 {
592+
filter |= d3d12_ty::D3D12_FILTER_ANISOTROPIC;
593+
};
593594

594595
let border_color = conv::map_border_color(desc.border_color);
595596

@@ -602,10 +603,10 @@ impl crate::Device<super::Api> for super::Device {
602603
conv::map_address_mode(desc.address_modes[2]),
603604
],
604605
0.0,
605-
desc.anisotropy_clamp.map_or(0, |aniso| aniso.get() as u32),
606+
desc.anisotropy_clamp as u32,
606607
conv::map_comparison(desc.compare.unwrap_or(wgt::CompareFunction::Always)),
607608
border_color,
608-
desc.lod_clamp.clone().unwrap_or(0.0..16.0),
609+
desc.lod_clamp.clone(),
609610
);
610611

611612
Ok(super::Sampler { handle })

wgpu-hal/src/gles/adapter.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,11 @@ impl super::Adapter {
315315
&& (vertex_shader_storage_blocks != 0 || vertex_ssbo_false_zero),
316316
);
317317
downlevel_flags.set(wgt::DownlevelFlags::FRAGMENT_STORAGE, supports_storage);
318-
downlevel_flags.set(
319-
wgt::DownlevelFlags::ANISOTROPIC_FILTERING,
320-
extensions.contains("EXT_texture_filter_anisotropic"),
321-
);
318+
if extensions.contains("EXT_texture_filter_anisotropic") {
319+
let max_aniso =
320+
unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_MAX_ANISOTROPY_EXT) } as u32;
321+
downlevel_flags.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, max_aniso >= 16);
322+
}
322323
downlevel_flags.set(
323324
wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
324325
!(cfg!(target_arch = "wasm32") || is_angle),

wgpu-hal/src/gles/device.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -864,14 +864,17 @@ impl crate::Device<super::Api> for super::Device {
864864
unsafe { gl.sampler_parameter_f32_slice(raw, glow::TEXTURE_BORDER_COLOR, &border) };
865865
}
866866

867-
if let Some(ref range) = desc.lod_clamp {
868-
unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, range.start) };
869-
unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, range.end) };
870-
}
867+
unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MIN_LOD, desc.lod_clamp.start) };
868+
unsafe { gl.sampler_parameter_f32(raw, glow::TEXTURE_MAX_LOD, desc.lod_clamp.end) };
871869

872-
if let Some(anisotropy) = desc.anisotropy_clamp {
870+
// If clamp is not 1, we know anisotropy is supported up to 16x
871+
if desc.anisotropy_clamp != 1 {
873872
unsafe {
874-
gl.sampler_parameter_i32(raw, glow::TEXTURE_MAX_ANISOTROPY, anisotropy.get() as i32)
873+
gl.sampler_parameter_i32(
874+
raw,
875+
glow::TEXTURE_MAX_ANISOTROPY,
876+
desc.anisotropy_clamp as i32,
877+
)
875878
};
876879
}
877880

wgpu-hal/src/lib.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub mod api {
8787
use std::{
8888
borrow::{Borrow, Cow},
8989
fmt,
90-
num::{NonZeroU32, NonZeroU8},
90+
num::NonZeroU32,
9191
ops::{Range, RangeInclusive},
9292
ptr::NonNull,
9393
sync::atomic::AtomicBool,
@@ -919,9 +919,12 @@ pub struct SamplerDescriptor<'a> {
919919
pub mag_filter: wgt::FilterMode,
920920
pub min_filter: wgt::FilterMode,
921921
pub mipmap_filter: wgt::FilterMode,
922-
pub lod_clamp: Option<Range<f32>>,
922+
pub lod_clamp: Range<f32>,
923923
pub compare: Option<wgt::CompareFunction>,
924-
pub anisotropy_clamp: Option<NonZeroU8>,
924+
// Must in the range [1, 16].
925+
//
926+
// Anisotropic filtering must be supported if this is not 1.
927+
pub anisotropy_clamp: u16,
925928
pub border_color: Option<wgt::SamplerBorderColor>,
926929
}
927930

wgpu-hal/src/metal/adapter.rs

-1
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,6 @@ impl super::PrivateCapabilities {
540540
MUTABLE_COMPARISON_SAMPLER_SUPPORT,
541541
),
542542
sampler_clamp_to_border: Self::supports_any(device, SAMPLER_CLAMP_TO_BORDER_SUPPORT),
543-
sampler_lod_average: { version.at_least((11, 0), (9, 0), os_is_mac) },
544543
base_instance: Self::supports_any(device, BASE_INSTANCE_SUPPORT),
545544
base_vertex_instance_drawing: Self::supports_any(device, BASE_VERTEX_INSTANCE_SUPPORT),
546545
dual_source_blending: Self::supports_any(device, DUAL_SOURCE_BLEND_SUPPORT),

wgpu-hal/src/metal/device.rs

+5-13
Original file line numberDiff line numberDiff line change
@@ -423,14 +423,13 @@ impl crate::Device<super::Api> for super::Device {
423423
&self,
424424
desc: &crate::SamplerDescriptor,
425425
) -> DeviceResult<super::Sampler> {
426-
let caps = &self.shared.private_caps;
427426
objc::rc::autoreleasepool(|| {
428427
let descriptor = metal::SamplerDescriptor::new();
429428

430429
descriptor.set_min_filter(conv::map_filter_mode(desc.min_filter));
431430
descriptor.set_mag_filter(conv::map_filter_mode(desc.mag_filter));
432431
descriptor.set_mip_filter(match desc.mipmap_filter {
433-
wgt::FilterMode::Nearest if desc.lod_clamp.is_none() => {
432+
wgt::FilterMode::Nearest if desc.lod_clamp == (0.0..0.0) => {
434433
metal::MTLSamplerMipFilter::NotMipmapped
435434
}
436435
wgt::FilterMode::Nearest => metal::MTLSamplerMipFilter::Nearest,
@@ -442,18 +441,11 @@ impl crate::Device<super::Api> for super::Device {
442441
descriptor.set_address_mode_t(conv::map_address_mode(t));
443442
descriptor.set_address_mode_r(conv::map_address_mode(r));
444443

445-
if let Some(aniso) = desc.anisotropy_clamp {
446-
descriptor.set_max_anisotropy(aniso.get() as _);
447-
}
448-
449-
if let Some(ref range) = desc.lod_clamp {
450-
descriptor.set_lod_min_clamp(range.start);
451-
descriptor.set_lod_max_clamp(range.end);
452-
}
444+
// Anisotropy is always supported on mac up to 16x
445+
descriptor.set_max_anisotropy(desc.anisotropy_clamp as _);
453446

454-
if caps.sampler_lod_average {
455-
descriptor.set_lod_average(true); // optimization
456-
}
447+
descriptor.set_lod_min_clamp(desc.lod_clamp.start);
448+
descriptor.set_lod_max_clamp(desc.lod_clamp.end);
457449

458450
if let Some(fun) = desc.compare {
459451
descriptor.set_compare_function(conv::map_compare_function(fun));

0 commit comments

Comments
 (0)