Procházet zdrojové kódy

Increase tile atlas size when needed

Johannes Hofmann před 8 roky
rodič
revize
24ef61939c
4 změnil soubory, kde provedl 98 přidání a 21 odebrání
  1. 16
    1
      src/main.rs
  2. 13
    11
      src/map_view_gl.rs
  3. 25
    0
      src/texture.rs
  4. 44
    9
      src/tile_atlas.rs

+ 16
- 1
src/main.rs Zobrazit soubor

@@ -273,6 +273,7 @@ fn main() {
273 273
     // estimated draw duration
274 274
     let mut est_draw_dur = duration_per_frame;
275 275
     let mut last_draw = Instant::now();
276
+    let mut increase_atlas_size = true;
276 277
 
277 278
     'outer: for event in window.wait_events() {
278 279
         debug!("{:?}", &event);
@@ -321,11 +322,25 @@ fn main() {
321 322
 
322 323
         if redraw {
323 324
             let draw_start = Instant::now();
324
-            map.draw(sources.current());
325
+            let draw_result = map.draw(sources.current());
325 326
             let draw_dur = draw_start.elapsed();
326 327
 
327 328
             let _ = window.swap_buffers();
328 329
 
330
+            //TODO increase atlas size earlier to avoid excessive copying to the GPU
331
+            //TODO increase max tile cache size?
332
+            increase_atlas_size = {
333
+                match (draw_result, increase_atlas_size) {
334
+                    (Err(draws), true) if draws > 1 => {
335
+                        map.increase_atlas_size().is_ok()
336
+                    },
337
+                    (Ok(draws), true) if draws > 1 => {
338
+                        map.increase_atlas_size().is_ok()
339
+                    },
340
+                    _ => increase_atlas_size,
341
+                }
342
+            };
343
+
329 344
             last_draw = Instant::now();
330 345
 
331 346
             debug!("draw: {} sec (est {} sec)", dur_to_sec(draw_dur), dur_to_sec(est_draw_dur));

+ 13
- 11
src/map_view_gl.rs Zobrazit soubor

@@ -3,7 +3,6 @@ use ::std::ffi::CStr;
3 3
 use buffer::{Buffer, DrawMode};
4 4
 use context::Context;
5 5
 use coord::{ScreenCoord, View};
6
-use image;
7 6
 use map_view::MapView;
8 7
 use program::Program;
9 8
 use texture::{Texture, TextureFormat};
@@ -31,13 +30,8 @@ impl<'a> MapViewGl<'a> {
31 30
             let mut program = Program::from_paths(cx, "shader/map.vert", "shader/map.frag");
32 31
 
33 32
             check_gl_errors!(cx);
34
-            let mut tex = Texture::empty(cx, 2048, 2048, TextureFormat::Rgb8);
33
+            let tex = Texture::empty(cx, 2048, 2048, TextureFormat::Rgb8);
35 34
             check_gl_errors!(cx);
36
-            {
37
-                let img = image::open("img/no_tile.png").unwrap();
38
-                tex.sub_image(0, 0, &img);
39
-                check_gl_errors!(cx);
40
-            }
41 35
 
42 36
             let buf = Buffer::new(cx, &[], 0);
43 37
 
@@ -89,7 +83,14 @@ impl<'a> MapViewGl<'a> {
89 83
         }
90 84
     }
91 85
 
92
-    pub fn draw(&mut self, source: &TileSource) {
86
+    pub fn increase_atlas_size(&mut self) -> Result<(), ()> {
87
+        self.tile_atlas.double_texture_size()
88
+    }
89
+
90
+    /// Returns `Err` when tile cache is too small for this view.
91
+    /// Returns the number of OpenGL draw calls, which can be decreased to `1` by increasing the
92
+    /// size of the tile atlas.
93
+    pub fn draw(&mut self, source: &TileSource) -> Result<usize, usize> {
93 94
         self.tile_cache.set_view_location(View {
94 95
             source_id: source.id(),
95 96
             zoom: self.map_view.tile_zoom(),
@@ -167,6 +168,7 @@ impl<'a> MapViewGl<'a> {
167 168
             self.buf.draw(DrawMode::Triangles);
168 169
 
169 170
             num_draws += 1;
171
+
170 172
             debug!("draw #{}: tvt.len() = {}, remainder = {:?}, max_tiles = {}",
171 173
                 num_draws,
172 174
                 textured_visible_tiles.len(),
@@ -175,15 +177,15 @@ impl<'a> MapViewGl<'a> {
175 177
 
176 178
             if max_tiles_to_use <= 0 {
177 179
                 warn!("tile cache is too small for this view.");
178
-                break;
180
+                return Err(num_draws);
179 181
             }
180 182
 
181 183
             match remainder_opt {
182
-                None => break,
184
+                None => return Ok(num_draws),
183 185
                 Some(new_remainder) => {
184 186
                     if new_remainder.len() >= remainder.len() {
185 187
                         warn!("failed to draw all tiles. number of remaining tiles did not decrease.");
186
-                        break;
188
+                        return Err(num_draws);
187 189
                     } else {
188 190
                         remainder = new_remainder;
189 191
                     }

+ 25
- 0
src/texture.rs Zobrazit soubor

@@ -10,6 +10,7 @@ pub struct Texture<'a> {
10 10
     texture_obj: u32,
11 11
     width: u32,
12 12
     height: u32,
13
+    format: TextureFormat,
13 14
 }
14 15
 
15 16
 #[derive(Clone, Debug)]
@@ -64,6 +65,7 @@ impl<'a> Texture<'a> {
64 65
             texture_obj: tex_obj,
65 66
             width: width,
66 67
             height: height,
68
+            format: format,
67 69
         }
68 70
     }
69 71
 
@@ -90,6 +92,25 @@ impl<'a> Texture<'a> {
90 92
         }
91 93
     }
92 94
 
95
+    pub fn resize(&mut self, width: u32, height: u32) {
96
+        unsafe {
97
+            self.cx.gl.BindTexture(context::gl::TEXTURE_2D, self.texture_obj);
98
+            self.cx.gl.TexImage2D(
99
+                context::gl::TEXTURE_2D,
100
+                0, // level
101
+                self.format.to_gl_enum() as i32,
102
+                width as i32,
103
+                height as i32,
104
+                0, // border (must be zero)
105
+                self.format.to_gl_enum(),
106
+                context::gl::UNSIGNED_BYTE,
107
+                ::std::ptr::null() as *const _);
108
+
109
+            self.width = width;
110
+            self.height = height;
111
+        }
112
+    }
113
+
93 114
     pub fn id(&self) -> TextureId {
94 115
         TextureId {
95 116
             id: self.texture_obj,
@@ -103,6 +124,10 @@ impl<'a> Texture<'a> {
103 124
     pub fn height(&self) -> u32 {
104 125
         self.height
105 126
     }
127
+
128
+    pub fn context(&self) -> &Context {
129
+        self.cx
130
+    }
106 131
 }
107 132
 
108 133
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]

+ 44
- 9
src/tile_atlas.rs Zobrazit soubor

@@ -1,4 +1,5 @@
1 1
 use coord::{ScreenRect, SubTileCoord, TileCoord, TextureRect};
2
+use image;
2 3
 use linked_hash_map::LinkedHashMap;
3 4
 use map_view::VisibleTile;
4 5
 use std::collections::HashMap;
@@ -24,26 +25,60 @@ pub struct TileAtlas<'a> {
24 25
 }
25 26
 
26 27
 impl<'a> TileAtlas<'a> {
27
-    pub fn new(tex: Texture<'a>, tile_size: u32) -> Self {
28
-        let slots_x = tex.width() / tile_size;
29
-        let slots_y = tex.height() / tile_size;
28
+    fn init(&mut self) {
29
+        // add tile for default slot
30
+        {
31
+            let img = image::open("img/no_tile.png").unwrap();
32
+            self.texture.sub_image(0, 0, &img);
33
+        }
34
+
35
+        let slots_x = self.texture.width() / self.tile_size;
36
+        let slots_y = self.texture.height() / self.tile_size;
30 37
         let num_slots = (slots_x * slots_y) as usize;
31 38
 
32
-        let mut slots_lru = LinkedHashMap::with_capacity(num_slots);
39
+        self.slots_lru.clear();
40
+        self.slots_lru.reserve(num_slots);
33 41
         for x in 0..slots_x {
34 42
             for y in 0..slots_y {
35 43
                 let slot = CacheSlot { x: x, y: y };
36
-                slots_lru.insert(slot, None);
44
+                self.slots_lru.insert(slot, None);
37 45
             }
38 46
         }
47
+        self.slots_lru.remove(&Self::default_slot());
39 48
 
40
-        slots_lru.remove(&Self::default_slot());
49
+        self.tile_to_slot.clear();
50
+        self.tile_to_slot.reserve(num_slots);
51
+    }
41 52
 
42
-        TileAtlas {
53
+    pub fn new(tex: Texture<'a>, tile_size: u32) -> Self {
54
+        let mut atlas = TileAtlas {
43 55
             texture: tex,
44 56
             tile_size: tile_size,
45
-            slots_lru: slots_lru,
46
-            tile_to_slot: HashMap::with_capacity(num_slots),
57
+            slots_lru: LinkedHashMap::new(),
58
+            tile_to_slot: HashMap::new(),
59
+        };
60
+
61
+        atlas.init();
62
+        atlas
63
+    }
64
+
65
+    pub fn double_texture_size(&mut self) -> Result<(), ()> {
66
+        let max_size = self.texture.context().max_texture_size() as u32;
67
+
68
+        let new_width = self.texture.width() * 2;
69
+        let new_height = self.texture.height() * 2;
70
+
71
+        if new_width <= max_size && new_height <= max_size {
72
+            self.texture.resize(new_width, new_height);
73
+
74
+            // remove old entries, initialize texture
75
+            self.init();
76
+
77
+            info!("new atlas size {}x{}", new_width, new_height);
78
+
79
+            Ok(())
80
+        } else {
81
+            Err(())
47 82
         }
48 83
     }
49 84