maplibre/sdf/
resource_system.rs

1//! Prepares GPU-owned resources by initializing them if they are uninitialized or out-of-date.
2use wgpu::util::{DeviceExt, TextureDataOrder};
3
4use crate::{
5    context::MapContext,
6    render::{
7        eventually::Eventually,
8        resource::{RenderPipeline, TilePipeline},
9        shaders,
10        shaders::Shader,
11        RenderResources, Renderer,
12    },
13    sdf::{resource::GlyphTexture, text::GlyphSet, SymbolBufferPool, SymbolPipeline},
14    tcs::system::{SystemError, SystemResult},
15    vector::resource::BufferPool,
16};
17
18pub fn resource_system(
19    MapContext {
20        world,
21        renderer:
22            Renderer {
23                device,
24                queue,
25                resources: RenderResources { surface, .. },
26                settings,
27                ..
28            },
29        ..
30    }: &mut MapContext,
31) -> SystemResult {
32    let Some((
33        symbol_buffer_pool,
34        symbol_pipeline,
35        glyph_texture_sampler,
36        glyph_texture_bind_group,
37    )) = world.resources.query_mut::<(
38        &mut Eventually<SymbolBufferPool>,
39        &mut Eventually<SymbolPipeline>,
40        &mut Eventually<(wgpu::Texture, wgpu::Sampler)>,
41        &mut Eventually<GlyphTexture>,
42    )>()
43    else {
44        return Err(SystemError::Dependencies);
45    };
46
47    symbol_buffer_pool.initialize(|| BufferPool::from_device(device));
48
49    symbol_pipeline.initialize(|| {
50        let tile_shader = shaders::SymbolShader {
51            format: surface.surface_format(),
52        };
53
54        let pipeline = TilePipeline::new(
55            "symbol_pipeline".into(),
56            *settings,
57            tile_shader.describe_vertex(),
58            tile_shader.describe_fragment(),
59            false,
60            false,
61            true, // TODO ignore tile mask
62            false,
63            surface.is_multisampling_supported(settings.msaa),
64            false,
65            true,
66        )
67        .describe_render_pipeline()
68        .initialize(device);
69
70        let (texture, sampler) = glyph_texture_sampler.initialize(|| {
71            let data = include_bytes!("../../../data/0-255.pbf");
72            let glyphs = GlyphSet::try_from(data.as_slice()).unwrap();
73
74            let (width, height) = glyphs.get_texture_dimensions();
75
76            let texture = device.create_texture_with_data(
77                queue,
78                &wgpu::TextureDescriptor {
79                    label: Some("Glyph Texture"),
80                    size: wgpu::Extent3d {
81                        width: width as _,
82                        height: height as _,
83                        depth_or_array_layers: 1,
84                    },
85                    mip_level_count: 1,
86                    sample_count: 1,
87                    dimension: wgpu::TextureDimension::D2,
88                    format: wgpu::TextureFormat::R8Unorm,
89                    usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
90                    view_formats: &[wgpu::TextureFormat::R8Unorm], // TODO
91                },
92                TextureDataOrder::LayerMajor, // TODO
93                glyphs.get_texture_bytes(),
94            );
95
96            let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
97                // SDF rendering requires linear interpolation
98                mag_filter: wgpu::FilterMode::Linear,
99                min_filter: wgpu::FilterMode::Linear,
100                ..Default::default()
101            });
102
103            (texture, sampler)
104        });
105
106        glyph_texture_bind_group.initialize(|| {
107            GlyphTexture::from_device(device, texture, sampler, &pipeline.get_bind_group_layout(0))
108        });
109
110        SymbolPipeline(pipeline)
111    });
112    Ok(())
113}