Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: SDF Font Rendering #231

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data/0-255.pbf
Binary file not shown.
37 changes: 37 additions & 0 deletions data/sprites.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"circle-11": {
"height": 34,
"pixelRatio": 2,
"width": 34,
"x": 51,
"y": 0
},
"circle-alt-11": {
"height": 34,
"pixelRatio": 2,
"width": 34,
"x": 0,
"y": 84
},
"pin-standard": {
"height": 84,
"pixelRatio": 2,
"width": 51,
"x": 0,
"y": 0
},
"star-11": {
"height": 34,
"pixelRatio": 2,
"width": 34,
"x": 34,
"y": 84
},
"star-alt-11": {
"height": 34,
"pixelRatio": 2,
"width": 34,
"x": 68,
"y": 84
}
}
Binary file added data/sprites.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions maplibre/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,10 @@ smallvec = "1.9.0"
# Headless
png = { version = "0.17.5", optional = true }

# SDF font
image = { version = "0.24.5", features = ["png"] }
prost-types = "0.10.1"

[build-dependencies]
maplibre-build-tools = { path = "../maplibre-build-tools", version = "0.1.0" }
prost-build = "0.10.4"
22 changes: 22 additions & 0 deletions maplibre/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! This script is built and executed just before building the package.
//! It will validate the WGSL (WebGPU Shading Language) shaders and embed static files.

use std::{fs, path::PathBuf};

use maplibre_build_tools::wgsl::validate_project_wgsl;

#[cfg(feature = "embed-static-tiles")]
Expand Down Expand Up @@ -50,9 +52,29 @@ fn embed_tiles_statically() {
}
}

fn generate_protobuf() {
let proto_paths = fs::read_dir("./proto")
.unwrap()
.filter_map(|entry| {
let entry = entry.ok()?;
println!(
"cargo:rerun-if-changed={}",
entry.path().display().to_string()
);
Some(entry.path())
})
.collect::<Vec<_>>();

if !proto_paths.is_empty() {
prost_build::compile_protos(&proto_paths, &[PathBuf::from("./proto/")]).unwrap();
}
}

fn main() {
validate_project_wgsl();

#[cfg(feature = "embed-static-tiles")]
embed_tiles_statically();

generate_protobuf();
}
33 changes: 33 additions & 0 deletions maplibre/proto/glyphs.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Protocol Version 1

package glyphs;

option optimize_for = LITE_RUNTIME;

// Stores a glyph with metrics and optional SDF bitmap information.
message glyph {
required uint32 id = 1;

// A signed distance field of the glyph with a border of 3 pixels.
optional bytes bitmap = 2;

// Glyph metrics.
required uint32 width = 3;
required uint32 height = 4;
required sint32 left = 5;
required sint32 top = 6;
required uint32 advance = 7;
}

// Stores fontstack information and a list of faces.
message fontstack {
required string name = 1;
required string range = 2;
repeated glyph glyphs = 3;
}

message glyphs {
repeated fontstack stacks = 1;

extensions 16 to 8191;
}
1 change: 1 addition & 0 deletions maplibre/src/io/apc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum Message<T: Transferables> {
TileTessellated(T::TileTessellated),
LayerUnavailable(T::LayerUnavailable),
LayerTessellated(T::LayerTessellated),
SymbolLayerTessellated(T::SymbolLayerTessellated),

LayerIndexed(T::LayerIndexed),
}
Expand Down
3 changes: 1 addition & 2 deletions maplibre/src/io/geometry_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use geozero::{
error::GeozeroError, geo_types::GeoWriter, ColumnValue, FeatureProcessor, GeomProcessor,
PropertyProcessor,
};
use log::warn;
use rstar::{Envelope, PointDistance, RTree, RTreeObject, AABB};

use crate::{
Expand Down Expand Up @@ -313,7 +312,7 @@ impl FeatureProcessor for IndexProcessor {
.unwrap(),
),
_ => {
warn!("Unknown geometry in index")
log::trace!("Unknown geometry in index")
}
};

Expand Down
11 changes: 10 additions & 1 deletion maplibre/src/io/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use thiserror::Error;
use crate::{
coords::WorldTileCoords,
io::{apc::SendError, geometry_index::IndexedGeometry},
render::ShaderVertex,
render::{ShaderVertex, SymbolVertex},
tessellation::{IndexDataType, OverAlignedVertexBuffer},
};

