
We switch back and forwards between logical and physical coordinates all over the place. Systems have to query for cameras and the UiScale when they shouldn't need to. It's confusing and fragile and new scale factor bugs get found constantly. * Use physical coordinates whereever possible in `bevy_ui`. * Store physical coords in `ComputedNode` and tear out all the unneeded scale factor calculations and queries. * Add an `inverse_scale_factor` field to `ComputedNode` and set nodes changed when their scale factor changes. `ComputedNode`'s fields and methods now use physical coordinates. `ComputedNode` has a new field `inverse_scale_factor`. Multiplying the physical coordinates by the `inverse_scale_factor` will give the logical values. --------- Co-authored-by: atlv <email@atlasdostal.com>
128 lines
3.9 KiB
Rust
128 lines
3.9 KiB
Rust
use bevy_ecs::prelude::*;
|
|
use bevy_image::BevyDefault as _;
|
|
use bevy_render::{
|
|
render_resource::{
|
|
binding_types::{sampler, texture_2d, uniform_buffer},
|
|
*,
|
|
},
|
|
renderer::RenderDevice,
|
|
view::{ViewTarget, ViewUniform},
|
|
};
|
|
|
|
#[derive(Resource)]
|
|
pub struct UiPipeline {
|
|
pub view_layout: BindGroupLayout,
|
|
pub image_layout: BindGroupLayout,
|
|
}
|
|
|
|
impl FromWorld for UiPipeline {
|
|
fn from_world(world: &mut World) -> Self {
|
|
let render_device = world.resource::<RenderDevice>();
|
|
|
|
let view_layout = render_device.create_bind_group_layout(
|
|
"ui_view_layout",
|
|
&BindGroupLayoutEntries::single(
|
|
ShaderStages::VERTEX_FRAGMENT,
|
|
uniform_buffer::<ViewUniform>(true),
|
|
),
|
|
);
|
|
|
|
let image_layout = render_device.create_bind_group_layout(
|
|
"ui_image_layout",
|
|
&BindGroupLayoutEntries::sequential(
|
|
ShaderStages::FRAGMENT,
|
|
(
|
|
texture_2d(TextureSampleType::Float { filterable: true }),
|
|
sampler(SamplerBindingType::Filtering),
|
|
),
|
|
),
|
|
);
|
|
|
|
UiPipeline {
|
|
view_layout,
|
|
image_layout,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
|
pub struct UiPipelineKey {
|
|
pub hdr: bool,
|
|
pub anti_alias: bool,
|
|
}
|
|
|
|
impl SpecializedRenderPipeline for UiPipeline {
|
|
type Key = UiPipelineKey;
|
|
|
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
|
let vertex_layout = VertexBufferLayout::from_vertex_formats(
|
|
VertexStepMode::Vertex,
|
|
vec![
|
|
// position
|
|
VertexFormat::Float32x3,
|
|
// uv
|
|
VertexFormat::Float32x2,
|
|
// color
|
|
VertexFormat::Float32x4,
|
|
// mode
|
|
VertexFormat::Uint32,
|
|
// border radius
|
|
VertexFormat::Float32x4,
|
|
// border thickness
|
|
VertexFormat::Float32x4,
|
|
// border size
|
|
VertexFormat::Float32x2,
|
|
// position relative to the center
|
|
VertexFormat::Float32x2,
|
|
],
|
|
);
|
|
let shader_defs = if key.anti_alias {
|
|
vec!["ANTI_ALIAS".into()]
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
RenderPipelineDescriptor {
|
|
vertex: VertexState {
|
|
shader: super::UI_SHADER_HANDLE,
|
|
entry_point: "vertex".into(),
|
|
shader_defs: shader_defs.clone(),
|
|
buffers: vec![vertex_layout],
|
|
},
|
|
fragment: Some(FragmentState {
|
|
shader: super::UI_SHADER_HANDLE,
|
|
shader_defs,
|
|
entry_point: "fragment".into(),
|
|
targets: vec![Some(ColorTargetState {
|
|
format: if key.hdr {
|
|
ViewTarget::TEXTURE_FORMAT_HDR
|
|
} else {
|
|
TextureFormat::bevy_default()
|
|
},
|
|
blend: Some(BlendState::ALPHA_BLENDING),
|
|
write_mask: ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
layout: vec![self.view_layout.clone(), self.image_layout.clone()],
|
|
push_constant_ranges: Vec::new(),
|
|
primitive: PrimitiveState {
|
|
front_face: FrontFace::Ccw,
|
|
cull_mode: None,
|
|
unclipped_depth: false,
|
|
polygon_mode: PolygonMode::Fill,
|
|
conservative: false,
|
|
topology: PrimitiveTopology::TriangleList,
|
|
strip_index_format: None,
|
|
},
|
|
depth_stencil: None,
|
|
multisample: MultisampleState {
|
|
count: 1,
|
|
mask: !0,
|
|
alpha_to_coverage_enabled: false,
|
|
},
|
|
label: Some("ui_pipeline".into()),
|
|
zero_initialize_workgroup_memory: false,
|
|
}
|
|
}
|
|
}
|