From 7e1ba79344feb8319b892c2ec0fa61575b38687b Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Sun, 2 Jul 2023 21:00:44 -0700 Subject: [PATCH] Texture coordinates are now integers There is a lot of random cleanup in this commit, but the only real change in functionality is switching from inputting precomputed f32 fractional coordinates to unsigned pixels for the texture coordinates. This has a couple of major benefits: resizing textures no longer breaks texture coordinates, and fractionally extracting from a texture is no longer possible. Subpixel rendering may be desired, however. I'm currently debating whether to always pixel align or offer some sort of integer-based subpixel subdivisions. With the modern movement being to move to higher DPI screens, in general, the importance of subpixel rendering dimenishes and the sharpness of pixel aligned graphics shines. Ultimately, I'm sure this will be tweaked once text rendering is supported, as that's the situation where subpixel rendering seems like it should be important. --- examples/shapes.rs | 2 +- examples/texture-collection.rs | 2 +- examples/texture.rs | 2 +- src/atlas.rs | 13 ++++++--- src/lib.rs | 53 ++++++++++++---------------------- src/sealed.rs | 26 +++++++++++++++++ src/shapes.rs | 43 ++++++++++++--------------- src/shapes.wgsl | 14 ++++----- 8 files changed, 82 insertions(+), 73 deletions(-) create mode 100644 src/sealed.rs diff --git a/examples/shapes.rs b/examples/shapes.rs index 9ac8749ab..5d4b135b8 100644 --- a/examples/shapes.rs +++ b/examples/shapes.rs @@ -3,8 +3,8 @@ use std::time::Duration; use appit::RunningWindow; use kludgine::app::WindowBehavior; use kludgine::math::{Dips, Pixels, Point, Rect, Size}; -use kludgine::shapes::{PathBuilder, PreparedGraphic}; use kludgine::Color; +use kludgine::{PathBuilder, PreparedGraphic}; fn main() { Test::run(); diff --git a/examples/texture-collection.rs b/examples/texture-collection.rs index 21f711251..1582572d3 100644 --- a/examples/texture-collection.rs +++ b/examples/texture-collection.rs @@ -3,7 +3,7 @@ use std::time::Duration; use appit::RunningWindow; use kludgine::app::WindowBehavior; use kludgine::math::{Dips, Pixels, Point, Rect, Size}; -use kludgine::{shapes::PreparedGraphic, Color, TextureCollection}; +use kludgine::{Color, PreparedGraphic, TextureCollection}; fn main() { Test::run(); diff --git a/examples/texture.rs b/examples/texture.rs index fc93e8558..ec82a96c8 100644 --- a/examples/texture.rs +++ b/examples/texture.rs @@ -3,7 +3,7 @@ use std::time::Duration; use appit::RunningWindow; use kludgine::app::WindowBehavior; use kludgine::math::{Dips, Point, Rect, Size}; -use kludgine::shapes::PreparedGraphic; +use kludgine::PreparedGraphic; use kludgine::Texture; fn main() { diff --git a/src/atlas.rs b/src/atlas.rs index 6845d3057..828e1522a 100644 --- a/src/atlas.rs +++ b/src/atlas.rs @@ -6,6 +6,7 @@ use alot::{LotId, Lots}; use crate::math::{Rect, Size, ToFloat, UPixels}; use crate::pack::{TextureAllocation, TexturePacker}; +use crate::sealed; use crate::shapes::{PreparedGraphic, Vertex}; use crate::{Graphics, Texture, TextureSource, WgpuDeviceAndQueue}; @@ -164,13 +165,15 @@ impl TextureCollection { } } -impl TextureSource for TextureCollection { +impl TextureSource for TextureCollection {} + +impl sealed::TextureSource for TextureCollection { fn bind_group(&self, graphics: &Graphics<'_>) -> Arc { let data = self.data.read().map_or_else(PoisonError::into_inner, |g| g); data.texture.bind_group(graphics) } - fn id(&self) -> crate::TextureId { + fn id(&self) -> sealed::TextureId { let data = self.data.read().map_or_else(PoisonError::into_inner, |g| g); data.texture.id() } @@ -207,12 +210,14 @@ impl Drop for CollectedTexture { } } -impl TextureSource for CollectedTexture { +impl TextureSource for CollectedTexture {} + +impl sealed::TextureSource for CollectedTexture { fn bind_group(&self, graphics: &Graphics<'_>) -> Arc { self.collection.bind_group(graphics) } - fn id(&self) -> crate::TextureId { + fn id(&self) -> sealed::TextureId { self.collection.id() } } diff --git a/src/lib.rs b/src/lib.rs index 6bdfc444f..5faf7940f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::mem::size_of; use std::ops::{Add, Div, Neg, Range}; -use std::sync::atomic::{self, AtomicUsize}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use bytemuck::{Pod, Zeroable}; use wgpu::util::DeviceExt; @@ -15,8 +14,7 @@ use crate::math::{ Dips, Pixels, Point, Ratio, Rect, ScreenTransformation, Size, ToFloat, UPixels, Zero, }; use crate::shapes::{ - PathBuilder, PreparedGraphic, PushConstants, ShaderScalable, Shape, Vertex, FLAG_ROTATE, - FLAG_SCALE, FLAG_TEXTURED, FLAG_TRANSLATE, + PushConstants, Vertex, FLAG_ROTATE, FLAG_SCALE, FLAG_TEXTURED, FLAG_TRANSLATE, }; #[cfg(feature = "app")] @@ -25,7 +23,10 @@ mod atlas; mod buffer; pub mod math; mod pack; -pub mod shapes; +mod sealed; +mod shapes; + +pub use shapes::{Path, PathBuilder, PreparedGraphic, ShaderScalable, Shape}; pub use atlas::{CollectedTexture, TextureCollection}; @@ -82,7 +83,7 @@ impl Kludgine { }, wgpu::BindGroupLayoutEntry { binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { sample_type: wgpu::TextureSampleType::Float { filterable: true }, view_dimension: wgpu::TextureViewDimension::D2, @@ -170,7 +171,7 @@ impl Kludgine { shader_location: 0, }, wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, + format: wgpu::VertexFormat::Uint32x2, offset: 8, shader_location: 1, }, @@ -793,23 +794,9 @@ struct Uniforms { _padding: [u32; 3], } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct TextureId(usize); - -impl TextureId { - pub fn new_unique_id() -> Self { - static COUNTER: OnceLock = OnceLock::new(); - Self( - COUNTER - .get_or_init(AtomicUsize::default) - .fetch_add(1, atomic::Ordering::Relaxed), - ) - } -} - #[derive(Debug)] pub struct Texture { - id: TextureId, + id: sealed::TextureId, wgpu: wgpu::Texture, view: wgpu::TextureView, bind_group: Arc, @@ -835,7 +822,7 @@ impl Texture { let view = wgpu.create_view(&wgpu::TextureViewDescriptor::default()); let bind_group = Arc::new(create_bind_group(graphics, &view)); Self { - id: TextureId::new_unique_id(), + id: sealed::TextureId::new_unique_id(), wgpu, view, bind_group, @@ -866,7 +853,7 @@ impl Texture { let view = wgpu.create_view(&wgpu::TextureViewDescriptor::default()); let bind_group = Arc::new(create_bind_group(graphics, &view)); Self { - id: TextureId::new_unique_id(), + id: sealed::TextureId::new_unique_id(), wgpu, view, bind_group, @@ -961,9 +948,6 @@ impl Texture { Vertex: bytemuck::Pod, { let (source_top_left, source_bottom_right) = source.extents(); - let size = self.size().into_float(); - let source_top_left = source_top_left.into_float() / size; - let source_bottom_right = source_bottom_right.into_float() / size; let (dest_top_left, dest_bottom_right) = dest.extents(); let path = PathBuilder::new_textured(dest_top_left, source_top_left) .line_to( @@ -1049,17 +1033,16 @@ fn create_bind_group( // } // } -pub trait TextureSource { - fn id(&self) -> TextureId; - fn bind_group(&self, graphics: &Graphics<'_>) -> Arc; -} +pub trait TextureSource: sealed::TextureSource {} + +impl TextureSource for Texture {} -impl TextureSource for Texture { +impl sealed::TextureSource for Texture { fn bind_group(&self, _graphics: &Graphics<'_>) -> Arc { self.bind_group.clone() } - fn id(&self) -> TextureId { + fn id(&self) -> sealed::TextureId { self.id } } @@ -1073,7 +1056,7 @@ pub struct Renderer<'render, 'gfx> { struct Command { indices: Range, constants: PushConstants, - texture: Option, + texture: Option, } #[derive(Eq, PartialEq, Debug, Clone, Copy)] @@ -1240,7 +1223,7 @@ pub struct Rendering { vertices: Vec>, vertex_index_by_id: HashMap, indices: Vec, - textures: HashMap>, + textures: HashMap>, commands: Vec, } diff --git a/src/sealed.rs b/src/sealed.rs new file mode 100644 index 000000000..c5372dd28 --- /dev/null +++ b/src/sealed.rs @@ -0,0 +1,26 @@ +use std::sync::atomic::{self, AtomicUsize}; +use std::sync::{Arc, OnceLock}; + +use crate::Graphics; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct TextureId(usize); + +impl TextureId { + pub fn new_unique_id() -> Self { + static COUNTER: OnceLock = OnceLock::new(); + Self( + COUNTER + .get_or_init(AtomicUsize::default) + .fetch_add(1, atomic::Ordering::Relaxed), + ) + } +} + +pub trait ShaderScalableSealed { + fn flags() -> u32; +} +pub trait TextureSource { + fn id(&self) -> TextureId; + fn bind_group(&self, graphics: &Graphics<'_>) -> Arc; +} diff --git a/src/shapes.rs b/src/shapes.rs index 2a58decb0..d9f260e32 100644 --- a/src/shapes.rs +++ b/src/shapes.rs @@ -12,7 +12,7 @@ use wgpu::{BufferUsages, ShaderStages}; use crate::buffer::Buffer; use crate::math::{Dips, Pixels, Point, Rect, ToFloat, UPixels, Zero}; -use crate::{Color, Graphics, RenderingGraphics, TextureSource}; +use crate::{sealed, Color, Graphics, RenderingGraphics, TextureSource}; #[derive(Debug, Clone, PartialEq)] pub struct Shape { @@ -167,12 +167,6 @@ where pub trait ShaderScalable: sealed::ShaderScalableSealed {} -mod sealed { - pub trait ShaderScalableSealed { - fn flags() -> u32; - } -} - impl ShaderScalable for Pixels {} impl ShaderScalable for Dips {} @@ -217,7 +211,7 @@ where Vertex { location: Point::new(Unit::from_float(position.x), Unit::from_float(position.y)), - texture: Point::new(attributes[0], attributes[1]), + texture: Point::new(attributes[0].round(), attributes[1].round()), color: self.default_color, } } @@ -335,10 +329,7 @@ pub struct Vertex { #[test] fn vertex_align() { - assert_eq!( - std::mem::size_of::<[Vertex; 2]>(), - std::mem::size_of::>() * 2 - ); + assert_eq!(std::mem::size_of::>(), 20); } unsafe impl bytemuck::Pod for Vertex {} @@ -360,13 +351,13 @@ pub enum PathEvent { Begin { /// The location to begin at. at: Endpoint, - texture: Point, + texture: Point, }, /// A straight line segment. Line { /// The end location of the line. to: Endpoint, - texture: Point, + texture: Point, }, /// A quadratic curve (one control point). Quadratic { @@ -374,7 +365,7 @@ pub enum PathEvent { ctrl: ControlPoint, /// The end location of the curve. to: Endpoint, - texture: Point, + texture: Point, }, /// A cubic curve (two control points). Cubic { @@ -384,7 +375,7 @@ pub enum PathEvent { ctrl2: ControlPoint, /// The end location of the curve. to: Endpoint, - texture: Point, + texture: Point, }, /// Ends the path. Must be the last entry. End { @@ -417,13 +408,17 @@ where for &event in &self.events { match event { PathEvent::Begin { at, texture } => { - builder.begin(at.into(), &[texture.x, texture.y]); + builder.begin(at.into(), &[texture.x.into_float(), texture.y.into_float()]); } PathEvent::Line { to, texture } => { - builder.line_to(to.into(), &[texture.x, texture.y]); + builder.line_to(to.into(), &[texture.x.into_float(), texture.y.into_float()]); } PathEvent::Quadratic { ctrl, to, texture } => { - builder.quadratic_bezier_to(ctrl.into(), to.into(), &[texture.x, texture.y]); + builder.quadratic_bezier_to( + ctrl.into(), + to.into(), + &[texture.x.into_float(), texture.y.into_float()], + ); } PathEvent::Cubic { ctrl1, @@ -435,7 +430,7 @@ where ctrl1.into(), ctrl2.into(), to.into(), - &[texture.x, texture.y], + &[texture.x.into_float(), texture.y.into_float()], ); } PathEvent::End { close } => builder.end(close), @@ -563,7 +558,7 @@ where { /// Creates a new path with the initial position `start_at`. #[must_use] - pub fn new_textured(start_at: Endpoint, texture: Point) -> Self { + pub fn new_textured(start_at: Endpoint, texture: Point) -> Self { Self { path: Path::from_iter([(PathEvent::Begin { at: start_at, @@ -583,7 +578,7 @@ where /// Create a straight line from the current location to `end_at`. #[must_use] - pub fn line_to(mut self, end_at: Endpoint, texture: Point) -> Self { + pub fn line_to(mut self, end_at: Endpoint, texture: Point) -> Self { self.path.events.push(PathEvent::Line { to: end_at, texture, @@ -599,7 +594,7 @@ where mut self, control: ControlPoint, end_at: Endpoint, - texture: Point, + texture: Point, ) -> Self { self.path.events.push(PathEvent::Quadratic { ctrl: control, @@ -618,7 +613,7 @@ where control1: ControlPoint, control2: ControlPoint, end_at: Endpoint, - texture: Point, + texture: Point, ) -> Self { self.path.events.push(PathEvent::Cubic { ctrl1: control1, diff --git a/src/shapes.wgsl b/src/shapes.wgsl index be4132ce2..af04f7983 100644 --- a/src/shapes.wgsl +++ b/src/shapes.wgsl @@ -10,14 +10,14 @@ var pc: PushConstants; struct VertexInput { @location(0) position: vec2, - @location(1) texture: vec2, + @location(1) uv: vec2, @location(2) color: u32, } struct VertexOutput { @builtin(position) position: vec4, - @location(0) color: u32, - @location(1) texture: vec2, + @location(0) uv: vec2, + @location(1) color: u32, } struct Uniforms { @@ -96,13 +96,13 @@ fn vs_main(input: VertexInput) -> VertexOutput { } outval.position = uniforms.ortho * vec4(position, 0., 1.0); outval.color = input.color; - outval.texture = input.texture; + outval.uv = vec2(input.uv) / vec2(textureDimensions(r_texture)); return outval; } struct FragmentInput { - @location(0) color: u32, - @location(1) texture: vec2, + @location(0) uv: vec2, + @location(1) color: u32, } @group(0) @@ -117,7 +117,7 @@ fn fs_main(fragment: FragmentInput) -> @location(0) vec4 { let flag_textured = u32(1) << u32(4); if (pc.flags & flag_textured) != u32(0) { - return textureSample(r_texture, r_sampler, fragment.texture); + return textureSample(r_texture, r_sampler, fragment.uv); } let r = fragment.color >> u32(24);