Add a stencil pattern which prepares the stencil buffer accordingly

This commit is contained in:
Maximilian Ammann 2022-01-07 20:28:40 +01:00
parent c54f3ee84b
commit e9ed013489
7 changed files with 226 additions and 58 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 275 KiB

View File

@ -1,6 +1,7 @@
use crate::render::shader_ffi::Vec3f32;
use std::fmt;
use crate::render::shader_ffi::Vec3f32;
#[derive(Clone, Copy, Debug)]
pub struct TileCoords {
pub x: u32,
@ -42,21 +43,29 @@ pub struct WorldTileCoords {
}
impl WorldTileCoords {
pub fn into_world(self, extent: u16) -> WorldCoords {
pub fn into_world(self, extent: f32) -> WorldCoords {
WorldCoords {
x: self.x as f32 * extent as f32,
y: self.y as f32 * extent as f32 + extent as f32, // We add extent here as we want the upper left corner
x: self.x as f32 * extent,
y: self.y as f32 * extent + extent, // We add extent here as we want the upper left corner
z: self.z as f32,
}
}
pub fn into_aligned(self) -> AlignedWorldTileCoords {
return AlignedWorldTileCoords(WorldTileCoords {
x: self.x / 2 * 2,
y: self.y / 2 * 2 - 1,
z: self.z,
});
}
pub fn stencil_reference_value(&self) -> u8 {
match (self.x, self.y) {
(x, y) if x % 2 == 0 && y % 2 == 0 => 1,
(x, y) if x % 2 == 0 && y % 2 != 0 => 2,
(x, y) if x % 2 != 0 && y % 2 == 0 => 3,
(x, y) if x % 2 != 0 && y % 2 != 0 => 4,
_ => 0,
_ => unreachable!(),
}
}
}
@ -77,6 +86,38 @@ impl From<(i32, i32, u8)> for WorldTileCoords {
}
}
pub struct AlignedWorldTileCoords(pub WorldTileCoords);
impl AlignedWorldTileCoords {
pub fn into_upper_left(self) -> WorldTileCoords {
self.0
}
pub fn to_upper_right(&self) -> WorldTileCoords {
WorldTileCoords {
x: self.0.x + 1,
y: self.0.y + 1,
z: self.0.z,
}
}
pub fn to_lower_left(&self) -> WorldTileCoords {
WorldTileCoords {
x: self.0.x,
y: self.0.y - 1,
z: self.0.z,
}
}
pub fn to_lower_right(&self) -> WorldTileCoords {
WorldTileCoords {
x: self.0.x - 1,
y: self.0.y - 1,
z: self.0.z,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct WorldCoords {
pub x: f32,

View File

@ -5,8 +5,8 @@ pub const MUNICH_Y: u32 = 11360;
pub const MUNICH_Z: u8 = 15;
pub fn fetch_munich_tiles(cache: &Cache) {
for x in 0..10 {
for y in 0..10 {
for x in 0..15 {
for y in 0..15 {
cache.fetch((MUNICH_X + x, MUNICH_Y + y, MUNICH_Z).into())
}
}

View File

@ -1,9 +1,9 @@
mod buffer_pool;
mod piplines;
mod shaders;
mod stencil_pattern;
mod texture;
mod shaders;
mod buffer_pool;
pub mod camera;
pub mod shader_ffi;
pub mod state;

View File

@ -26,13 +26,15 @@ fn main(
[[builtin(vertex_index)]] vertex_idx: u32,
[[builtin(instance_index)]] instance_idx: u32 // instance_index is used when we have multiple instances of the same "object"
) -> VertexOutput {
var VERTICES: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
vec2<f32>(0.0, 0.0),
vec2<f32>(0.0, EXTENT),
vec2<f32>(EXTENT, 0.0),
vec2<f32>(EXTENT, 0.0),
vec2<f32>(0.0, EXTENT),
vec2<f32>(EXTENT, EXTENT)
let z = 0.0;
var VERTICES: array<vec3<f32>, 6> = array<vec3<f32>, 6>(
vec3<f32>(0.0, 0.0, z),
vec3<f32>(0.0, EXTENT, z),
vec3<f32>(EXTENT, 0.0, z),
vec3<f32>(EXTENT, 0.0, z),
vec3<f32>(0.0, EXTENT, z),
vec3<f32>(EXTENT, EXTENT, z)
);
let a_position = VERTICES[vertex_idx];
@ -42,10 +44,7 @@ fn main(
vec3<f32>(0.0, 0.0, 1.0)
);
let z = 0.0;
let world_pos_3d = vec3<f32>(a_position + mask_offset, z);
let world_pos = scaling * world_pos_3d;
let world_pos = scaling * a_position + vec3<f32>(mask_offset, z);
let position = globals.camera.view_proj * vec4<f32>(world_pos, 1.0);

View File

@ -3,8 +3,6 @@ use std::default::Default;
use std::io::Cursor;
use std::ops::Range;
use crate::coords::{TileCoords, WorldTileCoords};
use crate::example::{MUNICH_X, MUNICH_Y};
use log::{trace, warn};
use lyon::tessellation::VertexBuffers;
use wgpu::util::DeviceExt;
@ -15,11 +13,14 @@ use winit::event::{
};
use winit::window::Window;
use crate::coords::{TileCoords, WorldTileCoords};
use crate::example::{MUNICH_X, MUNICH_Y};
use crate::fps_meter::FPSMeter;
use crate::io::cache::Cache;
use crate::io::static_database;
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
use crate::render::stencil_pattern::TileMaskPattern;
use crate::render::{camera, shaders};
use crate::tesselation::{IndexDataType, Tesselated};
use crate::util::measure::Measure;
@ -68,7 +69,8 @@ pub struct State {
buffer_pool: BufferPool<Queue, Buffer, GpuVertexUniform, IndexDataType>,
tile_mask_instances: wgpu::Buffer,
tile_mask_pattern: TileMaskPattern,
tile_mask_instances_buffer: wgpu::Buffer,
pub camera: camera::Camera,
projection: camera::Projection,
@ -161,24 +163,14 @@ impl State {
mapped_at_creation: false,
});
let instances = [
// Step 1
MaskInstanceUniform::new([0.0, 0.0], 4.0, -4.0, [1.0, 0.0, 0.0, 1.0]), // horizontal
// Step 2
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, -4.0, [0.0, 1.0, 0.0, 1.0]), // vertical
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, -4.0, [0.0, 1.0, 0.0, 1.0]), // vertical
// Step 3
MaskInstanceUniform::new([0.0, -1.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
MaskInstanceUniform::new([0.0, -3.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
// Step 4
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, -4.0, [0.5, 0.25, 0.5, 1.0]), // vertical
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, -4.0, [0.5, 0.25, 0.5, 1.0]), // vertical
];
let tile_masks_uniform_buffer_size =
std::mem::size_of::<MaskInstanceUniform>() as u64 * 128; // FIXME: Tile count?
let tile_mask_instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let tile_mask_instances = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
contents: bytemuck::cast_slice(&instances),
usage: wgpu::BufferUsages::VERTEX,
size: tile_masks_uniform_buffer_size,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let globals_buffer_byte_size = cmp::max(
@ -309,7 +301,7 @@ impl State {
globals_uniform_buffer,
tiles_uniform_buffer,
fps_meter: FPSMeter::new(),
tile_mask_instances,
tile_mask_instances_buffer: tile_mask_instances,
camera,
projection,
suspended: false, // Initially the app is not suspended
@ -317,6 +309,7 @@ impl State {
BackingBufferDescriptor(vertex_uniform_buffer, 1024 * 1024 * 16),
BackingBufferDescriptor(indices_uniform_buffer, 1024 * 1024 * 16),
),
tile_mask_pattern: TileMaskPattern::new(),
}
}
@ -371,28 +364,29 @@ impl State {
let upload = cache.pop_all();
for tile in upload.iter() {
let new_coords = TileCoords {
x: tile.coords.x,
y: tile.coords.y,
z: tile.coords.z,
};
let world_coords = tile.coords.into_world_tile();
self.tile_mask_pattern.update_bounds(&world_coords);
self.buffer_pool
.allocate_geometry(&self.queue, tile.id, new_coords, &tile.geometry);
.allocate_geometry(&self.queue, tile.id, tile.coords, &tile.geometry);
let uniform = TileUniform::new(
[0.0, 0.0, 0.0, 1.0],
new_coords
.into_world_tile()
.into_world(4096)
.into_shader_coords(),
);
self.queue.write_buffer(
&self.tiles_uniform_buffer,
std::mem::size_of::<TileUniform>() as u64 * tile.id as u64,
bytemuck::cast_slice(&[uniform]),
bytemuck::cast_slice(&[TileUniform::new(
[0.0, 0.0, 0.0, 1.0],
world_coords.into_world(4096.0).into_shader_coords(),
)]),
);
}
self.tile_mask_pattern.update_pattern(15u8, 4096.0);
self.queue.write_buffer(
&self.tile_mask_instances_buffer,
0,
bytemuck::cast_slice(self.tile_mask_pattern.as_slice()),
);
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -460,9 +454,9 @@ impl State {
{
// Draw masks
pass.set_pipeline(&self.mask_pipeline);
pass.set_vertex_buffer(0, self.tile_mask_instances.slice(..));
pass.set_vertex_buffer(0, self.tile_mask_instances_buffer.slice(..));
// Draw 7 squares each out of 6 vertices
pass.draw(0..6, 0..7);
pass.draw(0..6, 0..self.tile_mask_pattern.instances());
}
{
for entry in self.buffer_pool.available_vertices() {

View File

@ -0,0 +1,134 @@
use std::num;
use crate::coords::{AlignedWorldTileCoords, WorldTileCoords};
use crate::render::shader_ffi::MaskInstanceUniform;
struct MinMaxBoundingBox {
min_x: i32,
min_y: i32,
max_x: i32,
max_y: i32,
initialized: bool,
}
impl MinMaxBoundingBox {
fn new() -> Self {
Self {
min_x: i32::MAX,
min_y: i32::MAX,
max_x: i32::MIN,
max_y: i32::MIN,
initialized: false,
}
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn update(&mut self, world_coords: &WorldTileCoords) {
self.initialized = true;
if world_coords.x < self.min_x {
self.min_x = world_coords.x;
}
if world_coords.y < self.min_y {
self.min_y = world_coords.y;
}
if world_coords.x > self.max_x {
self.max_x = world_coords.x;
}
if world_coords.y > self.max_y {
self.max_y = world_coords.y;
}
}
}
pub struct TileMaskPattern {
bounding_box: MinMaxBoundingBox,
pattern: Vec<MaskInstanceUniform>,
}
impl TileMaskPattern {
pub fn new() -> Self {
Self {
bounding_box: MinMaxBoundingBox::new(),
pattern: vec![],
}
}
pub fn update_bounds(&mut self, world_coords: &WorldTileCoords) {
self.bounding_box.update(world_coords)
}
pub fn as_slice(&self) -> &[MaskInstanceUniform] {
self.pattern.as_slice()
}
pub fn instances(&self) -> u32 {
self.pattern.len() as u32
}
fn vertical(&mut self, dx: i32, dy: i32, anchor_x: f32, anchor_y: f32, extent: f32) {
for i in 0..(dx.abs() / 2) {
self.pattern.push(MaskInstanceUniform::new(
[anchor_x + ((i * 2) + 1) as f32 * extent, anchor_y],
1.0,
dy as f32,
[0.0, 1.0, 0.0, 1.0],
));
}
}
fn horizontal(&mut self, dx: i32, dy: i32, anchor_x: f32, anchor_y: f32, extent: f32) {
for i in 0..(dy.abs() / 2) {
self.pattern.push(MaskInstanceUniform::new(
[anchor_x, anchor_y - extent - (i * 2) as f32 * extent],
dx as f32,
1.0,
[0.0, 0.0, 1.0, 1.0],
));
}
}
pub fn update_pattern(&mut self, z: u8, extent: f32) {
if !self.bounding_box.is_initialized() {
return;
}
self.pattern.clear();
let start: WorldTileCoords = (self.bounding_box.min_x, self.bounding_box.max_y, z).into(); // upper left corner
let end: WorldTileCoords = (self.bounding_box.max_x, self.bounding_box.min_y, z).into(); // lower right corner
let aligned_start = start.into_aligned();
let aligned_end = end.into_aligned().to_lower_right();
let start_world = start.into_world(extent);
let dy = aligned_end.y - aligned_start.0.y;
let dx = aligned_end.x - aligned_start.0.x;
let anchor_x = start_world.x;
let anchor_y = start_world.y;
// red step
self.pattern.push(MaskInstanceUniform::new(
[anchor_x, anchor_y],
dx as f32,
dy as f32,
[1.0, 0.0, 0.0, 1.0],
));
// green step
self.vertical(dx, dy, anchor_x, anchor_y, extent);
// blue step
self.horizontal(dx, dy, anchor_x, anchor_y, extent);
// violet step
self.vertical(dx, dy, anchor_x, anchor_y, extent);
}
}