mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Dynamically render tiles and fix stencil buffer layout
This commit is contained in:
parent
cb47623caf
commit
53903c5fbc
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 202 KiB |
@ -12,7 +12,7 @@ fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let cache_io = Cache::new();
|
||||
let mut cache_io = Cache::new();
|
||||
let cache_main = cache_io.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
|
||||
@ -111,7 +111,7 @@ impl InputHandler {
|
||||
scene.stroke_width + (self.target_stroke_width - scene.stroke_width) / 5.0;
|
||||
|
||||
// Animate the strokes of primitive
|
||||
scene.cpu_primitives[0 as usize].width = scene.stroke_width;
|
||||
/* scene.cpu_primitives[0 as usize].width = scene.stroke_width;*/
|
||||
/*
|
||||
scene.cpu_primitives[STROKE_PRIM_ID as usize].color = [
|
||||
(time_secs * 0.8 - 1.6).sin() * 0.1 + 0.1,
|
||||
|
||||
@ -14,12 +14,14 @@ use crate::tesselation::{IndexDataType, Tesselated};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TesselatedTile {
|
||||
pub id: u32,
|
||||
pub coords: TileCoords,
|
||||
pub geometry: VertexBuffers<GpuVertexUniform, IndexDataType>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cache {
|
||||
current_id: u32,
|
||||
requests: Arc<WorkQueue<TileCoords>>,
|
||||
responses: Arc<WorkQueue<TesselatedTile>>,
|
||||
}
|
||||
@ -27,6 +29,7 @@ pub struct Cache {
|
||||
impl Cache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_id: 0,
|
||||
requests: Arc::new(WorkQueue::new()),
|
||||
responses: Arc::new(WorkQueue::new()),
|
||||
}
|
||||
@ -41,7 +44,7 @@ impl Cache {
|
||||
self.responses.try_pop_all()
|
||||
}
|
||||
|
||||
pub fn run_loop(&self) {
|
||||
pub fn run_loop(&mut self) {
|
||||
loop {
|
||||
while let Some(coords) = self.requests.pop() {
|
||||
if let Some(file) = static_database::get_tile(&coords) {
|
||||
@ -51,7 +54,12 @@ impl Cache {
|
||||
VertexBuffers::new();
|
||||
|
||||
tile.tesselate_stroke(&mut geometry, 1);
|
||||
self.responses.push(TesselatedTile { coords, geometry });
|
||||
self.responses.push(TesselatedTile {
|
||||
id: self.current_id,
|
||||
coords,
|
||||
geometry,
|
||||
});
|
||||
self.current_id += 1;
|
||||
info!("tile ready: {:?}", &coords);
|
||||
} else {
|
||||
info!("tile failed: {:?}", &coords);
|
||||
|
||||
@ -63,7 +63,7 @@ pub async fn setup(window: winit::window::Window, event_loop: EventLoop<()>, cac
|
||||
let dt = now - last_render_time;
|
||||
last_render_time = now;
|
||||
input.update_state(&mut state, dt);
|
||||
state.update(&cache);
|
||||
state.upload_tile_geometry(&cache);
|
||||
match state.render() {
|
||||
Ok(_) => {}
|
||||
// Reconfigure the surface if lost
|
||||
|
||||
@ -64,6 +64,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
pub fn allocate_geometry(
|
||||
&mut self,
|
||||
queue: &Q,
|
||||
id: u32,
|
||||
coords: TileCoords,
|
||||
geometry: &VertexBuffers<V, I>,
|
||||
) {
|
||||
@ -73,6 +74,7 @@ impl<Q: Queue<B>, B, V: bytemuck::Pod, I: bytemuck::Pod> BufferPool<Q, B, V, I>
|
||||
let new_indices = (geometry.indices.len() * indices_stride) as wgpu::BufferAddress;
|
||||
|
||||
let maybe_entry = IndexEntry {
|
||||
id,
|
||||
coords,
|
||||
indices_stride: indices_stride as u64,
|
||||
vertices: self.vertices.make_room(new_vertices, &mut self.index, true),
|
||||
@ -182,6 +184,7 @@ impl<B> BackingBuffer<B> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IndexEntry {
|
||||
pub id: u32,
|
||||
pub coords: TileCoords,
|
||||
indices_stride: u64,
|
||||
vertices: Range<wgpu::BufferAddress>,
|
||||
|
||||
@ -37,7 +37,7 @@ pub fn create_map_render_pipeline_description<'a>(
|
||||
}
|
||||
} else {
|
||||
wgpu::StencilFaceState {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
|
||||
@ -49,17 +49,17 @@ impl GlobalsUniform {
|
||||
pub struct GpuVertexUniform {
|
||||
pub position: Vec2f32,
|
||||
pub normal: Vec2f32,
|
||||
pub prim_id: u32,
|
||||
pub tile_id: u32,
|
||||
_pad1: i32, // _padX aligns it to 8 bytes = AlignOf(Vec2f32=vec2<f32>):
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
}
|
||||
|
||||
impl GpuVertexUniform {
|
||||
pub fn new(position: Vec2f32, normal: Vec2f32, prim_id: u32) -> Self {
|
||||
pub fn new(position: Vec2f32, normal: Vec2f32, tile_id: u32) -> Self {
|
||||
Self {
|
||||
position,
|
||||
normal,
|
||||
prim_id,
|
||||
tile_id,
|
||||
_pad1: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -98,33 +98,18 @@ impl MaskInstanceUniform {
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
pub struct PrimitiveUniform {
|
||||
pub struct TileUniform {
|
||||
pub color: Vec4f32,
|
||||
pub translate: Vec2f32,
|
||||
pub z_index: i32,
|
||||
pub width: f32,
|
||||
pub angle: f32,
|
||||
pub scale: f32,
|
||||
_pad1: i32, // _padX aligns it to 16 bytes = AlignOf(Vec4f32/vec4<f32>):
|
||||
_pad2: i32, // https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
}
|
||||
|
||||
impl PrimitiveUniform {
|
||||
pub fn new(
|
||||
color: Vec4f32,
|
||||
translate: Vec2f32,
|
||||
z_index: i32,
|
||||
width: f32,
|
||||
angle: f32,
|
||||
scale: f32,
|
||||
) -> Self {
|
||||
impl TileUniform {
|
||||
pub fn new(color: Vec4f32, translate: Vec2f32) -> Self {
|
||||
Self {
|
||||
color,
|
||||
translate,
|
||||
z_index,
|
||||
width,
|
||||
angle,
|
||||
scale,
|
||||
_pad1: Default::default(),
|
||||
_pad2: Default::default(),
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ pub mod tile_mask {
|
||||
&[wgpu::ColorTargetState {
|
||||
format: COLOR_TEXTURE_FORMAT,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
write_mask: wgpu::ColorWrites::empty(),
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
||||
@ -8,23 +8,19 @@ struct GlobalsUniform {
|
||||
camera: CameraUniform;
|
||||
};
|
||||
|
||||
struct PrimitiveUniform {
|
||||
struct TileUniform {
|
||||
color: vec4<f32>;
|
||||
translate: vec2<f32>;
|
||||
z_index: i32;
|
||||
width: f32;
|
||||
angle: f32;
|
||||
scale: f32;
|
||||
pad1: i32;
|
||||
pad2: i32;
|
||||
};
|
||||
|
||||
struct Primitives {
|
||||
primitives: [[stride(48)]] array<PrimitiveUniform, 256>;
|
||||
struct Tiles {
|
||||
tiles: [[stride(32)]] array<TileUniform, 128>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]] var<uniform> globals: GlobalsUniform;
|
||||
[[group(0), binding(1)]] var<uniform> u_primitives: Primitives;
|
||||
[[group(0), binding(1)]] var<uniform> tiles: Tiles;
|
||||
|
||||
struct VertexOutput {
|
||||
[[location(0)]] v_color: vec4<f32>;
|
||||
@ -33,17 +29,17 @@ struct VertexOutput {
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn main(
|
||||
[[location(0)]] a_position: vec2<f32>,
|
||||
[[location(1)]] a_normal: vec2<f32>,
|
||||
[[location(2)]] a_prim_id: u32,
|
||||
[[location(0)]] position: vec2<f32>,
|
||||
[[location(1)]] normal: vec2<f32>,
|
||||
[[location(2)]] tile_id: u32,
|
||||
[[builtin(instance_index)]] instance_idx: u32 // instance_index is used when we have multiple instances of the same "object"
|
||||
) -> VertexOutput {
|
||||
let prim: PrimitiveUniform = u_primitives.primitives[a_prim_id];
|
||||
let tile = tiles.tiles[instance_idx];
|
||||
let z = 0.0;
|
||||
|
||||
let world_pos = a_position + prim.translate + a_normal * prim.width;
|
||||
let world_pos = position + tile.translate + normal;
|
||||
|
||||
let position = globals.camera.view_proj * vec4<f32>(world_pos, z, 1.0);
|
||||
|
||||
return VertexOutput(prim.color, position);
|
||||
return VertexOutput(tile.color, position);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::cmp;
|
||||
use std::default::Default;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Range;
|
||||
|
||||
@ -16,7 +17,7 @@ use vector_tile::parse_tile_reader;
|
||||
|
||||
use crate::fps_meter::FPSMeter;
|
||||
use crate::io::cache::Cache;
|
||||
use crate::io::static_database;
|
||||
use crate::io::{static_database, TileCoords};
|
||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
|
||||
use crate::render::{camera, shaders};
|
||||
@ -29,26 +30,16 @@ use super::texture::Texture;
|
||||
|
||||
pub struct SceneParams {
|
||||
pub stroke_width: f32,
|
||||
pub cpu_primitives: Vec<PrimitiveUniform>,
|
||||
}
|
||||
|
||||
impl Default for SceneParams {
|
||||
fn default() -> Self {
|
||||
SceneParams {
|
||||
stroke_width: 1.0,
|
||||
cpu_primitives: vec![],
|
||||
}
|
||||
SceneParams { stroke_width: 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32;
|
||||
|
||||
const PRIM_BUFFER_LEN: usize = 256;
|
||||
const STROKE_PRIM_ID: u32 = 0;
|
||||
const FILL_PRIM_ID: u32 = 1;
|
||||
const SECOND_TILE_FILL_PRIM_ID: u32 = 2;
|
||||
const SECOND_TILE_STROKE_PRIM_ID: u32 = 5;
|
||||
|
||||
pub struct State {
|
||||
instance: wgpu::Instance,
|
||||
|
||||
@ -72,7 +63,7 @@ pub struct State {
|
||||
|
||||
depth_texture: Texture,
|
||||
|
||||
prims_uniform_buffer: wgpu::Buffer,
|
||||
tiles_uniform_buffer: wgpu::Buffer,
|
||||
globals_uniform_buffer: wgpu::Buffer,
|
||||
|
||||
buffer_pool: BufferPool<Queue, Buffer, GpuVertexUniform, IndexDataType>,
|
||||
@ -87,31 +78,7 @@ pub struct State {
|
||||
|
||||
impl SceneParams {
|
||||
pub fn new() -> Self {
|
||||
let mut cpu_primitives = Vec::with_capacity(PRIM_BUFFER_LEN);
|
||||
for _ in 0..PRIM_BUFFER_LEN {
|
||||
cpu_primitives.push(PrimitiveUniform::new(
|
||||
[1.0, 0.0, 0.0, 1.0],
|
||||
[0.0, 0.0],
|
||||
0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
));
|
||||
}
|
||||
|
||||
// Stroke primitive
|
||||
cpu_primitives[STROKE_PRIM_ID as usize] =
|
||||
PrimitiveUniform::new([1.0, 0.0, 1.0, 1.0], [0.0, 0.0], 0, 1.0, 0.0, 1.0);
|
||||
cpu_primitives[SECOND_TILE_STROKE_PRIM_ID as usize] =
|
||||
PrimitiveUniform::new([0.5, 0.8, 0.1, 1.0], [4096.0, 0.0], 0, 1.0, 0.0, 1.0);
|
||||
// Main fill primitive
|
||||
cpu_primitives[FILL_PRIM_ID as usize] =
|
||||
PrimitiveUniform::new([0.0, 0.0, 1.0, 1.0], [0.0, 0.0], 0, 1.0, 0.0, 1.0);
|
||||
cpu_primitives[SECOND_TILE_FILL_PRIM_ID as usize] =
|
||||
PrimitiveUniform::new([0.0, 1.0, 1.0, 1.0], [4096.0, 0.0], 0, 1.0, 0.0, 1.0);
|
||||
|
||||
Self {
|
||||
cpu_primitives,
|
||||
..SceneParams::default()
|
||||
}
|
||||
}
|
||||
@ -198,18 +165,14 @@ impl State {
|
||||
MaskInstanceUniform::new([0.0, 0.0], 4.0, 4.0, [1.0, 0.0, 0.0, 1.0]), // horizontal
|
||||
//MaskInstanceUniform::new([0.0, 2.0 * 4096.0], 4.0, 1.0, [1.0, 0.0, 0.0, 1.0]), // vertical
|
||||
// Step 2
|
||||
MaskInstanceUniform::new([1.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 0.0, 1.0, 1.0]), // vertical
|
||||
MaskInstanceUniform::new([0.0, 1.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
|
||||
MaskInstanceUniform::new([3.0 * 4096.0, 0.0], 1.0, 4.0, [0.0, 0.0, 1.0, 1.0]), // vertical
|
||||
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 4.0, 1.0, [0.0, 0.0, 1.0, 1.0]), // horizontal
|
||||
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, 1.0, 0.0, 1.0]), // horizontal
|
||||
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 4.0, 1.0, [0.0, 1.0, 0.0, 1.0]), // horizontal
|
||||
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([0.0, 1.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
|
||||
MaskInstanceUniform::new([0.0, 3.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
|
||||
MaskInstanceUniform::new([2.0 * 4096.0, 1.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
|
||||
MaskInstanceUniform::new([2.0 * 4096.0, 3.0 * 4096.0], 1.0, 1.0, [0.0, 1.0, 1.0, 1.0]), // horizontal
|
||||
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_mask_instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
@ -218,18 +181,15 @@ impl State {
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
|
||||
let prim_buffer_byte_size = cmp::max(
|
||||
MIN_BUFFER_SIZE,
|
||||
(PRIM_BUFFER_LEN * std::mem::size_of::<PrimitiveUniform>()) as u64,
|
||||
);
|
||||
let globals_buffer_byte_size = cmp::max(
|
||||
MIN_BUFFER_SIZE,
|
||||
std::mem::size_of::<GlobalsUniform>() as u64,
|
||||
);
|
||||
|
||||
let prims_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Prims ubo"),
|
||||
size: prim_buffer_byte_size,
|
||||
let tiles_uniform_buffer_size = std::mem::size_of::<TileUniform>() as u64 * 128; // FIXME: Tile count?
|
||||
let tiles_uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Tiles ubo"),
|
||||
size: tiles_uniform_buffer_size,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
@ -260,7 +220,7 @@ impl State {
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: wgpu::BufferSize::new(prim_buffer_byte_size),
|
||||
min_binding_size: wgpu::BufferSize::new(tiles_uniform_buffer_size),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
@ -280,7 +240,7 @@ impl State {
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Buffer(
|
||||
prims_uniform_buffer.as_entire_buffer_binding(),
|
||||
tiles_uniform_buffer.as_entire_buffer_binding(),
|
||||
),
|
||||
},
|
||||
],
|
||||
@ -367,7 +327,7 @@ impl State {
|
||||
sample_count,
|
||||
scene: SceneParams::new(),
|
||||
globals_uniform_buffer,
|
||||
prims_uniform_buffer,
|
||||
tiles_uniform_buffer: tiles_uniform_buffer,
|
||||
fps_meter: FPSMeter::new(),
|
||||
tile_mask_instances,
|
||||
camera,
|
||||
@ -425,12 +385,34 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, cache: &Cache) {
|
||||
for tile in cache.pop_all().iter() {
|
||||
pub fn upload_tile_geometry(&mut self, cache: &Cache) {
|
||||
const OFFSET_X: u32 = 2179;
|
||||
const OFFSET_Y: u32 = 1421;
|
||||
|
||||
let upload = cache.pop_all();
|
||||
|
||||
for tile in upload.iter() {
|
||||
let new_coords = TileCoords {
|
||||
x: tile.coords.x - OFFSET_X,
|
||||
y: tile.coords.y - OFFSET_Y,
|
||||
z: tile.coords.z,
|
||||
};
|
||||
|
||||
self.buffer_pool
|
||||
.allocate_geometry(&self.queue, tile.coords, &tile.geometry);
|
||||
.allocate_geometry(&self.queue, tile.id, new_coords, &tile.geometry);
|
||||
|
||||
let uniform = TileUniform::new(
|
||||
[0.0, 0.0, 0.0, 1.0],
|
||||
[new_coords.x as f32 * 4096.0, new_coords.y as f32 * 4096.0],
|
||||
);
|
||||
self.queue.write_buffer(
|
||||
&self.tiles_uniform_buffer,
|
||||
std::mem::size_of::<TileUniform>() as u64 * tile.id as u64,
|
||||
bytemuck::cast_slice(&[uniform]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||
let frame = self.surface.get_current_texture()?;
|
||||
let scene = &mut self.scene;
|
||||
@ -446,12 +428,6 @@ impl State {
|
||||
self.camera.create_camera_uniform(&self.projection),
|
||||
)]),
|
||||
);
|
||||
|
||||
self.queue.write_buffer(
|
||||
&self.prims_uniform_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&scene.cpu_primitives),
|
||||
);
|
||||
}
|
||||
|
||||
let mut encoder = self
|
||||
@ -504,12 +480,21 @@ impl State {
|
||||
pass.set_pipeline(&self.mask_pipeline);
|
||||
pass.set_vertex_buffer(0, self.tile_mask_instances.slice(..));
|
||||
// Draw 11 squares each out of 6 vertices
|
||||
pass.draw(0..6, 0..11);
|
||||
pass.draw(0..6, 0..7);
|
||||
}
|
||||
{
|
||||
for entry in self.buffer_pool.available_vertices() {
|
||||
let TileCoords { x, y, .. } = entry.coords;
|
||||
|
||||
pass.set_pipeline(&self.render_pipeline);
|
||||
pass.set_stencil_reference(1);
|
||||
let reference = match (x, 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,
|
||||
};
|
||||
pass.set_stencil_reference(reference);
|
||||
pass.set_index_buffer(
|
||||
self.buffer_pool
|
||||
.indices()
|
||||
@ -526,7 +511,12 @@ impl State {
|
||||
pass.draw_indexed(self.tile_fill_range.clone(), 0, 0..1);
|
||||
}*/
|
||||
trace!("current buffer_pool index {:?}", self.buffer_pool.index);
|
||||
pass.draw_indexed(entry.indices_range(), 0, 0..1);
|
||||
// FIXME: Custom Instance index possibly breaks on Metal
|
||||
pass.draw_indexed(
|
||||
entry.indices_range(),
|
||||
0,
|
||||
entry.id as u32..(entry.id + 1) as u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user