Skip to content

Commit

Permalink
gpu: Shrink block vertices using fixed-point texture clamp rectangles.
Browse files Browse the repository at this point in the history
  • Loading branch information
kpreid committed Oct 14, 2023
1 parent 79c3e98 commit 681d651
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 26 deletions.
6 changes: 4 additions & 2 deletions all-is-cubes-gpu/src/in_wgpu/block_texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use all_is_cubes::time;
use all_is_cubes_mesh::texture;

use crate::in_wgpu::glue::{size_vector_to_extent, write_texture_by_aab};
use crate::in_wgpu::vertex::{AtlasTexel, TexPoint};
use crate::in_wgpu::vertex::{AtlasTexel, FixTexCoord, TexPoint};
use crate::octree_alloc::{Alloctree, AlloctreeHandle};
use crate::BlockTextureInfo;

Expand Down Expand Up @@ -354,7 +354,9 @@ impl texture::Plane for AtlasPlane {

fn grid_to_texcoord(&self, in_tile_grid: texture::TilePoint) -> Self::Point {
// TODO: assert in bounds, just in case
(in_tile_grid + self.tile.offset.to_vector().map(|c| c as f32)).cast_unit()
let float_point =
(in_tile_grid + self.tile.offset.to_vector().map(|c| c as f32)).cast_unit();
float_point.map(FixTexCoord::from_float)
}
}

Expand Down
15 changes: 10 additions & 5 deletions all-is-cubes-gpu/src/in_wgpu/shaders/blocks-and-lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ struct WgpuBlockVertex {
@location(0) cube_packed: u32,
@location(1) position_in_cube_and_normal_packed: vec2<u32>,
@location(2) color_or_texture: vec4<f32>,
@location(3) clamp_min: vec3<f32>,
@location(4) clamp_max: vec3<f32>,
@location(3) clamp_min_max: vec3<u32>,
};

// Mirrors `struct WgpuInstanceData` on the Rust side.
Expand Down Expand Up @@ -152,7 +151,13 @@ fn block_vertex_main(
case 6u { normal = vec3<f32>(0.0, 0.0, 1.0); }
default: {}
}


// Unpack clamp rectangle coordinates.
let clamp_min_fixpoint = input.clamp_min_max & vec3<u32>(0x0000FFFFu);
let clamp_max_fixpoint = (input.clamp_min_max & vec3<u32>(0xFFFF0000u)) >> vec3<u32>(16u);
let clamp_min = vec3<f32>(clamp_min_fixpoint) / 2.0;
let clamp_max = vec3<f32>(clamp_max_fixpoint) / 2.0;

