diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 7e7036d1e8..d6d2d869d3 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -441,7 +441,10 @@ impl Camera { #[inline] pub fn target_scaling_factor(&self) -> Option { - self.computed.target_info.as_ref().map(|t| t.scale_factor) + self.computed + .target_info + .as_ref() + .map(|t: &RenderTargetInfo| t.scale_factor) } /// The projection matrix computed using this camera's [`CameraProjection`]. diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index eb7c8169d4..fc19dde8bf 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -371,6 +371,7 @@ pub fn queue_shadows( ), batch_range: 0..0, extra_index: PhaseItemExtraIndex::NONE, + inverse_scale_factor: 1., }); } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 27068346e8..b8bdb40d5e 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -528,6 +528,11 @@ const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; #[derive(Component)] pub struct DefaultCameraView(pub Entity); +#[derive(Component)] +pub struct ExtractedAA { + pub scale_factor: f32, +} + /// Extracts all UI elements associated with a camera into the render world. pub fn extract_default_ui_camera_view( mut commands: Commands, @@ -555,7 +560,7 @@ pub fn extract_default_ui_camera_view( commands .get_entity(entity) .expect("Camera entity wasn't synced.") - .remove::<(DefaultCameraView, UiAntiAlias, UiBoxShadowSamples)>(); + .remove::<(DefaultCameraView, ExtractedAA, UiBoxShadowSamples)>(); continue; } @@ -566,10 +571,12 @@ pub fn extract_default_ui_camera_view( .. }), Some(physical_size), + Some(scale_factor), ) = ( camera.logical_viewport_size(), camera.physical_viewport_rect(), camera.physical_viewport_size(), + camera.target_scaling_factor(), ) { // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection let projection_matrix = Mat4::orthographic_rh( @@ -580,6 +587,7 @@ pub fn extract_default_ui_camera_view( 0.0, UI_CAMERA_FAR, ); + let default_camera_view = commands .spawn(( ExtractedView { @@ -606,8 +614,10 @@ pub fn extract_default_ui_camera_view( .get_entity(entity) .expect("Camera entity wasn't synced."); entity_commands.insert(DefaultCameraView(default_camera_view)); - if let Some(ui_anti_alias) = ui_anti_alias { - entity_commands.insert(*ui_anti_alias); + if ui_anti_alias != Some(&UiAntiAlias::Off) { + entity_commands.insert(ExtractedAA { + scale_factor: (scale_factor * ui_scale.0), + }); } if let Some(shadow_samples) = shadow_samples { entity_commands.insert(*shadow_samples); @@ -785,6 +795,7 @@ struct UiVertex { pub size: [f32; 2], /// Position relative to the center of the UI node. pub point: [f32; 2], + pub inverse_scale_factor: f32, } #[derive(Resource)] @@ -835,13 +846,13 @@ pub fn queue_uinodes( ui_pipeline: Res, mut pipelines: ResMut>, mut transparent_render_phases: ResMut>, - mut views: Query<(Entity, &ExtractedView, Option<&UiAntiAlias>)>, + mut views: Query<(Entity, &ExtractedView, Option<&ExtractedAA>)>, pipeline_cache: Res, draw_functions: Res>, ) { let draw_function = draw_functions.read().id::(); for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() { - let Ok((view_entity, view, ui_anti_alias)) = views.get_mut(extracted_uinode.camera_entity) + let Ok((view_entity, view, extracted_aa)) = views.get_mut(extracted_uinode.camera_entity) else { continue; }; @@ -855,7 +866,7 @@ pub fn queue_uinodes( &ui_pipeline, UiPipelineKey { hdr: view.hdr, - anti_alias: matches!(ui_anti_alias, None | Some(UiAntiAlias::On)), + anti_alias: extracted_aa.is_some(), }, ); transparent_phase.add(TransparentUi { @@ -869,6 +880,7 @@ pub fn queue_uinodes( // batch_range will be calculated in prepare_uinodes batch_range: 0..0, extra_index: PhaseItemExtraIndex::NONE, + inverse_scale_factor: extracted_aa.map(|aa| aa.scale_factor).unwrap_or(1.), }); } } @@ -1139,6 +1151,7 @@ pub fn prepare_uinodes( border: [border.left, border.top, border.right, border.bottom], size: rect_size.xy().into(), point: points[i].into(), + inverse_scale_factor: item.inverse_scale_factor, }); } @@ -1242,6 +1255,7 @@ pub fn prepare_uinodes( border: [0.0; 4], size: size.into(), point: [0.0; 2], + inverse_scale_factor: item.inverse_scale_factor, }); } diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index dd465515c5..caa3e804dc 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -74,6 +74,8 @@ impl SpecializedRenderPipeline for UiPipeline { VertexFormat::Float32x2, // position relative to the center VertexFormat::Float32x2, + // inverse scale factor + VertexFormat::Float32, ], ); let shader_defs = if key.anti_alias { diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index c673c8229b..cbae1204db 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -97,6 +97,7 @@ pub struct TransparentUi { pub draw_function: DrawFunctionId, pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, + pub inverse_scale_factor: f32, } impl PhaseItem for TransparentUi { @@ -206,6 +207,7 @@ impl RenderCommand

for SetUiTextureBindGroup RenderCommandResult::Success } } + pub struct DrawUiNode; impl RenderCommand

