@@ -528,6 +528,7 @@ pub struct SamplerBuilder {
528528 address_w : AddressMode ,
529529 lod_min : f32 ,
530530 lod_max : f32 ,
531+ anisotropy_clamp : u16 ,
531532}
532533
533534impl Default for SamplerBuilder {
@@ -549,6 +550,7 @@ impl SamplerBuilder {
549550 address_w : AddressMode :: ClampToEdge ,
550551 lod_min : 0.0 ,
551552 lod_max : 32.0 ,
553+ anisotropy_clamp : 1 ,
552554 } ;
553555 }
554556
@@ -621,7 +623,65 @@ impl SamplerBuilder {
621623 return self ;
622624 }
623625
624- fn to_descriptor ( & self ) -> wgpu:: SamplerDescriptor < ' _ > {
626+ /// Set the maximum anisotropic filtering level.
627+ ///
628+ /// Valid values are `1` (disabled) through `16`. Values outside this range
629+ /// are clamped. Higher values improve texture quality at oblique viewing
630+ /// angles but increase GPU cost.
631+ ///
632+ /// Common values:
633+ /// - `1`: Disabled (default)
634+ /// - `4`: Good balance of quality and performance
635+ /// - `8`: High quality
636+ /// - `16`: Maximum quality
637+ ///
638+ /// Note: Anisotropic filtering is most effective with linear filtering and
639+ /// mipmapped textures. wgpu also requires all filter modes to be linear when
640+ /// anisotropy is enabled; otherwise anisotropy is disabled.
641+ ///
642+ /// ```no_run
643+ /// # use lambda_platform::wgpu::texture::{FilterMode, SamplerBuilder};
644+ /// # fn demo(gpu: &lambda_platform::wgpu::gpu::Gpu) {
645+ /// // High-quality sampler for floor/wall textures viewed at angles
646+ /// let aniso_sampler = SamplerBuilder::new()
647+ /// .linear_clamp()
648+ /// .with_mip_filter(FilterMode::Linear)
649+ /// .with_anisotropy_clamp(8)
650+ /// .build(gpu);
651+ ///
652+ /// // Default sampler (no anisotropy) for UI textures
653+ /// let ui_sampler = SamplerBuilder::new().linear_clamp().build(gpu);
654+ /// # let _ = (aniso_sampler, ui_sampler);
655+ /// # }
656+ /// ```
657+ pub fn with_anisotropy_clamp ( mut self , clamp : u16 ) -> Self {
658+ self . anisotropy_clamp = clamp. clamp ( 1 , 16 ) ;
659+ return self ;
660+ }
661+
662+ fn to_descriptor (
663+ & self ,
664+ max_supported_anisotropy : u16 ,
665+ ) -> wgpu:: SamplerDescriptor < ' _ > {
666+ let max_supported_anisotropy = max_supported_anisotropy. clamp ( 1 , 16 ) ;
667+ let mut anisotropy_clamp = self
668+ . anisotropy_clamp
669+ . clamp ( 1 , 16 )
670+ . min ( max_supported_anisotropy) ;
671+ if anisotropy_clamp > 1
672+ && !matches ! (
673+ ( self . min_filter, self . mag_filter, self . mipmap_filter) ,
674+ ( FilterMode :: Linear , FilterMode :: Linear , FilterMode :: Linear )
675+ )
676+ {
677+ logging:: warn!(
678+ "Sampler anisotropy requested ({}), but all filters must be \
679+ linear; anisotropy disabled.",
680+ anisotropy_clamp
681+ ) ;
682+ anisotropy_clamp = 1 ;
683+ }
684+
625685 return wgpu:: SamplerDescriptor {
626686 label : self . label . as_deref ( ) ,
627687 address_mode_u : self . address_u . to_wgpu ( ) ,
@@ -632,13 +692,23 @@ impl SamplerBuilder {
632692 mipmap_filter : self . mipmap_filter . to_wgpu_mipmap ( ) ,
633693 lod_min_clamp : self . lod_min ,
634694 lod_max_clamp : self . lod_max ,
695+ anisotropy_clamp,
635696 ..Default :: default ( )
636697 } ;
637698 }
638699
639700 /// Create the sampler on the provided device.
640701 pub fn build ( self , gpu : & Gpu ) -> Sampler {
641- let desc = self . to_descriptor ( ) ;
702+ let downlevel = gpu. adapter ( ) . get_downlevel_capabilities ( ) ;
703+ let max_supported_anisotropy = if downlevel
704+ . flags
705+ . contains ( wgpu:: DownlevelFlags :: ANISOTROPIC_FILTERING )
706+ {
707+ 16
708+ } else {
709+ 1
710+ } ;
711+ let desc = self . to_descriptor ( max_supported_anisotropy) ;
642712 let raw = gpu. device ( ) . create_sampler ( & desc) ;
643713 return Sampler {
644714 raw,
@@ -1051,7 +1121,7 @@ mod tests {
10511121 #[ test]
10521122 fn sampler_builder_defaults_map ( ) {
10531123 let b = SamplerBuilder :: new ( ) ;
1054- let d = b. to_descriptor ( ) ;
1124+ let d = b. to_descriptor ( 16 ) ;
10551125 assert_eq ! ( d. address_mode_u, wgpu:: AddressMode :: ClampToEdge ) ;
10561126 assert_eq ! ( d. address_mode_v, wgpu:: AddressMode :: ClampToEdge ) ;
10571127 assert_eq ! ( d. address_mode_w, wgpu:: AddressMode :: ClampToEdge ) ;
@@ -1060,19 +1130,53 @@ mod tests {
10601130 assert_eq ! ( d. mipmap_filter, wgpu:: MipmapFilterMode :: Nearest ) ;
10611131 assert_eq ! ( d. lod_min_clamp, 0.0 ) ;
10621132 assert_eq ! ( d. lod_max_clamp, 32.0 ) ;
1133+ assert_eq ! ( d. anisotropy_clamp, 1 ) ;
10631134 }
10641135
10651136 #[ test]
10661137 fn sampler_builder_linear_clamp_map ( ) {
10671138 let b = SamplerBuilder :: new ( )
10681139 . linear_clamp ( )
10691140 . with_mip_filter ( FilterMode :: Linear ) ;
1070- let d = b. to_descriptor ( ) ;
1141+ let d = b. to_descriptor ( 16 ) ;
10711142 assert_eq ! ( d. address_mode_u, wgpu:: AddressMode :: ClampToEdge ) ;
10721143 assert_eq ! ( d. address_mode_v, wgpu:: AddressMode :: ClampToEdge ) ;
10731144 assert_eq ! ( d. address_mode_w, wgpu:: AddressMode :: ClampToEdge ) ;
10741145 assert_eq ! ( d. mag_filter, wgpu:: FilterMode :: Linear ) ;
10751146 assert_eq ! ( d. min_filter, wgpu:: FilterMode :: Linear ) ;
10761147 assert_eq ! ( d. mipmap_filter, wgpu:: MipmapFilterMode :: Linear ) ;
10771148 }
1149+
1150+ #[ test]
1151+ fn sampler_builder_anisotropy_is_clamped_and_passed_through ( ) {
1152+ let b = SamplerBuilder :: new ( )
1153+ . linear_clamp ( )
1154+ . with_mip_filter ( FilterMode :: Linear )
1155+ . with_anisotropy_clamp ( 8 ) ;
1156+ assert_eq ! ( b. to_descriptor( 16 ) . anisotropy_clamp, 8 ) ;
1157+ assert_eq ! ( b. to_descriptor( 4 ) . anisotropy_clamp, 4 ) ;
1158+ assert_eq ! ( b. to_descriptor( 1 ) . anisotropy_clamp, 1 ) ;
1159+ }
1160+
1161+ #[ test]
1162+ fn sampler_builder_anisotropy_clamps_to_valid_range ( ) {
1163+ assert_eq ! (
1164+ SamplerBuilder :: new( )
1165+ . linear_clamp( )
1166+ . with_mip_filter( FilterMode :: Linear )
1167+ . with_anisotropy_clamp( 0 )
1168+ . to_descriptor( 16 )
1169+ . anisotropy_clamp,
1170+ 1
1171+ ) ;
1172+ assert_eq ! (
1173+ SamplerBuilder :: new( )
1174+ . linear_clamp( )
1175+ . with_mip_filter( FilterMode :: Linear )
1176+ . with_anisotropy_clamp( 100 )
1177+ . to_descriptor( 16 )
1178+ . anisotropy_clamp,
1179+ 16
1180+ ) ;
1181+ }
10781182}
0 commit comments