Expand Down Expand Up @@ -42,6 +42,15 @@ pub trait PipelineProcessor: Downcast {
) -> Result<(), PipelineError> {
Ok(())
}
fn symbol_layer_tesselation_finished(
&mut self,
_coords: &WorldTileCoords,
_buffer: OverAlignedVertexBuffer<SymbolVertex, IndexDataType>,
_feature_indices: Vec<u32>,
_layer_data: tile::Layer,
) -> Result<(), PipelineError> {
Ok(())
}
fn layer_indexing_finished(
&mut self,
_coords: &WorldTileCoords,
Expand Down
46 changes: 42 additions & 4 deletions maplibre/src/io/tile_pipelines.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashSet;

use geozero::GeozeroDatasource;
use geozero::{mvt::tile::Layer, GeozeroDatasource};
use prost::Message;

use crate::{
Expand All @@ -9,7 +9,9 @@ use crate::{
pipeline::{DataPipeline, PipelineContext, PipelineEnd, PipelineError, Processable},
TileRequest,
},
tessellation::{zero_tessellator::ZeroTessellator, IndexDataType},
tessellation::{
text_tesselator::TextTessellator, zero_tessellator::ZeroTessellator, IndexDataType,
},
};

#[derive(Default)]
Expand Down Expand Up @@ -134,12 +136,48 @@ impl Processable for TessellateLayer {
}
}

#[derive(Default)]
pub struct TessellateGlyphQuads;

impl Processable for TessellateGlyphQuads {
type Input = (TileRequest, geozero::mvt::Tile);
type Output = (TileRequest, geozero::mvt::Tile);

#[tracing::instrument(skip_all)]
fn process(
&self,
(tile_request, mut tile): Self::Input,
context: &mut PipelineContext,
) -> Result<Self::Output, PipelineError> {
let coords = &tile_request.coords;

let mut tessellator = TextTessellator::<IndexDataType>::default();
for layer in &mut tile.layers {
layer.process(&mut tessellator).unwrap();
}

let mut layer1 = Layer::default();
layer1.name = "text".to_string(); // FIXME
context.processor_mut().symbol_layer_tesselation_finished(
coords,
tessellator.quad_buffer.into(),
tessellator.feature_indices,
layer1,
)?;

Ok((tile_request, tile))
}
}

pub fn build_vector_tile_pipeline() -> impl Processable<Input = <ParseTile as Processable>::Input> {
DataPipeline::new(
ParseTile,
DataPipeline::new(
TessellateLayer,
DataPipeline::new(IndexLayer, PipelineEnd::default()),
TessellateGlyphQuads::default(),
DataPipeline::new(
TessellateLayer,
DataPipeline::new(IndexLayer, PipelineEnd::default()),
),
),
)
}
Expand Down
28 changes: 23 additions & 5 deletions maplibre/src/io/tile_repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
coords::{Quadkey, WorldTileCoords},
render::{
resource::{BufferPool, Queue},
ShaderVertex,
ShaderVertex, SymbolVertex,
},
tessellation::{IndexDataType, OverAlignedVertexBuffer},
};
Expand All @@ -28,20 +28,29 @@ pub enum StoredLayer {
/// Holds for each feature the count of indices.
feature_indices: Vec<u32>,
},
TessellatedSymbolLayer {
coords: WorldTileCoords,
layer_name: String,
buffer: OverAlignedVertexBuffer<SymbolVertex, IndexDataType>,
/// Holds for each feature the count of indices.
feature_indices: Vec<u32>,
},
}

impl StoredLayer {
pub fn get_coords(&self) -> WorldTileCoords {
match self {
StoredLayer::UnavailableLayer { coords, .. } => *coords,
StoredLayer::TessellatedLayer { coords, .. } => *coords,
StoredLayer::TessellatedSymbolLayer { coords, .. } => *coords,
}
}

pub fn layer_name(&self) -> &str {
match self {
StoredLayer::UnavailableLayer { layer_name, .. } => layer_name.as_str(),
StoredLayer::TessellatedLayer { layer_name, .. } => layer_name.as_str(),
StoredLayer::TessellatedSymbolLayer { layer_name, .. } => layer_name.as_str(),
}
}
}
Expand Down Expand Up @@ -151,16 +160,25 @@ impl TileRepository {

/// Returns the list of tessellated layers at the given world tile coords, which are loaded in
/// the BufferPool
pub fn iter_loaded_layers_at<Q: Queue<B>, B, V: Pod, I: Pod, TM: Pod, FM: Pod>(
pub fn loaded_layers_at<Q: Queue<B>, B, V1: Pod, V2: Pod, I: Pod, TM: Pod, FM: Pod>(
&self,
buffer_pool: &BufferPool<Q, B, V, I, TM, FM>,
buffer_pool1: &BufferPool<Q, B, V1, I, TM, FM>,
buffer_pool2: &BufferPool<Q, B, V2, I, TM, FM>,
coords: &WorldTileCoords,
) -> Option<Vec<&StoredLayer>> {
let loaded_layers = buffer_pool.get_loaded_layers_at(coords).unwrap_or_default();
let loaded_layers1 = buffer_pool1
.get_loaded_layers_at(coords)
.unwrap_or_default();
let loaded_layers2 = buffer_pool2
.get_loaded_layers_at(coords)
.unwrap_or_default();

self.iter_layers_at(coords).map(|layers| {
layers
.filter(|result| !loaded_layers.contains(&result.layer_name()))
.filter(|result| {
!loaded_layers1.contains(&result.layer_name())
&& !loaded_layers2.contains(&result.layer_name())
})
.collect::<Vec<_>>()
})
}
Expand Down
Loading