for DrawUiNode { type Param = SRes; diff --git a/crates/bevy_ui/src/render/ui.wgsl b/crates/bevy_ui/src/render/ui.wgsl index d73e7bd929..940bdbfed6 100644 --- a/crates/bevy_ui/src/render/ui.wgsl +++ b/crates/bevy_ui/src/render/ui.wgsl @@ -22,6 +22,7 @@ struct VertexOutput { // Position relative to the center of the rectangle. @location(6) point: vec2, + @location(7) @interpolate(flat) scale_factor: f32, @builtin(position) position: vec4, }; @@ -39,6 +40,7 @@ fn vertex( @location(5) border: vec4, @location(6) size: vec2, @location(7) point: vec2, + @location(8) scale_factor: f32, ) -> VertexOutput { var out: VertexOutput; out.uv = vertex_uv; @@ -49,6 +51,7 @@ fn vertex( out.size = size; out.border = border; out.point = point; + out.scale_factor = scale_factor; return out; } @@ -115,10 +118,9 @@ fn sd_inset_rounded_box(point: vec2, size: vec2, radius: vec4, in } // get alpha for antialiasing for sdf -fn antialias(distance: f32) -> f32 { +fn antialias(distance: f32, scale_factor: f32) -> f32 { // Using the fwidth(distance) was causing artifacts, so just use the distance. - // This antialiases between the distance values of 0.25 and -0.25 - return clamp(0.0, 1.0, 0.5 - 2.0 * distance); + return clamp(0.0, 1.0, (0.5 - scale_factor * distance)); } fn draw(in: VertexOutput, texture_color: vec4) -> vec4 { @@ -149,7 +151,7 @@ fn draw(in: VertexOutput, texture_color: vec4) -> vec4 { // This select statement ensures we only perform anti-aliasing where a non-zero width border // is present, otherwise an outline about the external boundary would be drawn even without // a border. - let t = select(1.0 - step(0.0, border_distance), antialias(border_distance), external_distance < internal_distance); + let t = select(1.0 - step(0.0, border_distance), antialias(border_distance, in.scale_factor), external_distance < internal_distance); #else let t = 1.0 - step(0.0, border_distance); #endif @@ -165,7 +167,7 @@ fn draw_background(in: VertexOutput, texture_color: vec4) -> vec4 { let internal_distance = sd_inset_rounded_box(in.point, in.size, in.radius, in.border); #ifdef ANTI_ALIAS - let t = antialias(internal_distance); + let t = antialias(internal_distance, in.scale_factor); #else let t = 1.0 - step(0.0, internal_distance); #endif diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index 23be50063c..c0bd6a0673 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -655,6 +655,7 @@ pub fn queue_ui_material_nodes( ), batch_range: 0..0, extra_index: PhaseItemExtraIndex::NONE, + inverse_scale_factor: 1., }); } } diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index 67f9b327ae..423a57bd1c 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -372,6 +372,7 @@ pub fn queue_ui_slices( ), batch_range: 0..0, extra_index: PhaseItemExtraIndex::NONE, + inverse_scale_factor: 1., }); } } diff --git a/examples/testbed/ui_layout_rounding.rs b/examples/testbed/ui_layout_rounding.rs index fc59e7c6cb..be653039d0 100644 --- a/examples/testbed/ui_layout_rounding.rs +++ b/examples/testbed/ui_layout_rounding.rs @@ -18,7 +18,7 @@ fn main() { } fn setup(mut commands: Commands) { - commands.spawn((Camera2d, UiAntiAlias::Off)); + commands.spawn((Camera2d, UiAntiAlias::On)); commands .spawn((