// Generate tangents (with always positive components).
// TODO: Would it be better to just put this in the above switch statement?
// TODO: Is the always-positive rule actually needed?
Expand All @@ -177,8 +182,8 @@ fn block_vertex_main(
bitangent,
normal,
input.color_or_texture,
input.clamp_min,
input.clamp_max,
clamp_min,
clamp_max,
compute_fog(world_position),
// Note that we do not normalize this vector: by keeping things linear, we
// allow linear interpolation between vertices to get the right answer.
Expand Down
76 changes: 57 additions & 19 deletions all-is-cubes-gpu/src/in_wgpu/vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use crate::DebugLineVertex;

/// Texture coordinates in the 3D atlas texture, in units of texels (i.e. the range is
/// 0..256 or similar, not 0..1).
///
/// TODO: Convert these to fixed-point and save some size
pub(crate) type TexPoint = Point3D<f32, AtlasTexel>;
pub(crate) type TexPoint = Point3D<FixTexCoord, AtlasTexel>;

/// Coordinate system for texels in the 3D atlas texture.
#[doc(hidden)]
Expand All @@ -24,7 +22,7 @@ pub(crate) enum CubeFix256 {}
#[repr(C)]
pub(crate) struct WgpuBlockVertex {
/// Chunk-relative position of the cube containing the triangle this vertex belongs to,
/// packed into a u32 as x + (y << 8) + (z << 16).
/// packed into a u32 as x | (y << 8) | (z << 16).
///
/// Note that this is not the same as floor() of the final coordinates, since a
/// block's mesh coordinates range from 0 to 1 inclusive.
Expand All @@ -50,25 +48,28 @@ pub(crate) struct WgpuBlockVertex {
/// Packed format:
/// * If `[3]` is in the range 0.0 to 1.0, then the attribute is a linear RGBA color.
/// * If `[3]` is -1.0, then the first three components are 3D texture coordinates,
/// stored in texel units not normalized 0-1 units.
/// stored in texel units rather than normalized 0-1 units.
///
/// TODO: we don't need `f32` precision here.
color_or_texture: [f32; 4],

/// Interpolated texture coordinates are clamped to be ≥ this value, to avoid bleeding.
/// Interpolated texture coordinates are clamped to be within these ranges,
/// to avoid bleeding.
///
/// TODO: pack these into u16s and save some memory.
clamp_min: [f32; 3],

/// Interpolated texture coordinates are clamped to be ≤ this value, to avoid bleeding.
clamp_max: [f32; 3],
/// Each u32 is two packed [`FixTexCoord`], min | (max << 16).
///
/// Design note: It would be more straightforward to use `f16` here, but that's a
/// WebGPU optional extension; and there are no `[f16; 3]` vectors so it would still
/// require some data shuffling.
clamp_min_max: [u32; 3],
}

impl WgpuBlockVertex {
const ATTRIBUTE_LAYOUT: &'static [wgpu::VertexAttribute] = &wgpu::vertex_attr_array![
0 => Uint32, // cube_packed
1 => Uint32x2, // position_in_cube_and_normal_packed
2 => Float32x4, // color_or_texture
3 => Float32x3, // clamp_min
4 => Float32x3, // clamp_max
3 => Uint32x3, // clamp_min_max
// location numbers must not clash with WgpuInstanceData
];

Expand Down Expand Up @@ -105,8 +106,7 @@ impl From<BlockVertex<TexPoint>> for WgpuBlockVertex {
cube_packed,
position_in_cube_and_normal_packed,
color_or_texture: color_attribute,
clamp_min: [0., 0., 0.],
clamp_max: [0., 0., 0.],
clamp_min_max: [0, 0, 0],
}
}
Coloring::Texture {
Expand All @@ -116,9 +116,8 @@ impl From<BlockVertex<TexPoint>> for WgpuBlockVertex {
} => Self {
cube_packed,
position_in_cube_and_normal_packed,
color_or_texture: [tc.x, tc.y, tc.z, -1.0],
clamp_min: clamp_min.into(),
clamp_max: clamp_max.into(),
color_or_texture: [tc.x.into(), tc.y.into(), tc.z.into(), -1.0],
clamp_min_max: clamp_min.zip(clamp_max, FixTexCoord::pack).into(),
},
}
}
Expand All @@ -129,6 +128,7 @@ impl GfxVertex for WgpuBlockVertex {
/// TODO: no reason this should be f32 other than scaling to fractional integers.
/// The depth sorting system should be made more flexible here.
type Coordinate = f32;

/// Packed cube coordinates
type BlockInst = u32;
type TexPoint = TexPoint;
Expand Down Expand Up @@ -229,6 +229,32 @@ impl DebugLineVertex for WgpuLinesVertex {
}
}

/// Fixed-point texture coordinate, with a multiplier of 2 so it can represent edges and
/// middles of texels (0.0, 0.5, 1.0, 1.5, ...)
///
/// This type should not be used directly; it is public as an element of [`WgpuBlockVertex`]'s
/// trait implementations.
#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct FixTexCoord(u16);

impl FixTexCoord {
pub(crate) fn from_float(float_tc: f32) -> Self {
debug_assert!(float_tc >= 0.0);
Self((float_tc * 2.).round() as u16)
}

pub(crate) fn pack(low: Self, high: Self) -> u32 {
u32::from(low.0) | (u32::from(high.0) << 16)
}
}

impl From<FixTexCoord> for f32 {
fn from(tc: FixTexCoord) -> Self {
f32::from(tc.0) / 2.
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -240,7 +266,7 @@ mod tests {
/// the struct is designed to have a fixed layout communicating to the shader anyway.
#[test]
fn vertex_size() {
assert_eq!(mem::size_of::<WgpuBlockVertex>(), 52);
assert_eq!(mem::size_of::<WgpuBlockVertex>(), 40);
assert_eq!(mem::size_of::<WgpuLinesVertex>(), 28);
}

Expand All @@ -260,4 +286,16 @@ mod tests {
Point3D::new(100.25, 50.0, 8.0)
);
}

#[test]
fn fix_tex_coord() {
for int_part in 0..=256u16 {
for frac_part in 0..=1 {
let float = f32::from(int_part) + f32::from(frac_part) * 0.5;
let fix = FixTexCoord::from_float(float);
assert_eq!(fix.0, int_part * 2 + frac_part);
assert_eq!(float, f32::from(fix));
}
}
}
}

0 comments on commit 681d651

Please sign in to comment.