From 1bcfc9a5cce0b30c3ad9983e407c06e237b491f3 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Fri, 10 Jan 2020 14:39:29 +0100
Subject: Implemented a texture atlas for images and svgs.

---
 native/src/widget/image.rs     |   2 +-
 wgpu/Cargo.toml                |   1 +
 wgpu/src/image.rs              | 177 ++++++++++++++++----------
 wgpu/src/image/raster.rs       | 283 +++++++++++++++++++++++++++--------------
 wgpu/src/image/vector.rs       | 198 +++++++++++++++++++++-------
 wgpu/src/shader/image.vert     |  26 ++++
 wgpu/src/shader/image.vert.spv | Bin 2136 -> 2324 bytes
 7 files changed, 476 insertions(+), 211 deletions(-)
 create mode 100644 wgpu/src/shader/image.vert

diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs
index 200401f9..9b92c7f1 100644
--- a/native/src/widget/image.rs
+++ b/native/src/widget/image.rs
@@ -18,7 +18,7 @@ use std::{
 /// ```
 ///
 /// <img src="https://github.com/hecrj/iced/blob/9712b319bb7a32848001b96bd84977430f14b623/examples/resources/ferris.png?raw=true" width="300">
-#[derive(Debug)]
+#[derive(Debug, Hash)]
 pub struct Image {
     handle: Handle,
     width: Length,
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 887c2d21..56839cf0 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -21,6 +21,7 @@ raw-window-handle = "0.3"
 glam = "0.8"
 font-kit = "0.4"
 log = "0.4"
+guillotiere = "0.4"
 
 [dependencies.image]
 version = "0.22"
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index f7ed67c3..65780886 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -3,13 +3,16 @@ mod raster;
 #[cfg(feature = "svg")]
 mod vector;
 
+#[cfg(feature = "image")]
+use crate::image::raster::Memory;
+
 use crate::Transformation;
 use iced_native::{image, svg, Rectangle};
 
 use std::mem;
 
 #[cfg(any(feature = "image", feature = "svg"))]
-use std::cell::RefCell;
+use std::{cell::RefCell, collections::HashMap};
 
 #[derive(Debug)]
 pub struct Pipeline {
@@ -174,6 +177,16 @@ impl Pipeline {
                                 format: wgpu::VertexFormat::Float2,
                                 offset: 4 * 2,
                             },
+                            wgpu::VertexAttributeDescriptor {
+                                shader_location: 3,
+                                format: wgpu::VertexFormat::Float2,
+                                offset: 4 * 4,
+                            },
+                            wgpu::VertexAttributeDescriptor {
+                                shader_location: 4,
+                                format: wgpu::VertexFormat::Float2,
+                                offset: 4 * 6,
+                            },
                         ],
                     },
                 ],
@@ -197,9 +210,10 @@ impl Pipeline {
 
         Pipeline {
             #[cfg(feature = "image")]
-            raster_cache: RefCell::new(raster::Cache::new()),
+            raster_cache: RefCell::new(raster::Cache::new(&device)),
+
             #[cfg(feature = "svg")]
-            vector_cache: RefCell::new(vector::Cache::new()),
+            vector_cache: RefCell::new(vector::Cache::new(&device)),
 
             pipeline,
             uniforms: uniforms_buffer,
@@ -251,50 +265,72 @@ impl Pipeline {
             std::mem::size_of::<Uniforms>() as u64,
         );
 
-        // TODO: Batch draw calls using a texture atlas
-        // Guillotière[1] by @nical can help us a lot here.
-        //
-        // [1]: https://github.com/nical/guillotiere
-        for image in instances {
-            let uploaded_texture = match &image.handle {
+        #[cfg(any(feature = "image", feature = "svg"))]
+        let mut recs = HashMap::new();
+
+        for (index, image) in instances.iter().enumerate() {
+            match &image.handle {
                 Handle::Raster(_handle) => {
                     #[cfg(feature = "image")]
                     {
-                        let mut cache = self.raster_cache.borrow_mut();
-                        let memory = cache.load(&_handle);
+                        let mut raster_cache = self.raster_cache.borrow_mut();
 
-                        memory.upload(device, encoder, &self.texture_layout)
-                    }
+                        if let Memory::Device(allocation) = raster_cache.upload(
+                            _handle,
+                            device,
+                            encoder)
+                        {
+                            let rec = allocation.rectangle;
 
-                    #[cfg(not(feature = "image"))]
-                    None
+                            let _ = recs.insert(index, rec);
+                        }
+                    }
                 }
                 Handle::Vector(_handle) => {
                     #[cfg(feature = "svg")]
                     {
-                        let mut cache = self.vector_cache.borrow_mut();
+                        let mut vector_cache = self.vector_cache.borrow_mut();
 
-                        cache.upload(
+                        if let Some(allocation) = vector_cache.upload(
                             _handle,
                             image.scale,
                             _scale,
                             device,
                             encoder,
-                            &self.texture_layout,
-                        )
-                    }
+                        ) {
+                            let rec = allocation.rectangle;
 
-                    #[cfg(not(feature = "svg"))]
-                    None
+                            let _ = recs.insert(index, rec);
+                        }
+                    }
                 }
-            };
+            }
+        }
+
+        #[cfg(feature = "image")]
+        let raster_atlas = self.raster_cache.borrow().atlas(device, &self.texture_layout);
+
+        #[cfg(feature = "svg")]
+        let vector_atlas = self.vector_cache.borrow().atlas(device, &self.texture_layout);
+
+        #[cfg(any(feature = "image", feature = "svg"))]
+        for (index, image) in instances.iter().enumerate() {
+            if let Some(rec) = recs.get(&index) {
+                let atlas_size = match image.handle {
+                    #[cfg(feature = "image")]
+                    Handle::Raster(_) => self.raster_cache.borrow().atlas_size(),
+                    #[cfg(feature = "svg")]
+                    Handle::Vector(_) => self.vector_cache.borrow().atlas_size(),
+                    _ => guillotiere::Size::new(0, 0)
+                };
 
-            if let Some(texture) = uploaded_texture {
                 let instance_buffer = device
                     .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
                     .fill_from_slice(&[Instance {
                         _position: image.position,
                         _scale: image.scale,
+                        _position_in_atlas: [rec.min.x as f32 / atlas_size.width as f32, rec.min.y as f32 / atlas_size.height as f32],
+                        _scale_in_atlas: [rec.size().width as f32 / atlas_size.width as f32, rec.size().height as f32 / atlas_size.height as f32]
                     }]);
 
                 encoder.copy_buffer_to_buffer(
@@ -305,48 +341,57 @@ impl Pipeline {
                     mem::size_of::<Instance>() as u64,
                 );
 
-                {
-                    let mut render_pass = encoder.begin_render_pass(
-                        &wgpu::RenderPassDescriptor {
-                            color_attachments: &[
-                                wgpu::RenderPassColorAttachmentDescriptor {
-                                    attachment: target,
-                                    resolve_target: None,
-                                    load_op: wgpu::LoadOp::Load,
-                                    store_op: wgpu::StoreOp::Store,
-                                    clear_color: wgpu::Color {
-                                        r: 0.0,
-                                        g: 0.0,
-                                        b: 0.0,
-                                        a: 0.0,
-                                    },
+                let texture = match &image.handle {
+                    #[cfg(feature = "image")]
+                    Handle::Raster(_) => &raster_atlas,
+                    #[cfg(feature = "svg")]
+                    Handle::Vector(_) => &vector_atlas,
+                    #[cfg(feature = "image")]
+                    _ => &raster_atlas,
+                    #[cfg(feature = "svg")]
+                    _ => &vector_atlas,
+                };
+
+                let mut render_pass = encoder.begin_render_pass(
+                    &wgpu::RenderPassDescriptor {
+                        color_attachments: &[
+                            wgpu::RenderPassColorAttachmentDescriptor {
+                                attachment: target,
+                                resolve_target: None,
+                                load_op: wgpu::LoadOp::Load,
+                                store_op: wgpu::StoreOp::Store,
+                                clear_color: wgpu::Color {
+                                    r: 0.0,
+                                    g: 0.0,
+                                    b: 0.0,
+                                    a: 0.0,
                                 },
-                            ],
-                            depth_stencil_attachment: None,
-                        },
-                    );
-
-                    render_pass.set_pipeline(&self.pipeline);
-                    render_pass.set_bind_group(0, &self.constants, &[]);
-                    render_pass.set_bind_group(1, &texture, &[]);
-                    render_pass.set_index_buffer(&self.indices, 0);
-                    render_pass.set_vertex_buffers(
-                        0,
-                        &[(&self.vertices, 0), (&self.instances, 0)],
-                    );
-                    render_pass.set_scissor_rect(
-                        bounds.x,
-                        bounds.y,
-                        bounds.width,
-                        bounds.height,
-                    );
-
-                    render_pass.draw_indexed(
-                        0..QUAD_INDICES.len() as u32,
-                        0,
-                        0..1 as u32,
-                    );
-                }
+                            },
+                        ],
+                        depth_stencil_attachment: None,
+                    },
+                );
+
+                render_pass.set_pipeline(&self.pipeline);
+                render_pass.set_bind_group(0, &self.constants, &[]);
+                render_pass.set_bind_group(1, &texture, &[]);
+                render_pass.set_index_buffer(&self.indices, 0);
+                render_pass.set_vertex_buffers(
+                    0,
+                    &[(&self.vertices, 0), (&self.instances, 0)],
+                );
+                render_pass.set_scissor_rect(
+                    bounds.x,
+                    bounds.y,
+                    bounds.width,
+                    bounds.height,
+                );
+
+                render_pass.draw_indexed(
+                    0..QUAD_INDICES.len() as u32,
+                    0,
+                    0..1 as u32,
+                );
             }
         }
     }
@@ -399,6 +444,8 @@ const QUAD_VERTS: [Vertex; 4] = [
 struct Instance {
     _position: [f32; 2],
     _scale: [f32; 2],
+    _position_in_atlas: [f32; 2],
+    _scale_in_atlas: [f32; 2],
 }
 
 #[repr(C)]
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index fa107879..0418bc0b 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,17 +1,13 @@
 use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
-    rc::Rc,
+    fmt,
 };
+use guillotiere::{Allocation, AtlasAllocator, Size};
 
-#[derive(Debug)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device {
-        bind_group: Rc<wgpu::BindGroup>,
-        width: u32,
-        height: u32,
-    },
+    Device(Allocation),
     NotFound,
     Invalid,
 }
@@ -20,108 +16,59 @@ impl Memory {
     pub fn dimensions(&self) -> (u32, u32) {
         match self {
             Memory::Host(image) => image.dimensions(),
-            Memory::Device { width, height, .. } => (*width, *height),
+            Memory::Device(allocation) => {
+                let size = &allocation.rectangle.size();
+                (size.width as u32, size.height as u32)
+            },
             Memory::NotFound => (1, 1),
             Memory::Invalid => (1, 1),
         }
     }
-
-    pub fn upload(
-        &mut self,
-        device: &wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-        texture_layout: &wgpu::BindGroupLayout,
-    ) -> Option<Rc<wgpu::BindGroup>> {
-        match self {
-            Memory::Host(image) => {
-                let (width, height) = image.dimensions();
-
-                let extent = wgpu::Extent3d {
-                    width,
-                    height,
-                    depth: 1,
-                };
-
-                let texture = device.create_texture(&wgpu::TextureDescriptor {
-                    size: extent,
-                    array_layer_count: 1,
-                    mip_level_count: 1,
-                    sample_count: 1,
-                    dimension: wgpu::TextureDimension::D2,
-                    format: wgpu::TextureFormat::Bgra8UnormSrgb,
-                    usage: wgpu::TextureUsage::COPY_DST
-                        | wgpu::TextureUsage::SAMPLED,
-                });
-
-                let temp_buf = {
-                    let flat_samples = image.as_flat_samples();
-                    let slice = flat_samples.as_slice();
-
-                    device
-                        .create_buffer_mapped(
-                            slice.len(),
-                            wgpu::BufferUsage::COPY_SRC,
-                        )
-                        .fill_from_slice(slice)
-                };
-
-                encoder.copy_buffer_to_texture(
-                    wgpu::BufferCopyView {
-                        buffer: &temp_buf,
-                        offset: 0,
-                        row_pitch: 4 * width as u32,
-                        image_height: height as u32,
-                    },
-                    wgpu::TextureCopyView {
-                        texture: &texture,
-                        array_layer: 0,
-                        mip_level: 0,
-                        origin: wgpu::Origin3d {
-                            x: 0.0,
-                            y: 0.0,
-                            z: 0.0,
-                        },
-                    },
-                    extent,
-                );
-
-                let bind_group =
-                    device.create_bind_group(&wgpu::BindGroupDescriptor {
-                        layout: texture_layout,
-                        bindings: &[wgpu::Binding {
-                            binding: 0,
-                            resource: wgpu::BindingResource::TextureView(
-                                &texture.create_default_view(),
-                            ),
-                        }],
-                    });
-
-                let bind_group = Rc::new(bind_group);
-
-                *self = Memory::Device {
-                    bind_group: bind_group.clone(),
-                    width,
-                    height,
-                };
-
-                Some(bind_group)
-            }
-            Memory::Device { bind_group, .. } => Some(bind_group.clone()),
-            Memory::NotFound => None,
-            Memory::Invalid => None,
-        }
-    }
 }
 
-#[derive(Debug)]
 pub struct Cache {
+    allocator: AtlasAllocator,
+    atlas: wgpu::Texture,
     map: HashMap<u64, Memory>,
     hits: HashSet<u64>,
 }
 
+impl fmt::Debug for Cache {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("Vector Cache")
+            .field("allocator", &String::from("AtlasAllocator"))
+            .field("atlas", &self.atlas)
+            .field("map", &String::from("HashMap<u64, Memory>"))
+            .field("hits", &self.hits)
+            .finish()
+    }
+}
+
 impl Cache {
-    pub fn new() -> Self {
+    pub fn new(device: &wgpu::Device) -> Self {
+        let (width, height) = (1000, 1000);
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        let atlas = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: 1,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
         Self {
+            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
+            atlas,
             map: HashMap::new(),
             hits: HashSet::new(),
         }
@@ -153,9 +100,153 @@ impl Cache {
         self.get(handle).unwrap()
     }
 
+    pub fn atlas_size(&self) -> guillotiere::Size {
+        self.allocator.size()
+    }
+
+    pub fn upload(
+        &mut self,
+        handle: &image::Handle,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) -> &Memory {
+        let _ = self.load(handle);
+
+        let memory = self.map.get_mut(&handle.id()).unwrap();
+
+        if let Memory::Host(image) = memory {
+            let (width, height) = image.dimensions();
+            let size = Size::new(width as i32, height as i32);
+
+            let old_atlas_size = self.allocator.size();
+            let allocation;
+
+            loop {
+                if let Some(a) = self.allocator.allocate(size) {
+                    allocation = a;
+                    break;
+                }
+
+                self.allocator.grow(self.allocator.size() * 2);
+            }
+
+            let new_atlas_size = self.allocator.size();
+
+            if new_atlas_size != old_atlas_size {
+                let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
+                    size: wgpu::Extent3d {
+                        width: new_atlas_size.width as u32,
+                        height: new_atlas_size.height as u32,
+                        depth: 1,
+                    },
+                    array_layer_count: 1,
+                    mip_level_count: 1,
+                    sample_count: 1,
+                    dimension: wgpu::TextureDimension::D2,
+                    format: wgpu::TextureFormat::Bgra8UnormSrgb,
+                    usage: wgpu::TextureUsage::COPY_DST
+                        | wgpu::TextureUsage::COPY_SRC
+                        | wgpu::TextureUsage::SAMPLED,
+                });
+
+                encoder.copy_texture_to_texture(
+                    wgpu::TextureCopyView {
+                        texture: &self.atlas,
+                        array_layer: 0,
+                        mip_level: 0,
+                        origin: wgpu::Origin3d {
+                            x: 0.0,
+                            y: 0.0,
+                            z: 0.0,
+                        },
+                    },
+                    wgpu::TextureCopyView {
+                        texture: &new_atlas,
+                        array_layer: 0,
+                        mip_level: 0,
+                        origin: wgpu::Origin3d {
+                            x: 0.0,
+                            y: 0.0,
+                            z: 0.0,
+                        },
+                    },
+                    wgpu::Extent3d {
+                        width: old_atlas_size.width as u32,
+                        height: old_atlas_size.height as u32,
+                        depth: 1,
+                    }
+                );
+
+                self.atlas = new_atlas;
+            }
+
+            let extent = wgpu::Extent3d {
+                width,
+                height,
+                depth: 1,
+            };
+
+            let temp_buf = {
+                let flat_samples = image.as_flat_samples();
+                let slice = flat_samples.as_slice();
+
+                device
+                    .create_buffer_mapped(
+                        slice.len(),
+                        wgpu::BufferUsage::COPY_SRC,
+                    )
+                    .fill_from_slice(slice)
+            };
+
+            encoder.copy_buffer_to_texture(
+                wgpu::BufferCopyView {
+                    buffer: &temp_buf,
+                    offset: 0,
+                    row_pitch: 4 * width,
+                    image_height: height,
+                },
+                wgpu::TextureCopyView {
+                    texture: &self.atlas,
+                    array_layer: 0,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: allocation.rectangle.min.x as f32,
+                        y: allocation.rectangle.min.y as f32,
+                        z: 0.0,
+                    },
+                },
+                extent,
+            );
+
+            *memory = Memory::Device(allocation);
+        }
+
+        memory
+    }
+
+    pub fn atlas(&self, device: &wgpu::Device, texture_layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
+        device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: texture_layout,
+            bindings: &[wgpu::Binding {
+                binding: 0,
+                resource: wgpu::BindingResource::TextureView(
+                    &self.atlas.create_default_view(),
+                ),
+            }],
+        })
+    }
+
     pub fn trim(&mut self) {
         let hits = &self.hits;
 
+        for (id, mem) in &mut self.map {
+            if let Memory::Device(allocation) = mem {
+                if !hits.contains(&id) {
+                    self.allocator.deallocate(allocation.id);
+                }
+            }
+        }
+
         self.map.retain(|k, _| hits.contains(k));
         self.hits.clear();
     }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 713978f5..1a9352f2 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,8 +1,9 @@
 use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
-    rc::Rc,
+    fmt,
 };
+use guillotiere::{Allocation, AtlasAllocator, Size};
 
 pub enum Svg {
     Loaded { tree: resvg::usvg::Tree },
@@ -22,27 +23,63 @@ impl Svg {
     }
 }
 
-impl std::fmt::Debug for Svg {
+impl fmt::Debug for Svg {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "Svg")
     }
 }
 
-#[derive(Debug)]
 pub struct Cache {
+    allocator: AtlasAllocator,
+    atlas: wgpu::Texture,
     svgs: HashMap<u64, Svg>,
-    rasterized: HashMap<(u64, u32, u32), Rc<wgpu::BindGroup>>,
+    rasterized: HashMap<(u64, u32, u32), Allocation>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
 }
 
+impl fmt::Debug for Cache {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("Vector Cache")
+            .field("allocator", &String::from("AtlasAllocator"))
+            .field("atlas", &self.atlas)
+            .field("svgs", &self.svgs)
+            .field("rasterized", &String::from("HashMap<(u64, u32, u32), Allocation>"))
+            .field("svg_hits", &self.svg_hits)
+            .field("rasterized_hits", &self.rasterized_hits)
+            .finish()
+    }
+}
+
 impl Cache {
-    pub fn new() -> Self {
+    pub fn new(device: &wgpu::Device) -> Self {
+        let (width, height) = (512, 512);
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        let atlas = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: 1,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
         Self {
             svgs: HashMap::new(),
             rasterized: HashMap::new(),
             svg_hits: HashSet::new(),
             rasterized_hits: HashSet::new(),
+            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
+            atlas,
         }
     }
 
@@ -62,6 +99,10 @@ impl Cache {
         self.svgs.get(&handle.id()).unwrap()
     }
 
+    pub fn atlas_size(&self) -> guillotiere::Size {
+        self.allocator.size()
+    }
+
     pub fn upload(
         &mut self,
         handle: &svg::Handle,
@@ -69,8 +110,7 @@ impl Cache {
         scale: f32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        texture_layout: &wgpu::BindGroupLayout,
-    ) -> Option<Rc<wgpu::BindGroup>> {
+    ) -> Option<&Allocation> {
         let id = handle.id();
 
         let (width, height) = (
@@ -82,36 +122,88 @@ impl Cache {
         // We currently rerasterize the SVG when its size changes. This is slow
         // as heck. A GPU rasterizer like `pathfinder` may perform better.
         // It would be cool to be able to smooth resize the `svg` example.
-        if let Some(bind_group) = self.rasterized.get(&(id, width, height)) {
+        if self.rasterized.get(&(id, width, height)).is_some() {
             let _ = self.svg_hits.insert(id);
             let _ = self.rasterized_hits.insert((id, width, height));
 
-            return Some(bind_group.clone());
+            return self.rasterized.get(&(id, width, height));
         }
 
-        match self.load(handle) {
+        let _ = self.load(handle);
+
+        match self.svgs.get(&handle.id()).unwrap() {
             Svg::Loaded { tree } => {
                 if width == 0 || height == 0 {
                     return None;
                 }
 
-                let extent = wgpu::Extent3d {
-                    width,
-                    height,
-                    depth: 1,
-                };
+                let size = Size::new(width as i32, height as i32);
+                let old_atlas_size = self.allocator.size();
+                let allocation;
+
+                loop {
+                    if let Some(a) = self.allocator.allocate(size) {
+                        allocation = a;
+                        break;
+                    }
 
-                let texture = device.create_texture(&wgpu::TextureDescriptor {
-                    size: extent,
-                    array_layer_count: 1,
-                    mip_level_count: 1,
-                    sample_count: 1,
-                    dimension: wgpu::TextureDimension::D2,
-                    format: wgpu::TextureFormat::Bgra8UnormSrgb,
-                    usage: wgpu::TextureUsage::COPY_DST
-                        | wgpu::TextureUsage::SAMPLED,
-                });
+                    self.allocator.grow(self.allocator.size() * 2);
+                }
 
+                let new_atlas_size = self.allocator.size();
+
+                if new_atlas_size != old_atlas_size {
+                    let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
+                        size: wgpu::Extent3d {
+                            width: new_atlas_size.width as u32,
+                            height: new_atlas_size.height as u32,
+                            depth: 1,
+                        },
+                        array_layer_count: 1,
+                        mip_level_count: 1,
+                        sample_count: 1,
+                        dimension: wgpu::TextureDimension::D2,
+                        format: wgpu::TextureFormat::Bgra8UnormSrgb,
+                        usage: wgpu::TextureUsage::COPY_DST
+                            | wgpu::TextureUsage::COPY_SRC
+                            | wgpu::TextureUsage::SAMPLED,
+                    });
+
+                    encoder.copy_texture_to_texture(
+                        wgpu::TextureCopyView {
+                            texture: &self.atlas,
+                            array_layer: 0,
+                            mip_level: 0,
+                            origin: wgpu::Origin3d {
+                                x: 0.0,
+                                y: 0.0,
+                                z: 0.0,
+                            },
+                        },
+                        wgpu::TextureCopyView {
+                            texture: &new_atlas,
+                            array_layer: 0,
+                            mip_level: 0,
+                            origin: wgpu::Origin3d {
+                                x: 0.0,
+                                y: 0.0,
+                                z: 0.0,
+                            },
+                        },
+                        wgpu::Extent3d {
+                            width: old_atlas_size.width as u32,
+                            height: old_atlas_size.height as u32,
+                            depth: 1,
+                        }
+                    );
+
+                    self.atlas = new_atlas;
+                }
+
+                // TODO: Optimize!
+                // We currently rerasterize the SVG when its size changes. This is slow
+                // as heck. A GPU rasterizer like `pathfinder` may perform better.
+                // It would be cool to be able to smooth resize the `svg` example.
                 let temp_buf = {
                     let screen_size =
                         resvg::ScreenSize::new(width, height).unwrap();
@@ -122,7 +214,7 @@ impl Cache {
                     );
 
                     resvg::backend_raqote::render_to_canvas(
-                        &tree,
+                        tree,
                         &resvg::Options::default(),
                         screen_size,
                         &mut canvas,
@@ -146,48 +238,56 @@ impl Cache {
                         image_height: height as u32,
                     },
                     wgpu::TextureCopyView {
-                        texture: &texture,
+                        texture: &self.atlas,
                         array_layer: 0,
                         mip_level: 0,
                         origin: wgpu::Origin3d {
-                            x: 0.0,
-                            y: 0.0,
+                            x: allocation.rectangle.min.x as f32,
+                            y: allocation.rectangle.min.y as f32,
                             z: 0.0,
                         },
                     },
-                    extent,
+                    wgpu::Extent3d {
+                        width,
+                        height,
+                        depth: 1,
+                    },
                 );
 
-                let bind_group =
-                    device.create_bind_group(&wgpu::BindGroupDescriptor {
-                        layout: texture_layout,
-                        bindings: &[wgpu::Binding {
-                            binding: 0,
-                            resource: wgpu::BindingResource::TextureView(
-                                &texture.create_default_view(),
-                            ),
-                        }],
-                    });
-
-                let bind_group = Rc::new(bind_group);
-
-                let _ = self
-                    .rasterized
-                    .insert((id, width, height), bind_group.clone());
-
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert((id, width, height));
+                let _ = self
+                    .rasterized
+                    .insert((id, width, height), allocation);
 
-                Some(bind_group)
+                self.rasterized.get(&(id, width, height))
             }
-            Svg::NotFound => None,
+            Svg::NotFound => None
         }
     }
 
+    pub fn atlas(&self, device: &wgpu::Device, texture_layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
+        device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: texture_layout,
+            bindings: &[wgpu::Binding {
+                binding: 0,
+                resource: wgpu::BindingResource::TextureView(
+                    &self.atlas.create_default_view(),
+                ),
+            }],
+        })
+    }
+
     pub fn trim(&mut self) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
+        for (k, alloc) in &mut self.rasterized {
+            if !rasterized_hits.contains(&k) {
+                self.allocator.deallocate(alloc.id);
+            }
+        }
+
         self.svgs.retain(|k, _| svg_hits.contains(k));
         self.rasterized.retain(|k, _| rasterized_hits.contains(k));
         self.svg_hits.clear();
diff --git a/wgpu/src/shader/image.vert b/wgpu/src/shader/image.vert
new file mode 100644
index 00000000..953840d2
--- /dev/null
+++ b/wgpu/src/shader/image.vert
@@ -0,0 +1,26 @@
+#version 450
+
+layout(location = 0) in vec2 v_Pos;
+layout(location = 1) in vec2 i_Pos;
+layout(location = 2) in vec2 i_Scale;
+layout(location = 3) in vec2 i_Atlas_Pos;
+layout(location = 4) in vec2 i_Atlas_Scale;
+
+layout (set = 0, binding = 0) uniform Globals {
+    mat4 u_Transform;
+};
+
+layout(location = 0) out vec2 o_Uv;
+
+void main() {
+    o_Uv = v_Pos * i_Atlas_Scale + i_Atlas_Pos;
+
+    mat4 i_Transform = mat4(
+        vec4(i_Scale.x, 0.0, 0.0, 0.0),
+        vec4(0.0, i_Scale.y, 0.0, 0.0),
+        vec4(0.0, 0.0, 1.0, 0.0),
+        vec4(i_Pos, 0.0, 1.0)
+    );
+
+    gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0);
+}
diff --git a/wgpu/src/shader/image.vert.spv b/wgpu/src/shader/image.vert.spv
index 9ba702bc..da76eca0 100644
Binary files a/wgpu/src/shader/image.vert.spv and b/wgpu/src/shader/image.vert.spv differ
-- 
cgit 


From 743637ebda8c3e4ba30f41e755ee1079ee66a86c Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Fri, 10 Jan 2020 16:07:09 +0100
Subject: Merged image and svg texture atlases into one owned by the image
 pipeline.

---
 wgpu/Cargo.toml          |  1 +
 wgpu/src/image.rs        | 92 +++++++++++++++++++++++++++++++-----------------
 wgpu/src/image/raster.rs | 79 +++++++++--------------------------------
 wgpu/src/image/vector.rs | 78 ++++++++--------------------------------
 4 files changed, 91 insertions(+), 159 deletions(-)

diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 56839cf0..f8b5bb8c 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -22,6 +22,7 @@ glam = "0.8"
 font-kit = "0.4"
 log = "0.4"
 guillotiere = "0.4"
+debug_stub_derive = "0.3"
 
 [dependencies.image]
 version = "0.22"
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 65780886..aaecd492 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -14,7 +14,10 @@ use std::mem;
 #[cfg(any(feature = "image", feature = "svg"))]
 use std::{cell::RefCell, collections::HashMap};
 
-#[derive(Debug)]
+use guillotiere::{AtlasAllocator, Size};
+use debug_stub_derive::*;
+
+#[derive(DebugStub)]
 pub struct Pipeline {
     #[cfg(feature = "image")]
     raster_cache: RefCell<raster::Cache>,
@@ -28,6 +31,9 @@ pub struct Pipeline {
     instances: wgpu::Buffer,
     constants: wgpu::BindGroup,
     texture_layout: wgpu::BindGroupLayout,
+    #[debug_stub="ReplacementValue"]
+    allocator: AtlasAllocator,
+    atlas: wgpu::Texture,
 }
 
 impl Pipeline {
@@ -208,12 +214,32 @@ impl Pipeline {
             usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
         });
 
+        let (width, height) = (512, 512);
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        let atlas = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: 1,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
         Pipeline {
             #[cfg(feature = "image")]
-            raster_cache: RefCell::new(raster::Cache::new(&device)),
+            raster_cache: RefCell::new(raster::Cache::new()),
 
             #[cfg(feature = "svg")]
-            vector_cache: RefCell::new(vector::Cache::new(&device)),
+            vector_cache: RefCell::new(vector::Cache::new()),
 
             pipeline,
             uniforms: uniforms_buffer,
@@ -222,6 +248,8 @@ impl Pipeline {
             instances,
             constants: constant_bind_group,
             texture_layout,
+            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
+            atlas,
         }
     }
 
@@ -276,10 +304,12 @@ impl Pipeline {
                         let mut raster_cache = self.raster_cache.borrow_mut();
 
                         if let Memory::Device(allocation) = raster_cache.upload(
-                            _handle,
+                            handle,
                             device,
-                            encoder)
-                        {
+                            encoder,
+                            &mut self.allocator,
+                            &mut self.atlas
+                        ) {
                             let rec = allocation.rectangle;
 
                             let _ = recs.insert(index, rec);
@@ -291,12 +321,15 @@ impl Pipeline {
                     {
                         let mut vector_cache = self.vector_cache.borrow_mut();
 
+                        // Upload rasterized svg to texture atlas
                         if let Some(allocation) = vector_cache.upload(
                             _handle,
                             image.scale,
                             _scale,
                             device,
                             encoder,
+                            &mut self.allocator,
+                            &mut self.atlas,
                         ) {
                             let rec = allocation.rectangle;
 
@@ -307,30 +340,34 @@ impl Pipeline {
             }
         }
 
-        #[cfg(feature = "image")]
-        let raster_atlas = self.raster_cache.borrow().atlas(device, &self.texture_layout);
-
-        #[cfg(feature = "svg")]
-        let vector_atlas = self.vector_cache.borrow().atlas(device, &self.texture_layout);
+        let atlas_width = self.allocator.size().width as f32;
+        let atlas_height = self.allocator.size().height as f32;
+
+        let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: &self.texture_layout,
+            bindings: &[wgpu::Binding {
+                binding: 0,
+                resource: wgpu::BindingResource::TextureView(
+                    &self.atlas.create_default_view(),
+                ),
+            }],
+        });
 
         #[cfg(any(feature = "image", feature = "svg"))]
         for (index, image) in instances.iter().enumerate() {
             if let Some(rec) = recs.get(&index) {
-                let atlas_size = match image.handle {
-                    #[cfg(feature = "image")]
-                    Handle::Raster(_) => self.raster_cache.borrow().atlas_size(),
-                    #[cfg(feature = "svg")]
-                    Handle::Vector(_) => self.vector_cache.borrow().atlas_size(),
-                    _ => guillotiere::Size::new(0, 0)
-                };
+                let x = rec.min.x as f32 / atlas_width;
+                let y = rec.min.y as f32 / atlas_height;
+                let w = (rec.size().width - 1) as f32 / atlas_width;
+                let h = (rec.size().height - 1) as f32 / atlas_height;
 
                 let instance_buffer = device
                     .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
                     .fill_from_slice(&[Instance {
                         _position: image.position,
                         _scale: image.scale,
-                        _position_in_atlas: [rec.min.x as f32 / atlas_size.width as f32, rec.min.y as f32 / atlas_size.height as f32],
-                        _scale_in_atlas: [rec.size().width as f32 / atlas_size.width as f32, rec.size().height as f32 / atlas_size.height as f32]
+                        _position_in_atlas: [x, y],
+                        _scale_in_atlas: [w, h]
                     }]);
 
                 encoder.copy_buffer_to_buffer(
@@ -341,17 +378,6 @@ impl Pipeline {
                     mem::size_of::<Instance>() as u64,
                 );
 
-                let texture = match &image.handle {
-                    #[cfg(feature = "image")]
-                    Handle::Raster(_) => &raster_atlas,
-                    #[cfg(feature = "svg")]
-                    Handle::Vector(_) => &vector_atlas,
-                    #[cfg(feature = "image")]
-                    _ => &raster_atlas,
-                    #[cfg(feature = "svg")]
-                    _ => &vector_atlas,
-                };
-
                 let mut render_pass = encoder.begin_render_pass(
                     &wgpu::RenderPassDescriptor {
                         color_attachments: &[
@@ -398,10 +424,10 @@ impl Pipeline {
 
     pub fn trim_cache(&mut self) {
         #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim();
+        self.raster_cache.borrow_mut().trim(&mut self.allocator);
 
         #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim();
+        self.vector_cache.borrow_mut().trim(&mut self.allocator);
     }
 }
 
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 0418bc0b..651ec078 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,13 +1,14 @@
 use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
-    fmt,
 };
 use guillotiere::{Allocation, AtlasAllocator, Size};
+use debug_stub_derive::*;
 
+#[derive(DebugStub)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device(Allocation),
+    Device(#[debug_stub="ReplacementValue"]Allocation),
     NotFound,
     Invalid,
 }
@@ -26,49 +27,15 @@ impl Memory {
     }
 }
 
+#[derive(Debug)]
 pub struct Cache {
-    allocator: AtlasAllocator,
-    atlas: wgpu::Texture,
     map: HashMap<u64, Memory>,
     hits: HashSet<u64>,
 }
 
-impl fmt::Debug for Cache {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt.debug_struct("Vector Cache")
-            .field("allocator", &String::from("AtlasAllocator"))
-            .field("atlas", &self.atlas)
-            .field("map", &String::from("HashMap<u64, Memory>"))
-            .field("hits", &self.hits)
-            .finish()
-    }
-}
-
 impl Cache {
-    pub fn new(device: &wgpu::Device) -> Self {
-        let (width, height) = (1000, 1000);
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        let atlas = device.create_texture(&wgpu::TextureDescriptor {
-            size: extent,
-            array_layer_count: 1,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
+    pub fn new() -> Self {
         Self {
-            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
-            atlas,
             map: HashMap::new(),
             hits: HashSet::new(),
         }
@@ -100,15 +67,13 @@ impl Cache {
         self.get(handle).unwrap()
     }
 
-    pub fn atlas_size(&self) -> guillotiere::Size {
-        self.allocator.size()
-    }
-
     pub fn upload(
         &mut self,
         handle: &image::Handle,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
+        allocator: &mut AtlasAllocator,
+        atlas: &mut wgpu::Texture,
     ) -> &Memory {
         let _ = self.load(handle);
 
@@ -118,19 +83,19 @@ impl Cache {
             let (width, height) = image.dimensions();
             let size = Size::new(width as i32, height as i32);
 
-            let old_atlas_size = self.allocator.size();
+            let old_atlas_size = allocator.size();
             let allocation;
 
             loop {
-                if let Some(a) = self.allocator.allocate(size) {
+                if let Some(a) = allocator.allocate(size) {
                     allocation = a;
                     break;
                 }
 
-                self.allocator.grow(self.allocator.size() * 2);
+                allocator.grow(allocator.size() * 2);
             }
 
-            let new_atlas_size = self.allocator.size();
+            let new_atlas_size = allocator.size();
 
             if new_atlas_size != old_atlas_size {
                 let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
@@ -151,7 +116,7 @@ impl Cache {
 
                 encoder.copy_texture_to_texture(
                     wgpu::TextureCopyView {
-                        texture: &self.atlas,
+                        texture: atlas,
                         array_layer: 0,
                         mip_level: 0,
                         origin: wgpu::Origin3d {
@@ -177,7 +142,7 @@ impl Cache {
                     }
                 );
 
-                self.atlas = new_atlas;
+                *atlas = new_atlas;
             }
 
             let extent = wgpu::Extent3d {
@@ -206,7 +171,7 @@ impl Cache {
                     image_height: height,
                 },
                 wgpu::TextureCopyView {
-                    texture: &self.atlas,
+                    texture: atlas,
                     array_layer: 0,
                     mip_level: 0,
                     origin: wgpu::Origin3d {
@@ -224,25 +189,13 @@ impl Cache {
         memory
     }
 
-    pub fn atlas(&self, device: &wgpu::Device, texture_layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
-        device.create_bind_group(&wgpu::BindGroupDescriptor {
-            layout: texture_layout,
-            bindings: &[wgpu::Binding {
-                binding: 0,
-                resource: wgpu::BindingResource::TextureView(
-                    &self.atlas.create_default_view(),
-                ),
-            }],
-        })
-    }
-
-    pub fn trim(&mut self) {
+    pub fn trim(&mut self, allocator: &mut AtlasAllocator) {
         let hits = &self.hits;
 
         for (id, mem) in &mut self.map {
             if let Memory::Device(allocation) = mem {
                 if !hits.contains(&id) {
-                    self.allocator.deallocate(allocation.id);
+                    allocator.deallocate(allocation.id);
                 }
             }
         }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 1a9352f2..89477877 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -4,6 +4,7 @@ use std::{
     fmt,
 };
 use guillotiere::{Allocation, AtlasAllocator, Size};
+use debug_stub_derive::*;
 
 pub enum Svg {
     Loaded { tree: resvg::usvg::Tree },
@@ -29,57 +30,22 @@ impl fmt::Debug for Svg {
     }
 }
 
+#[derive(DebugStub)]
 pub struct Cache {
-    allocator: AtlasAllocator,
-    atlas: wgpu::Texture,
     svgs: HashMap<u64, Svg>,
+    #[debug_stub="ReplacementValue"]
     rasterized: HashMap<(u64, u32, u32), Allocation>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
 }
 
-impl fmt::Debug for Cache {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt.debug_struct("Vector Cache")
-            .field("allocator", &String::from("AtlasAllocator"))
-            .field("atlas", &self.atlas)
-            .field("svgs", &self.svgs)
-            .field("rasterized", &String::from("HashMap<(u64, u32, u32), Allocation>"))
-            .field("svg_hits", &self.svg_hits)
-            .field("rasterized_hits", &self.rasterized_hits)
-            .finish()
-    }
-}
-
 impl Cache {
-    pub fn new(device: &wgpu::Device) -> Self {
-        let (width, height) = (512, 512);
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        let atlas = device.create_texture(&wgpu::TextureDescriptor {
-            size: extent,
-            array_layer_count: 1,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
+    pub fn new() -> Self {
         Self {
             svgs: HashMap::new(),
             rasterized: HashMap::new(),
             svg_hits: HashSet::new(),
             rasterized_hits: HashSet::new(),
-            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
-            atlas,
         }
     }
 
@@ -99,10 +65,6 @@ impl Cache {
         self.svgs.get(&handle.id()).unwrap()
     }
 
-    pub fn atlas_size(&self) -> guillotiere::Size {
-        self.allocator.size()
-    }
-
     pub fn upload(
         &mut self,
         handle: &svg::Handle,
@@ -110,6 +72,8 @@ impl Cache {
         scale: f32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
+        allocator: &mut AtlasAllocator,
+        atlas: &mut wgpu::Texture,
     ) -> Option<&Allocation> {
         let id = handle.id();
 
@@ -138,19 +102,19 @@ impl Cache {
                 }
 
                 let size = Size::new(width as i32, height as i32);
-                let old_atlas_size = self.allocator.size();
+                let old_atlas_size = allocator.size();
                 let allocation;
 
                 loop {
-                    if let Some(a) = self.allocator.allocate(size) {
+                    if let Some(a) = allocator.allocate(size) {
                         allocation = a;
                         break;
                     }
 
-                    self.allocator.grow(self.allocator.size() * 2);
+                    allocator.grow(allocator.size() * 2);
                 }
 
-                let new_atlas_size = self.allocator.size();
+                let new_atlas_size = allocator.size();
 
                 if new_atlas_size != old_atlas_size {
                     let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
@@ -171,7 +135,7 @@ impl Cache {
 
                     encoder.copy_texture_to_texture(
                         wgpu::TextureCopyView {
-                            texture: &self.atlas,
+                            texture: atlas,
                             array_layer: 0,
                             mip_level: 0,
                             origin: wgpu::Origin3d {
@@ -197,7 +161,7 @@ impl Cache {
                         }
                     );
 
-                    self.atlas = new_atlas;
+                    *atlas = new_atlas;
                 }
 
                 // TODO: Optimize!
@@ -238,7 +202,7 @@ impl Cache {
                         image_height: height as u32,
                     },
                     wgpu::TextureCopyView {
-                        texture: &self.atlas,
+                        texture: atlas,
                         array_layer: 0,
                         mip_level: 0,
                         origin: wgpu::Origin3d {
@@ -266,25 +230,13 @@ impl Cache {
         }
     }
 
-    pub fn atlas(&self, device: &wgpu::Device, texture_layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
-        device.create_bind_group(&wgpu::BindGroupDescriptor {
-            layout: texture_layout,
-            bindings: &[wgpu::Binding {
-                binding: 0,
-                resource: wgpu::BindingResource::TextureView(
-                    &self.atlas.create_default_view(),
-                ),
-            }],
-        })
-    }
-
-    pub fn trim(&mut self) {
+    pub fn trim(&mut self, allocator: &mut AtlasAllocator) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
         for (k, alloc) in &mut self.rasterized {
             if !rasterized_hits.contains(&k) {
-                self.allocator.deallocate(alloc.id);
+                allocator.deallocate(alloc.id);
             }
         }
 
-- 
cgit 


From 82e0675c071eee6ee71a989f4c8fb9a9fc18fcfe Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Sat, 11 Jan 2020 21:50:42 +0100
Subject: Some small debug changes

---
 wgpu/src/image/raster.rs |  5 ++++-
 wgpu/src/image/vector.rs | 19 ++++++++-----------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 651ec078..33750cac 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -8,7 +8,10 @@ use debug_stub_derive::*;
 #[derive(DebugStub)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device(#[debug_stub="ReplacementValue"]Allocation),
+    Device(
+        #[debug_stub="ReplacementValue"]
+        Allocation
+    ),
     NotFound,
     Invalid,
 }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 89477877..2afe7d92 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,20 +1,23 @@
 use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
-    fmt,
 };
 use guillotiere::{Allocation, AtlasAllocator, Size};
 use debug_stub_derive::*;
 
+#[derive(DebugStub)]
 pub enum Svg {
-    Loaded { tree: resvg::usvg::Tree },
+    Loaded(
+        #[debug_stub="ReplacementValue"]
+        resvg::usvg::Tree
+    ),
     NotFound,
 }
 
 impl Svg {
     pub fn viewport_dimensions(&self) -> (u32, u32) {
         match self {
-            Svg::Loaded { tree } => {
+            Svg::Loaded(tree) => {
                 let size = tree.svg_node().size;
 
                 (size.width() as u32, size.height() as u32)
@@ -24,12 +27,6 @@ impl Svg {
     }
 }
 
-impl fmt::Debug for Svg {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "Svg")
-    }
-}
-
 #[derive(DebugStub)]
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
@@ -57,7 +54,7 @@ impl Cache {
         let opt = resvg::Options::default();
 
         let svg = match resvg::usvg::Tree::from_file(handle.path(), &opt.usvg) {
-            Ok(tree) => Svg::Loaded { tree },
+            Ok(tree) => Svg::Loaded(tree),
             Err(_) => Svg::NotFound,
         };
 
@@ -96,7 +93,7 @@ impl Cache {
         let _ = self.load(handle);
 
         match self.svgs.get(&handle.id()).unwrap() {
-            Svg::Loaded { tree } => {
+            Svg::Loaded(tree) => {
                 if width == 0 || height == 0 {
                     return None;
                 }
-- 
cgit 


From 8562a4c986ff48d478be794c8c4268047a9a57d7 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Sun, 12 Jan 2020 02:37:46 +0100
Subject: Fixed texture bleeding

---
 wgpu/src/image.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index aaecd492..59257399 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -356,10 +356,10 @@ impl Pipeline {
         #[cfg(any(feature = "image", feature = "svg"))]
         for (index, image) in instances.iter().enumerate() {
             if let Some(rec) = recs.get(&index) {
-                let x = rec.min.x as f32 / atlas_width;
-                let y = rec.min.y as f32 / atlas_height;
-                let w = (rec.size().width - 1) as f32 / atlas_width;
-                let h = (rec.size().height - 1) as f32 / atlas_height;
+                let x = (rec.min.x as f32 + 0.5) / atlas_width;
+                let y = (rec.min.y as f32 + 0.5) / atlas_height;
+                let w = (rec.size().width as f32 - 0.5) / atlas_width;
+                let h = (rec.size().height as f32 - 0.5) / atlas_height;
 
                 let instance_buffer = device
                     .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
-- 
cgit 


From 2f77a6bf5ac1b657c1f54ea0b589b1e115b95e6b Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Mon, 13 Jan 2020 15:33:12 +0100
Subject: Use array of atlases instead of one growing indefinitely.

---
 wgpu/src/image.rs          | 252 ++++++++++++++++++++++++++++++++++++---------
 wgpu/src/image/raster.rs   | 129 ++++-------------------
 wgpu/src/image/vector.rs   | 149 ++++++---------------------
 wgpu/src/shader/image.frag |   6 +-
 wgpu/src/shader/image.vert |   5 +-
 5 files changed, 260 insertions(+), 281 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 59257399..bda24280 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -9,15 +9,15 @@ use crate::image::raster::Memory;
 use crate::Transformation;
 use iced_native::{image, svg, Rectangle};
 
-use std::mem;
+use std::{collections::{HashMap, HashSet}, mem};
 
 #[cfg(any(feature = "image", feature = "svg"))]
-use std::{cell::RefCell, collections::HashMap};
+use std::cell::RefCell;
 
-use guillotiere::{AtlasAllocator, Size};
+use guillotiere::{Allocation, AtlasAllocator, Size};
 use debug_stub_derive::*;
 
-#[derive(DebugStub)]
+#[derive(Debug)]
 pub struct Pipeline {
     #[cfg(feature = "image")]
     raster_cache: RefCell<raster::Cache>,
@@ -31,9 +31,7 @@ pub struct Pipeline {
     instances: wgpu::Buffer,
     constants: wgpu::BindGroup,
     texture_layout: wgpu::BindGroupLayout,
-    #[debug_stub="ReplacementValue"]
-    allocator: AtlasAllocator,
-    atlas: wgpu::Texture,
+    atlas_array: AtlasArray,
 }
 
 impl Pipeline {
@@ -193,6 +191,11 @@ impl Pipeline {
                                 format: wgpu::VertexFormat::Float2,
                                 offset: 4 * 6,
                             },
+                            wgpu::VertexAttributeDescriptor {
+                                shader_location: 5,
+                                format: wgpu::VertexFormat::Float,
+                                offset: 4 * 8,
+                            },
                         ],
                     },
                 ],
@@ -214,25 +217,7 @@ impl Pipeline {
             usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
         });
 
-        let (width, height) = (512, 512);
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        let atlas = device.create_texture(&wgpu::TextureDescriptor {
-            size: extent,
-            array_layer_count: 1,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
+        let atlas_array = AtlasArray::new(1, device);
 
         Pipeline {
             #[cfg(feature = "image")]
@@ -248,8 +233,7 @@ impl Pipeline {
             instances,
             constants: constant_bind_group,
             texture_layout,
-            allocator: AtlasAllocator::new(Size::new(width as i32, height as i32)),
-            atlas,
+            atlas_array,
         }
     }
 
@@ -303,14 +287,13 @@ impl Pipeline {
                     {
                         let mut raster_cache = self.raster_cache.borrow_mut();
 
-                        if let Memory::Device(allocation) = raster_cache.upload(
-                            handle,
+                        if let Memory::Device { layer, allocation } = raster_cache.upload(
+                            _handle,
                             device,
                             encoder,
-                            &mut self.allocator,
-                            &mut self.atlas
+                            &mut self.atlas_array,
                         ) {
-                            let rec = allocation.rectangle;
+                            let rec = (*layer, allocation.rectangle);
 
                             let _ = recs.insert(index, rec);
                         }
@@ -322,16 +305,15 @@ impl Pipeline {
                         let mut vector_cache = self.vector_cache.borrow_mut();
 
                         // Upload rasterized svg to texture atlas
-                        if let Some(allocation) = vector_cache.upload(
+                        if let Some((layer, allocation)) = vector_cache.upload(
                             _handle,
                             image.scale,
                             _scale,
                             device,
                             encoder,
-                            &mut self.allocator,
-                            &mut self.atlas,
+                            &mut self.atlas_array,
                         ) {
-                            let rec = allocation.rectangle;
+                            let rec = (*layer, allocation.rectangle);
 
                             let _ = recs.insert(index, rec);
                         }
@@ -340,26 +322,23 @@ impl Pipeline {
             }
         }
 
-        let atlas_width = self.allocator.size().width as f32;
-        let atlas_height = self.allocator.size().height as f32;
-
         let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
             layout: &self.texture_layout,
             bindings: &[wgpu::Binding {
                 binding: 0,
                 resource: wgpu::BindingResource::TextureView(
-                    &self.atlas.create_default_view(),
+                    &self.atlas_array.texture().create_default_view(),
                 ),
             }],
         });
 
         #[cfg(any(feature = "image", feature = "svg"))]
         for (index, image) in instances.iter().enumerate() {
-            if let Some(rec) = recs.get(&index) {
-                let x = (rec.min.x as f32 + 0.5) / atlas_width;
-                let y = (rec.min.y as f32 + 0.5) / atlas_height;
-                let w = (rec.size().width as f32 - 0.5) / atlas_width;
-                let h = (rec.size().height as f32 - 0.5) / atlas_height;
+            if let Some((layer, rec)) = recs.get(&index) {
+                let x = (rec.min.x as f32 + 0.5) / (ATLAS_SIZE as f32);
+                let y = (rec.min.y as f32 + 0.5) / (ATLAS_SIZE as f32);
+                let w = (rec.size().width as f32 - 0.5) / (ATLAS_SIZE as f32);
+                let h = (rec.size().height as f32 - 0.5) / (ATLAS_SIZE as f32);
 
                 let instance_buffer = device
                     .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
@@ -367,7 +346,8 @@ impl Pipeline {
                         _position: image.position,
                         _scale: image.scale,
                         _position_in_atlas: [x, y],
-                        _scale_in_atlas: [w, h]
+                        _scale_in_atlas: [w, h],
+                        _layer: *layer as f32,
                     }]);
 
                 encoder.copy_buffer_to_buffer(
@@ -424,10 +404,10 @@ impl Pipeline {
 
     pub fn trim_cache(&mut self) {
         #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim(&mut self.allocator);
+        self.raster_cache.borrow_mut().trim(&mut self.atlas_array);
 
         #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim(&mut self.allocator);
+        self.vector_cache.borrow_mut().trim(&mut self.atlas_array);
     }
 }
 
@@ -442,6 +422,177 @@ pub enum Handle {
     Vector(svg::Handle),
 }
 
+#[derive(DebugStub)]
+pub struct AtlasArray {
+    texture: wgpu::Texture,
+    #[debug_stub="ReplacementValue"]
+    allocators: HashMap<u32, AtlasAllocator>,
+    layers_without_allocators: HashSet<u32>,
+    size: u32,
+}
+
+impl AtlasArray {
+    pub fn new(array_size: u32, device: &wgpu::Device) -> Self {
+        let (width, height) = (ATLAS_SIZE, ATLAS_SIZE);
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        let texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: array_size,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        AtlasArray {
+            texture,
+            allocators: HashMap::new(),
+            layers_without_allocators: HashSet::new(),
+            size: array_size,
+        }
+    }
+
+    pub fn texture(&self) -> &wgpu::Texture {
+        &self.texture
+    }
+
+    pub fn allocate(&mut self, size: Size) -> Option<(u32, Allocation)> {
+        for layer in 0..self.size {
+            if self.layers_without_allocators.contains(&layer) {
+                continue;
+            }
+
+            let allocator = self.allocators.entry(layer)
+                .or_insert_with(|| AtlasAllocator::new(
+                    Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32)
+                ));
+
+            if let Some(a) = allocator.allocate(size.clone()) {
+                return Some((layer, a));
+            }
+        }
+
+        None
+    }
+
+    pub fn deallocate(&mut self, layer: u32, allocation: &Allocation) {
+        if let Some(allocator) = self.allocators.get_mut(&layer) {
+            allocator.deallocate(allocation.id);
+        }
+    }
+
+    pub fn upload<T: Copy + 'static>(
+        &mut self,
+        data: &[T],
+        layer: u32,
+        allocation: &guillotiere::Allocation,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        let size = allocation.rectangle.size();
+        let (width, height) = (size.width as u32, size.height as u32);
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        let temp_buf = device
+            .create_buffer_mapped(
+                data.len(),
+                wgpu::BufferUsage::COPY_SRC,
+            )
+            .fill_from_slice(data);
+
+        encoder.copy_buffer_to_texture(
+            wgpu::BufferCopyView {
+                buffer: &temp_buf,
+                offset: 0,
+                row_pitch: 4 * width,
+                image_height: height,
+            },
+            wgpu::TextureCopyView {
+                texture: &self.texture,
+                array_layer: layer as u32,
+                mip_level: 0,
+                origin: wgpu::Origin3d {
+                    x: allocation.rectangle.min.x as f32,
+                    y: allocation.rectangle.min.y as f32,
+                    z: 0.0,
+                },
+            },
+            extent,
+        );
+    }
+
+    pub fn grow(
+        &mut self,
+        grow_by: u32,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        let old_atlas_array_size = self.size;
+
+        let new_texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: wgpu::Extent3d {
+                width: ATLAS_SIZE,
+                height: ATLAS_SIZE,
+                depth: 1,
+            },
+            array_layer_count: old_atlas_array_size + grow_by,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        for i in 0..old_atlas_array_size {
+            encoder.copy_texture_to_texture(
+                wgpu::TextureCopyView {
+                    texture: &self.texture,
+                    array_layer: i,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::TextureCopyView {
+                    texture: &new_texture,
+                    array_layer: i,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::Extent3d {
+                    width: ATLAS_SIZE,
+                    height: ATLAS_SIZE,
+                    depth: 1,
+                }
+            );
+        }
+
+        self.texture = new_texture;
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct Vertex {
@@ -465,6 +616,8 @@ const QUAD_VERTS: [Vertex; 4] = [
     },
 ];
 
+const ATLAS_SIZE: u32 = 8192;
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 struct Instance {
@@ -472,6 +625,7 @@ struct Instance {
     _scale: [f32; 2],
     _position_in_atlas: [f32; 2],
     _scale_in_atlas: [f32; 2],
+    _layer: f32,
 }
 
 #[repr(C)]
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 33750cac..32afb749 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,17 +1,19 @@
+use crate::image::AtlasArray;
 use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::{Allocation, AtlasAllocator, Size};
+use guillotiere::{Allocation, Size};
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device(
+    Device {
+        layer: u32,
         #[debug_stub="ReplacementValue"]
-        Allocation
-    ),
+        allocation: Allocation,
+    },
     NotFound,
     Invalid,
 }
@@ -20,7 +22,7 @@ impl Memory {
     pub fn dimensions(&self) -> (u32, u32) {
         match self {
             Memory::Host(image) => image.dimensions(),
-            Memory::Device(allocation) => {
+            Memory::Device { allocation, .. } => {
                 let size = &allocation.rectangle.size();
                 (size.width as u32, size.height as u32)
             },
@@ -75,8 +77,7 @@ impl Cache {
         handle: &image::Handle,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        allocator: &mut AtlasAllocator,
-        atlas: &mut wgpu::Texture,
+        atlas_array: &mut AtlasArray,
     ) -> &Memory {
         let _ = self.load(handle);
 
@@ -86,119 +87,29 @@ impl Cache {
             let (width, height) = image.dimensions();
             let size = Size::new(width as i32, height as i32);
 
-            let old_atlas_size = allocator.size();
-            let allocation;
+            let (layer, allocation) = atlas_array.allocate(size).unwrap_or_else(|| {
+                atlas_array.grow(1, device, encoder);
+                atlas_array.allocate(size).unwrap()
+            });
 
-            loop {
-                if let Some(a) = allocator.allocate(size) {
-                    allocation = a;
-                    break;
-                }
-
-                allocator.grow(allocator.size() * 2);
-            }
+            let flat_samples = image.as_flat_samples();
+            let slice = flat_samples.as_slice();
 
-            let new_atlas_size = allocator.size();
-
-            if new_atlas_size != old_atlas_size {
-                let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
-                    size: wgpu::Extent3d {
-                        width: new_atlas_size.width as u32,
-                        height: new_atlas_size.height as u32,
-                        depth: 1,
-                    },
-                    array_layer_count: 1,
-                    mip_level_count: 1,
-                    sample_count: 1,
-                    dimension: wgpu::TextureDimension::D2,
-                    format: wgpu::TextureFormat::Bgra8UnormSrgb,
-                    usage: wgpu::TextureUsage::COPY_DST
-                        | wgpu::TextureUsage::COPY_SRC
-                        | wgpu::TextureUsage::SAMPLED,
-                });
-
-                encoder.copy_texture_to_texture(
-                    wgpu::TextureCopyView {
-                        texture: atlas,
-                        array_layer: 0,
-                        mip_level: 0,
-                        origin: wgpu::Origin3d {
-                            x: 0.0,
-                            y: 0.0,
-                            z: 0.0,
-                        },
-                    },
-                    wgpu::TextureCopyView {
-                        texture: &new_atlas,
-                        array_layer: 0,
-                        mip_level: 0,
-                        origin: wgpu::Origin3d {
-                            x: 0.0,
-                            y: 0.0,
-                            z: 0.0,
-                        },
-                    },
-                    wgpu::Extent3d {
-                        width: old_atlas_size.width as u32,
-                        height: old_atlas_size.height as u32,
-                        depth: 1,
-                    }
-                );
-
-                *atlas = new_atlas;
-            }
+            atlas_array.upload(slice, layer, &allocation, device, encoder);
 
-            let extent = wgpu::Extent3d {
-                width,
-                height,
-                depth: 1,
-            };
-
-            let temp_buf = {
-                let flat_samples = image.as_flat_samples();
-                let slice = flat_samples.as_slice();
-
-                device
-                    .create_buffer_mapped(
-                        slice.len(),
-                        wgpu::BufferUsage::COPY_SRC,
-                    )
-                    .fill_from_slice(slice)
-            };
-
-            encoder.copy_buffer_to_texture(
-                wgpu::BufferCopyView {
-                    buffer: &temp_buf,
-                    offset: 0,
-                    row_pitch: 4 * width,
-                    image_height: height,
-                },
-                wgpu::TextureCopyView {
-                    texture: atlas,
-                    array_layer: 0,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: allocation.rectangle.min.x as f32,
-                        y: allocation.rectangle.min.y as f32,
-                        z: 0.0,
-                    },
-                },
-                extent,
-            );
-
-            *memory = Memory::Device(allocation);
+            *memory = Memory::Device { layer, allocation };
         }
 
         memory
     }
 
-    pub fn trim(&mut self, allocator: &mut AtlasAllocator) {
+    pub fn trim(&mut self, atlas_array: &mut AtlasArray) {
         let hits = &self.hits;
 
-        for (id, mem) in &mut self.map {
-            if let Memory::Device(allocation) = mem {
+        for (id, mem) in &self.map {
+            if let Memory::Device { layer, allocation } = mem {
                 if !hits.contains(&id) {
-                    allocator.deallocate(allocation.id);
+                    atlas_array.deallocate(*layer, allocation);
                 }
             }
         }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 2afe7d92..2972bc4a 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,8 +1,9 @@
+use crate::image::AtlasArray;
 use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::{Allocation, AtlasAllocator, Size};
+use guillotiere::{Allocation, Size};
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
@@ -31,7 +32,7 @@ impl Svg {
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
     #[debug_stub="ReplacementValue"]
-    rasterized: HashMap<(u64, u32, u32), Allocation>,
+    rasterized: HashMap<(u64, u32, u32), (u32, Allocation)>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
 }
@@ -69,9 +70,8 @@ impl Cache {
         scale: f32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        allocator: &mut AtlasAllocator,
-        atlas: &mut wgpu::Texture,
-    ) -> Option<&Allocation> {
+        atlas_array: &mut AtlasArray,
+    ) -> Option<&(u32, Allocation)> {
         let id = handle.id();
 
         let (width, height) = (
@@ -99,127 +99,40 @@ impl Cache {
                 }
 
                 let size = Size::new(width as i32, height as i32);
-                let old_atlas_size = allocator.size();
-                let allocation;
 
-                loop {
-                    if let Some(a) = allocator.allocate(size) {
-                        allocation = a;
-                        break;
-                    }
-
-                    allocator.grow(allocator.size() * 2);
-                }
-
-                let new_atlas_size = allocator.size();
-
-                if new_atlas_size != old_atlas_size {
-                    let new_atlas = device.create_texture(&wgpu::TextureDescriptor {
-                        size: wgpu::Extent3d {
-                            width: new_atlas_size.width as u32,
-                            height: new_atlas_size.height as u32,
-                            depth: 1,
-                        },
-                        array_layer_count: 1,
-                        mip_level_count: 1,
-                        sample_count: 1,
-                        dimension: wgpu::TextureDimension::D2,
-                        format: wgpu::TextureFormat::Bgra8UnormSrgb,
-                        usage: wgpu::TextureUsage::COPY_DST
-                            | wgpu::TextureUsage::COPY_SRC
-                            | wgpu::TextureUsage::SAMPLED,
-                    });
-
-                    encoder.copy_texture_to_texture(
-                        wgpu::TextureCopyView {
-                            texture: atlas,
-                            array_layer: 0,
-                            mip_level: 0,
-                            origin: wgpu::Origin3d {
-                                x: 0.0,
-                                y: 0.0,
-                                z: 0.0,
-                            },
-                        },
-                        wgpu::TextureCopyView {
-                            texture: &new_atlas,
-                            array_layer: 0,
-                            mip_level: 0,
-                            origin: wgpu::Origin3d {
-                                x: 0.0,
-                                y: 0.0,
-                                z: 0.0,
-                            },
-                        },
-                        wgpu::Extent3d {
-                            width: old_atlas_size.width as u32,
-                            height: old_atlas_size.height as u32,
-                            depth: 1,
-                        }
-                    );
-
-                    *atlas = new_atlas;
-                }
+                let (layer, allocation) = atlas_array.allocate(size).unwrap_or_else(|| {
+                    atlas_array.grow(1, device, encoder);
+                    atlas_array.allocate(size).unwrap()
+                });
 
                 // TODO: Optimize!
                 // We currently rerasterize the SVG when its size changes. This is slow
                 // as heck. A GPU rasterizer like `pathfinder` may perform better.
                 // It would be cool to be able to smooth resize the `svg` example.
-                let temp_buf = {
-                    let screen_size =
-                        resvg::ScreenSize::new(width, height).unwrap();
-
-                    let mut canvas = resvg::raqote::DrawTarget::new(
-                        width as i32,
-                        height as i32,
-                    );
-
-                    resvg::backend_raqote::render_to_canvas(
-                        tree,
-                        &resvg::Options::default(),
-                        screen_size,
-                        &mut canvas,
-                    );
-
-                    let slice = canvas.get_data();
-
-                    device
-                        .create_buffer_mapped(
-                            slice.len(),
-                            wgpu::BufferUsage::COPY_SRC,
-                        )
-                        .fill_from_slice(slice)
-                };
-
-                encoder.copy_buffer_to_texture(
-                    wgpu::BufferCopyView {
-                        buffer: &temp_buf,
-                        offset: 0,
-                        row_pitch: 4 * width as u32,
-                        image_height: height as u32,
-                    },
-                    wgpu::TextureCopyView {
-                        texture: atlas,
-                        array_layer: 0,
-                        mip_level: 0,
-                        origin: wgpu::Origin3d {
-                            x: allocation.rectangle.min.x as f32,
-                            y: allocation.rectangle.min.y as f32,
-                            z: 0.0,
-                        },
-                    },
-                    wgpu::Extent3d {
-                        width,
-                        height,
-                        depth: 1,
-                    },
+                let screen_size =
+                    resvg::ScreenSize::new(width, height).unwrap();
+
+                let mut canvas = resvg::raqote::DrawTarget::new(
+                    width as i32,
+                    height as i32,
                 );
 
+                resvg::backend_raqote::render_to_canvas(
+                    tree,
+                    &resvg::Options::default(),
+                    screen_size,
+                    &mut canvas,
+                );
+
+                let slice = canvas.get_data();
+
+                atlas_array.upload(slice, layer, &allocation, device, encoder);
+
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert((id, width, height));
                 let _ = self
                     .rasterized
-                    .insert((id, width, height), allocation);
+                    .insert((id, width, height), (layer, allocation));
 
                 self.rasterized.get(&(id, width, height))
             }
@@ -227,13 +140,13 @@ impl Cache {
         }
     }
 
-    pub fn trim(&mut self, allocator: &mut AtlasAllocator) {
+    pub fn trim(&mut self, atlas_array: &mut AtlasArray) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
-        for (k, alloc) in &mut self.rasterized {
-            if !rasterized_hits.contains(&k) {
-                allocator.deallocate(alloc.id);
+        for (k, (layer, allocation)) in &self.rasterized {
+            if !rasterized_hits.contains(k) {
+                atlas_array.deallocate(*layer, allocation);
             }
         }
 
diff --git a/wgpu/src/shader/image.frag b/wgpu/src/shader/image.frag
index e35e455a..2809e9e6 100644
--- a/wgpu/src/shader/image.frag
+++ b/wgpu/src/shader/image.frag
@@ -1,12 +1,12 @@
 #version 450
 
-layout(location = 0) in vec2 v_Uv;
+layout(location = 0) in vec3 v_Uv;
 
 layout(set = 0, binding = 1) uniform sampler u_Sampler;
-layout(set = 1, binding = 0) uniform texture2D u_Texture;
+layout(set = 1, binding = 0) uniform texture2DArray u_Texture;
 
 layout(location = 0) out vec4 o_Color;
 
 void main() {
-    o_Color = texture(sampler2D(u_Texture, u_Sampler), v_Uv);
+    o_Color = texture(sampler2DArray(u_Texture, u_Sampler), v_Uv);
 }
diff --git a/wgpu/src/shader/image.vert b/wgpu/src/shader/image.vert
index 953840d2..0ce7dd6b 100644
--- a/wgpu/src/shader/image.vert
+++ b/wgpu/src/shader/image.vert
@@ -5,15 +5,16 @@ layout(location = 1) in vec2 i_Pos;
 layout(location = 2) in vec2 i_Scale;
 layout(location = 3) in vec2 i_Atlas_Pos;
 layout(location = 4) in vec2 i_Atlas_Scale;
+layout(location = 5) in float i_Layer;
 
 layout (set = 0, binding = 0) uniform Globals {
     mat4 u_Transform;
 };
 
-layout(location = 0) out vec2 o_Uv;
+layout(location = 0) out vec3 o_Uv;
 
 void main() {
-    o_Uv = v_Pos * i_Atlas_Scale + i_Atlas_Pos;
+    o_Uv = vec3(v_Pos * i_Atlas_Scale + i_Atlas_Pos, i_Layer);
 
     mat4 i_Transform = mat4(
         vec4(i_Scale.x, 0.0, 0.0, 0.0),
-- 
cgit 


From 3f388351054c0961923b5f78e7dcf42289565a48 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Thu, 16 Jan 2020 14:03:46 +0100
Subject: Implement allocating large images across multiple texture array
 layers.

---
 wgpu/src/image.rs        | 685 +++++++++++++++++++++++++++++++++++------------
 wgpu/src/image/raster.rs |  35 +--
 wgpu/src/image/vector.rs |  27 +-
 3 files changed, 539 insertions(+), 208 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index bda24280..f8faa543 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -9,7 +9,7 @@ use crate::image::raster::Memory;
 use crate::Transformation;
 use iced_native::{image, svg, Rectangle};
 
-use std::{collections::{HashMap, HashSet}, mem};
+use std::mem;
 
 #[cfg(any(feature = "image", feature = "svg"))]
 use std::cell::RefCell;
@@ -31,7 +31,7 @@ pub struct Pipeline {
     instances: wgpu::Buffer,
     constants: wgpu::BindGroup,
     texture_layout: wgpu::BindGroupLayout,
-    atlas_array: AtlasArray,
+    texture_array: TextureArray,
 }
 
 impl Pipeline {
@@ -217,7 +217,7 @@ impl Pipeline {
             usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
         });
 
-        let atlas_array = AtlasArray::new(1, device);
+        let texture_array = TextureArray::new(device);
 
         Pipeline {
             #[cfg(feature = "image")]
@@ -233,7 +233,7 @@ impl Pipeline {
             instances,
             constants: constant_bind_group,
             texture_layout,
-            atlas_array,
+            texture_array,
         }
     }
 
@@ -259,8 +259,8 @@ impl Pipeline {
         encoder: &mut wgpu::CommandEncoder,
         instances: &[Image],
         transformation: Transformation,
-        bounds: Rectangle<u32>,
-        target: &wgpu::TextureView,
+        _bounds: Rectangle<u32>,
+        _target: &wgpu::TextureView,
         _scale: f32,
     ) {
         let uniforms_buffer = device
@@ -277,25 +277,27 @@ impl Pipeline {
             std::mem::size_of::<Uniforms>() as u64,
         );
 
-        #[cfg(any(feature = "image", feature = "svg"))]
-        let mut recs = HashMap::new();
-
-        for (index, image) in instances.iter().enumerate() {
+        for image in instances {
             match &image.handle {
                 Handle::Raster(_handle) => {
                     #[cfg(feature = "image")]
                     {
                         let mut raster_cache = self.raster_cache.borrow_mut();
 
-                        if let Memory::Device { layer, allocation } = raster_cache.upload(
+                        if let Memory::Device(allocation) = raster_cache.upload(
                             _handle,
                             device,
                             encoder,
-                            &mut self.atlas_array,
+                            &mut self.texture_array,
                         ) {
-                            let rec = (*layer, allocation.rectangle);
-
-                            let _ = recs.insert(index, rec);
+                            self.draw_image(
+                                device,
+                                encoder,
+                                image,
+                                allocation,
+                                _bounds,
+                                _target,
+                            );
                         }
                     }
                 }
@@ -305,109 +307,173 @@ impl Pipeline {
                         let mut vector_cache = self.vector_cache.borrow_mut();
 
                         // Upload rasterized svg to texture atlas
-                        if let Some((layer, allocation)) = vector_cache.upload(
+                        if let Some(allocation) = vector_cache.upload(
                             _handle,
                             image.scale,
                             _scale,
                             device,
                             encoder,
-                            &mut self.atlas_array,
+                            &mut self.texture_array,
                         ) {
-                            let rec = (*layer, allocation.rectangle);
-
-                            let _ = recs.insert(index, rec);
+                            self.draw_image(
+                                device,
+                                encoder,
+                                image,
+                                allocation,
+                                _bounds,
+                                _target,
+                            );
                         }
                     }
                 }
             }
         }
+    }
+
+    pub fn trim_cache(&mut self) {
+        #[cfg(feature = "image")]
+        self.raster_cache.borrow_mut().trim(&mut self.texture_array);
+
+        #[cfg(feature = "svg")]
+        self.vector_cache.borrow_mut().trim(&mut self.texture_array);
+    }
 
+    fn draw_image(
+        &self,
+        device: &mut wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+        image: &Image,
+        allocation: &ImageAllocation,
+        bounds: Rectangle<u32>,
+        target: &wgpu::TextureView,
+    )  {
         let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
             layout: &self.texture_layout,
             bindings: &[wgpu::Binding {
                 binding: 0,
                 resource: wgpu::BindingResource::TextureView(
-                    &self.atlas_array.texture().create_default_view(),
+                    &self.texture_array.texture.create_default_view(),
                 ),
             }],
         });
 
-        #[cfg(any(feature = "image", feature = "svg"))]
-        for (index, image) in instances.iter().enumerate() {
-            if let Some((layer, rec)) = recs.get(&index) {
-                let x = (rec.min.x as f32 + 0.5) / (ATLAS_SIZE as f32);
-                let y = (rec.min.y as f32 + 0.5) / (ATLAS_SIZE as f32);
-                let w = (rec.size().width as f32 - 0.5) / (ATLAS_SIZE as f32);
-                let h = (rec.size().height as f32 - 0.5) / (ATLAS_SIZE as f32);
-
-                let instance_buffer = device
-                    .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
-                    .fill_from_slice(&[Instance {
-                        _position: image.position,
-                        _scale: image.scale,
-                        _position_in_atlas: [x, y],
-                        _scale_in_atlas: [w, h],
-                        _layer: *layer as f32,
-                    }]);
-
-                encoder.copy_buffer_to_buffer(
-                    &instance_buffer,
-                    0,
-                    &self.instances,
-                    0,
-                    mem::size_of::<Instance>() as u64,
-                );
-
-                let mut render_pass = encoder.begin_render_pass(
-                    &wgpu::RenderPassDescriptor {
-                        color_attachments: &[
-                            wgpu::RenderPassColorAttachmentDescriptor {
-                                attachment: target,
-                                resolve_target: None,
-                                load_op: wgpu::LoadOp::Load,
-                                store_op: wgpu::StoreOp::Store,
-                                clear_color: wgpu::Color {
-                                    r: 0.0,
-                                    g: 0.0,
-                                    b: 0.0,
-                                    a: 0.0,
-                                },
-                            },
-                        ],
-                        depth_stencil_attachment: None,
-                    },
-                );
-
-                render_pass.set_pipeline(&self.pipeline);
-                render_pass.set_bind_group(0, &self.constants, &[]);
-                render_pass.set_bind_group(1, &texture, &[]);
-                render_pass.set_index_buffer(&self.indices, 0);
-                render_pass.set_vertex_buffers(
-                    0,
-                    &[(&self.vertices, 0), (&self.instances, 0)],
-                );
-                render_pass.set_scissor_rect(
-                    bounds.x,
-                    bounds.y,
-                    bounds.width,
-                    bounds.height,
-                );
-
-                render_pass.draw_indexed(
-                    0..QUAD_INDICES.len() as u32,
-                    0,
-                    0..1 as u32,
-                );
+        match allocation {
+            ImageAllocation::SingleAllocation(allocation) => {
+                self.draw_allocation(
+                    device,
+                    encoder,
+                    image.position,
+                    image.scale,
+                    allocation,
+                    &texture,
+                    bounds,
+                    target,
+                )
+            }
+            ImageAllocation::MultipleAllocations { mappings, size } => {
+                let scaling_x = image.scale[0] / size.0 as f32;
+                let scaling_y = image.scale[1] / size.1 as f32;
+
+                for mapping in mappings {
+                    let mut position = image.position;
+                    let mut scale = image.scale;
+
+                    position[0] += mapping.src_pos.0 as f32 * scaling_x;
+                    position[1] += mapping.src_pos.1 as f32 * scaling_y;
+                    scale[0] = mapping.allocation.size().0 as f32 * scaling_x;
+                    scale[1] = mapping.allocation.size().1 as f32 * scaling_y;
+
+                    self.draw_allocation(
+                        device,
+                        encoder,
+                        position,
+                        scale,
+                        &mapping.allocation,
+                        &texture,
+                        bounds,
+                        target,
+                    )
+                }
             }
+            _ => {}
         }
     }
 
-    pub fn trim_cache(&mut self) {
-        #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim(&mut self.atlas_array);
+    fn draw_allocation(
+        &self,
+        device: &mut wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+        position: [f32; 2],
+        scale: [f32; 2],
+        allocation: &ArrayAllocation,
+        texture: &wgpu::BindGroup,
+        bounds: Rectangle<u32>,
+        target: &wgpu::TextureView,
+    ) {
+        let x = (allocation.position().0 as f32 + 0.5) / (ATLAS_SIZE as f32);
+        let y = (allocation.position().1 as f32 + 0.5) / (ATLAS_SIZE as f32);
+        let w = (allocation.size().0 as f32 - 0.5) / (ATLAS_SIZE as f32);
+        let h = (allocation.size().1 as f32 - 0.5) / (ATLAS_SIZE as f32);
+        let layer = allocation.layer() as f32;
 
-        #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim(&mut self.atlas_array);
+        let instance_buffer = device
+            .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
+            .fill_from_slice(&[Instance {
+                _position: position,
+                _scale: scale,
+                _position_in_atlas: [x, y],
+                _scale_in_atlas: [w, h],
+                _layer: layer,
+            }]);
+
+        encoder.copy_buffer_to_buffer(
+            &instance_buffer,
+            0,
+            &self.instances,
+            0,
+            mem::size_of::<Instance>() as u64,
+        );
+
+        let mut render_pass = encoder.begin_render_pass(
+            &wgpu::RenderPassDescriptor {
+                color_attachments: &[
+                    wgpu::RenderPassColorAttachmentDescriptor {
+                        attachment: target,
+                        resolve_target: None,
+                        load_op: wgpu::LoadOp::Load,
+                        store_op: wgpu::StoreOp::Store,
+                        clear_color: wgpu::Color {
+                            r: 0.0,
+                            g: 0.0,
+                            b: 0.0,
+                            a: 0.0,
+                        },
+                    },
+                ],
+                depth_stencil_attachment: None,
+            },
+        );
+
+        render_pass.set_pipeline(&self.pipeline);
+        render_pass.set_bind_group(0, &self.constants, &[]);
+        render_pass.set_bind_group(1, &texture, &[]);
+        render_pass.set_index_buffer(&self.indices, 0);
+        render_pass.set_vertex_buffers(
+            0,
+            &[(&self.vertices, 0), (&self.instances, 0)],
+        );
+        render_pass.set_scissor_rect(
+            bounds.x,
+            bounds.y,
+            bounds.width,
+            bounds.height,
+        );
+
+        render_pass.draw_indexed(
+            0..QUAD_INDICES.len() as u32,
+            0,
+            0..1 as u32,
+        );
     }
 }
 
@@ -422,17 +488,96 @@ pub enum Handle {
     Vector(svg::Handle),
 }
 
+#[derive(Debug)]
+pub struct ArrayAllocationMapping {
+    src_pos: (u32, u32),
+    allocation: ArrayAllocation,
+}
+
+#[derive(Debug)]
+pub enum ImageAllocation {
+    SingleAllocation(ArrayAllocation),
+    MultipleAllocations {
+        mappings: Vec<ArrayAllocationMapping>,
+        size: (u32, u32),
+    },
+    Error,
+}
+
+impl ImageAllocation {
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            ImageAllocation::SingleAllocation(allocation) => {
+                allocation.size()
+            }
+            ImageAllocation::MultipleAllocations { size, .. } => {
+                *size
+            }
+            _ => (0, 0)
+        }
+    }
+}
+
+#[derive(DebugStub)]
+pub enum ArrayAllocation {
+    AtlasAllocation {
+        layer: usize,
+        #[debug_stub = "ReplacementValue"]
+        allocation: Allocation,
+    },
+    WholeLayer {
+        layer: usize,
+    }
+}
+
+impl ArrayAllocation {
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            ArrayAllocation::AtlasAllocation { allocation, .. } => {
+                let size = allocation.rectangle.size();
+                (size.width as u32, size.height as u32)
+            }
+            ArrayAllocation::WholeLayer { .. } => (ATLAS_SIZE, ATLAS_SIZE)
+        }
+    }
+
+    pub fn position(&self) -> (u32, u32) {
+        match self {
+            ArrayAllocation::AtlasAllocation { allocation, .. } => {
+                let min = &allocation.rectangle.min;
+                (min.x as u32, min.y as u32)
+            }
+            ArrayAllocation::WholeLayer { .. } => (0, 0)
+        }
+    }
+
+    pub fn layer(&self) -> usize {
+        match self {
+            ArrayAllocation::AtlasAllocation { layer, .. } => *layer,
+            ArrayAllocation::WholeLayer { layer } => *layer,
+        }
+    }
+}
+
 #[derive(DebugStub)]
-pub struct AtlasArray {
+pub enum TextureLayer {
+    Whole,
+    Atlas(
+        #[debug_stub="ReplacementValue"]
+        AtlasAllocator
+    ),
+    Empty,
+}
+
+#[derive(Debug)]
+pub struct TextureArray {
     texture: wgpu::Texture,
-    #[debug_stub="ReplacementValue"]
-    allocators: HashMap<u32, AtlasAllocator>,
-    layers_without_allocators: HashSet<u32>,
-    size: u32,
+    texture_array_size: u32,
+    layers: Vec<TextureLayer>,
 }
 
-impl AtlasArray {
-    pub fn new(array_size: u32, device: &wgpu::Device) -> Self {
+impl TextureArray {
+    pub fn new(device: &wgpu::Device) -> Self {
         let (width, height) = (ATLAS_SIZE, ATLAS_SIZE);
 
         let extent = wgpu::Extent3d {
@@ -443,7 +588,7 @@ impl AtlasArray {
 
         let texture = device.create_texture(&wgpu::TextureDescriptor {
             size: extent,
-            array_layer_count: array_size,
+            array_layer_count: 1,
             mip_level_count: 1,
             sample_count: 1,
             dimension: wgpu::TextureDimension::D2,
@@ -453,53 +598,217 @@ impl AtlasArray {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        AtlasArray {
+        let size = Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32);
+
+        TextureArray {
             texture,
-            allocators: HashMap::new(),
-            layers_without_allocators: HashSet::new(),
-            size: array_size,
+            texture_array_size: 1,
+            layers: vec!(TextureLayer::Atlas(AtlasAllocator::new(size))),
         }
     }
 
-    pub fn texture(&self) -> &wgpu::Texture {
-        &self.texture
-    }
+    pub fn allocate(&mut self, size: Size) -> ImageAllocation {
+        // Allocate one layer if allocation fits perfectly
+        if size.width == ATLAS_SIZE as i32 && size.height == ATLAS_SIZE as i32 {
+            for (i, layer) in &mut self.layers.iter_mut().enumerate() {
+                if let TextureLayer::Empty = layer
+                {
+                    *layer = TextureLayer::Whole;
+                    return ImageAllocation::SingleAllocation(
+                        ArrayAllocation::WholeLayer { layer: i }
+                    );
+                }
+            }
+
+            self.layers.push(TextureLayer::Whole);
+            return ImageAllocation::SingleAllocation(
+                ArrayAllocation::WholeLayer { layer: self.layers.len() - 1 }
+            );
+        }
 
-    pub fn allocate(&mut self, size: Size) -> Option<(u32, Allocation)> {
-        for layer in 0..self.size {
-            if self.layers_without_allocators.contains(&layer) {
-                continue;
+        // Split big allocations across multiple layers
+        if size.width > ATLAS_SIZE as i32 || size.height > ATLAS_SIZE as i32 {
+            let mut mappings = Vec::new();
+
+            let mut y = 0;
+            while y < size.height {
+                let height = std::cmp::min(size.height - y, ATLAS_SIZE as i32);
+                let mut x = 0;
+
+                while x < size.width {
+                    let width = std::cmp::min(size.width - x, ATLAS_SIZE as i32);
+                    if let ImageAllocation::SingleAllocation(allocation) = self.allocate(Size::new(width, height)) {
+                        let src_pos = (x as u32, y as u32);
+                        mappings.push(ArrayAllocationMapping { src_pos, allocation });
+                    }
+
+                    x += width;
+                }
+                y += height;
             }
 
-            let allocator = self.allocators.entry(layer)
-                .or_insert_with(|| AtlasAllocator::new(
-                    Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32)
-                ));
+            return ImageAllocation::MultipleAllocations {
+                mappings,
+                size: (size.width as u32, size.height as u32),
+            };
+        }
 
-            if let Some(a) = allocator.allocate(size.clone()) {
-                return Some((layer, a));
+        // Try allocating on an existing layer
+        for (i, layer) in self.layers.iter_mut().enumerate() {
+            if let TextureLayer::Atlas(allocator) = layer {
+                if let Some(allocation) = allocator.allocate(size.clone()) {
+                    let array_allocation = ArrayAllocation::AtlasAllocation { layer: i, allocation };
+                    return ImageAllocation::SingleAllocation(array_allocation);
+                }
             }
         }
 
-        None
+        // Create new layer with atlas allocator
+        let mut allocator = AtlasAllocator::new(Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32));
+        if let Some(allocation) = allocator.allocate(size) {
+            self.layers.push(TextureLayer::Atlas(allocator));
+
+            return ImageAllocation::SingleAllocation(
+                ArrayAllocation::AtlasAllocation {
+                    layer: self.layers.len() - 1,
+                    allocation,
+                }
+            );
+        }
+
+        // One of the above should have worked
+        ImageAllocation::Error
     }
 
-    pub fn deallocate(&mut self, layer: u32, allocation: &Allocation) {
-        if let Some(allocator) = self.allocators.get_mut(&layer) {
-            allocator.deallocate(allocation.id);
+    pub fn deallocate(&mut self, allocation: &ImageAllocation) {
+        match allocation {
+            ImageAllocation::SingleAllocation(allocation) => {
+                if let Some(layer) = self.layers.get_mut(allocation.layer()) {
+                    match allocation {
+                        ArrayAllocation::WholeLayer { .. } => {
+                            *layer = TextureLayer::Empty;
+                        }
+                        ArrayAllocation::AtlasAllocation { allocation, .. } => {
+                            if let TextureLayer::Atlas(allocator) = layer {
+                                allocator.deallocate(allocation.id);
+                            }
+                        }
+                    }
+                }
+            }
+            ImageAllocation::MultipleAllocations { mappings, .. } => {
+                for mapping in mappings {
+                    if let Some(layer) = self.layers.get_mut(mapping.allocation.layer()) {
+                        match &mapping.allocation {
+                            ArrayAllocation::WholeLayer { .. } => {
+                                *layer = TextureLayer::Empty;
+                            }
+                            ArrayAllocation::AtlasAllocation { allocation, .. } => {
+                                if let TextureLayer::Atlas(allocator) = layer {
+                                    allocator.deallocate(allocation.id);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            _ => {}
         }
+
     }
 
-    pub fn upload<T: Copy + 'static>(
+    fn upload<C, I>(
         &mut self,
-        data: &[T],
-        layer: u32,
-        allocation: &guillotiere::Allocation,
+        image: &I,
+        allocation: &ImageAllocation,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
+    )
+    where
+        I: RawImageData<Chunk = C>,
+        C: Copy + 'static,
+    {
+        match allocation {
+            ImageAllocation::SingleAllocation(allocation) => {
+                let data = image.data();
+                let buffer = device
+                    .create_buffer_mapped(
+                        data.len(),
+                        wgpu::BufferUsage::COPY_SRC,
+                    )
+                    .fill_from_slice(data);
+
+                if allocation.layer() >= self.texture_array_size as usize {
+                    self.grow(1, device, encoder);
+                }
+
+                self.upload_texture(
+                    &buffer,
+                    allocation,
+                    encoder,
+                );
+            }
+            ImageAllocation::MultipleAllocations { mappings, .. } => {
+                let chunks_per_pixel = 4 / std::mem::size_of::<C>();
+                let chunks_per_line = chunks_per_pixel * image.width() as usize;
+
+                for mapping in mappings {
+                    let sub_width = mapping.allocation.size().0 as usize;
+                    let sub_height = mapping.allocation.size().1 as usize;
+                    let sub_line_start = mapping.src_pos.0 as usize * chunks_per_pixel;
+                    let sub_line_end = (mapping.src_pos.0 as usize + sub_width) * chunks_per_pixel;
+
+                    let mut sub_lines = image
+                        .data()
+                        .chunks(chunks_per_line)
+                        .skip(mapping.src_pos.1 as usize)
+                        .take(sub_height)
+                        .map(|line| &line[sub_line_start..sub_line_end]);
+
+                    let buffer = device
+                        .create_buffer_mapped(
+                            chunks_per_pixel * sub_width * sub_height,
+                            wgpu::BufferUsage::COPY_SRC,
+                        );
+
+                    let mut buffer_lines = buffer.data.chunks_mut(sub_width * chunks_per_pixel);
+
+                    while let (Some(buffer_line), Some(sub_line)) = (buffer_lines.next(), sub_lines.next()) {
+                        buffer_line.copy_from_slice(sub_line);
+                    }
+
+                    let highest_layer = mappings
+                        .iter()
+                        .map(|m| m.allocation.layer() as u32)
+                        .max()
+                        .unwrap_or(0);
+
+                    if highest_layer >= self.texture_array_size {
+                        let grow_by = 1 + highest_layer - self.texture_array_size;
+                        self.grow(grow_by, device, encoder);
+                    }
+
+                    self.upload_texture(
+                        &buffer.finish(),
+                        &mapping.allocation,
+                        encoder,
+                    );
+                }
+            }
+            _ => {}
+        }
+
+    }
+
+    fn upload_texture(
+        &mut self,
+        buffer: &wgpu::Buffer,
+        allocation: &ArrayAllocation,
+        encoder: &mut wgpu::CommandEncoder,
     ) {
-        let size = allocation.rectangle.size();
-        let (width, height) = (size.width as u32, size.height as u32);
+        let array_layer = allocation.layer() as u32;
+
+        let (width, height) = allocation.size();
 
         let extent = wgpu::Extent3d {
             width,
@@ -507,27 +816,22 @@ impl AtlasArray {
             depth: 1,
         };
 
-        let temp_buf = device
-            .create_buffer_mapped(
-                data.len(),
-                wgpu::BufferUsage::COPY_SRC,
-            )
-            .fill_from_slice(data);
+        let (x, y) = allocation.position();
 
         encoder.copy_buffer_to_texture(
             wgpu::BufferCopyView {
-                buffer: &temp_buf,
+                buffer,
                 offset: 0,
                 row_pitch: 4 * width,
                 image_height: height,
             },
             wgpu::TextureCopyView {
                 texture: &self.texture,
-                array_layer: layer as u32,
+                array_layer,
                 mip_level: 0,
                 origin: wgpu::Origin3d {
-                    x: allocation.rectangle.min.x as f32,
-                    y: allocation.rectangle.min.y as f32,
+                    x: x as f32,
+                    y: y as f32,
                     z: 0.0,
                 },
             },
@@ -535,13 +839,17 @@ impl AtlasArray {
         );
     }
 
-    pub fn grow(
+    fn grow(
         &mut self,
         grow_by: u32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
     ) {
-        let old_atlas_array_size = self.size;
+        if grow_by == 0 {
+            return;
+        }
+
+        let old_texture_array_size = self.texture_array_size;
 
         let new_texture = device.create_texture(&wgpu::TextureDescriptor {
             size: wgpu::Extent3d {
@@ -549,7 +857,7 @@ impl AtlasArray {
                 height: ATLAS_SIZE,
                 depth: 1,
             },
-            array_layer_count: old_atlas_array_size + grow_by,
+            array_layer_count: old_texture_array_size + grow_by,
             mip_level_count: 1,
             sample_count: 1,
             dimension: wgpu::TextureDimension::D2,
@@ -559,40 +867,81 @@ impl AtlasArray {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        for i in 0..old_atlas_array_size {
-            encoder.copy_texture_to_texture(
-                wgpu::TextureCopyView {
-                    texture: &self.texture,
-                    array_layer: i,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
+        encoder.copy_texture_to_texture(
+            wgpu::TextureCopyView {
+                texture: &self.texture,
+                array_layer: 0,
+                mip_level: 0,
+                origin: wgpu::Origin3d {
+                    x: 0.0,
+                    y: 0.0,
+                    z: 0.0,
                 },
-                wgpu::TextureCopyView {
-                    texture: &new_texture,
-                    array_layer: i,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
+            },
+            wgpu::TextureCopyView {
+                texture: &new_texture,
+                array_layer: 0,
+                mip_level: 0,
+                origin: wgpu::Origin3d {
+                    x: 0.0,
+                    y: 0.0,
+                    z: 0.0,
                 },
-                wgpu::Extent3d {
-                    width: ATLAS_SIZE,
-                    height: ATLAS_SIZE,
-                    depth: 1,
-                }
-            );
-        }
+            },
+            wgpu::Extent3d {
+                width: ATLAS_SIZE,
+                height: ATLAS_SIZE,
+                depth: self.texture_array_size,
+            }
+        );
 
+        self.texture_array_size += grow_by;
         self.texture = new_texture;
     }
 }
 
+trait RawImageData {
+    type Chunk;
+
+    fn data(&self) -> &[Self::Chunk];
+    fn width(&self) -> u32;
+    fn height(&self) -> u32;
+}
+
+#[cfg(feature = "image")]
+impl RawImageData for ::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>> {
+    type Chunk = u8;
+
+    fn data(&self) -> &[Self::Chunk] {
+        &self
+    }
+
+    fn width(&self) -> u32 {
+        self.dimensions().0
+    }
+
+    fn height(&self) -> u32 {
+        self.dimensions().1
+    }
+}
+
+#[cfg(feature = "svg")]
+impl RawImageData for resvg::raqote::DrawTarget {
+    type Chunk = u32;
+
+    fn data(&self) -> &[Self::Chunk] {
+        self.get_data()
+    }
+
+    fn width(&self) -> u32 {
+        self.width() as u32
+    }
+
+    fn height(&self) -> u32 {
+        self.height() as u32
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct Vertex {
@@ -616,7 +965,7 @@ const QUAD_VERTS: [Vertex; 4] = [
     },
 ];
 
-const ATLAS_SIZE: u32 = 8192;
+const ATLAS_SIZE: u32 = 4096;
 
 #[repr(C)]
 #[derive(Clone, Copy)]
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 32afb749..387bd23a 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,19 +1,15 @@
-use crate::image::AtlasArray;
+use crate::image::{TextureArray, ImageAllocation};
 use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::{Allocation, Size};
+use guillotiere::Size;
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device {
-        layer: u32,
-        #[debug_stub="ReplacementValue"]
-        allocation: Allocation,
-    },
+    Device(ImageAllocation),
     NotFound,
     Invalid,
 }
@@ -22,10 +18,7 @@ impl Memory {
     pub fn dimensions(&self) -> (u32, u32) {
         match self {
             Memory::Host(image) => image.dimensions(),
-            Memory::Device { allocation, .. } => {
-                let size = &allocation.rectangle.size();
-                (size.width as u32, size.height as u32)
-            },
+            Memory::Device(allocation) => allocation.size(),
             Memory::NotFound => (1, 1),
             Memory::Invalid => (1, 1),
         }
@@ -77,7 +70,7 @@ impl Cache {
         handle: &image::Handle,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        atlas_array: &mut AtlasArray,
+        atlas_array: &mut TextureArray,
     ) -> &Memory {
         let _ = self.load(handle);
 
@@ -87,29 +80,23 @@ impl Cache {
             let (width, height) = image.dimensions();
             let size = Size::new(width as i32, height as i32);
 
-            let (layer, allocation) = atlas_array.allocate(size).unwrap_or_else(|| {
-                atlas_array.grow(1, device, encoder);
-                atlas_array.allocate(size).unwrap()
-            });
+            let allocation = atlas_array.allocate(size);
 
-            let flat_samples = image.as_flat_samples();
-            let slice = flat_samples.as_slice();
+            atlas_array.upload(image, &allocation, device, encoder);
 
-            atlas_array.upload(slice, layer, &allocation, device, encoder);
-
-            *memory = Memory::Device { layer, allocation };
+            *memory = Memory::Device(allocation);
         }
 
         memory
     }
 
-    pub fn trim(&mut self, atlas_array: &mut AtlasArray) {
+    pub fn trim(&mut self, texture_array: &mut TextureArray) {
         let hits = &self.hits;
 
         for (id, mem) in &self.map {
-            if let Memory::Device { layer, allocation } = mem {
+            if let Memory::Device(allocation) = mem {
                 if !hits.contains(&id) {
-                    atlas_array.deallocate(*layer, allocation);
+                    texture_array.deallocate(allocation);
                 }
             }
         }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 2972bc4a..a83d1a0a 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,9 +1,9 @@
-use crate::image::AtlasArray;
+use crate::image::{TextureArray, ImageAllocation};
 use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::{Allocation, Size};
+use guillotiere::Size;
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
@@ -32,7 +32,7 @@ impl Svg {
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
     #[debug_stub="ReplacementValue"]
-    rasterized: HashMap<(u64, u32, u32), (u32, Allocation)>,
+    rasterized: HashMap<(u64, u32, u32), ImageAllocation>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
 }
@@ -70,8 +70,8 @@ impl Cache {
         scale: f32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        atlas_array: &mut AtlasArray,
-    ) -> Option<&(u32, Allocation)> {
+        texture_array: &mut TextureArray,
+    ) -> Option<&ImageAllocation> {
         let id = handle.id();
 
         let (width, height) = (
@@ -100,10 +100,7 @@ impl Cache {
 
                 let size = Size::new(width as i32, height as i32);
 
-                let (layer, allocation) = atlas_array.allocate(size).unwrap_or_else(|| {
-                    atlas_array.grow(1, device, encoder);
-                    atlas_array.allocate(size).unwrap()
-                });
+                let array_allocation = texture_array.allocate(size);
 
                 // TODO: Optimize!
                 // We currently rerasterize the SVG when its size changes. This is slow
@@ -124,15 +121,13 @@ impl Cache {
                     &mut canvas,
                 );
 
-                let slice = canvas.get_data();
-
-                atlas_array.upload(slice, layer, &allocation, device, encoder);
+                texture_array.upload(&canvas, &array_allocation, device, encoder);
 
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert((id, width, height));
                 let _ = self
                     .rasterized
-                    .insert((id, width, height), (layer, allocation));
+                    .insert((id, width, height), array_allocation);
 
                 self.rasterized.get(&(id, width, height))
             }
@@ -140,13 +135,13 @@ impl Cache {
         }
     }
 
-    pub fn trim(&mut self, atlas_array: &mut AtlasArray) {
+    pub fn trim(&mut self, texture_array: &mut TextureArray) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
-        for (k, (layer, allocation)) in &self.rasterized {
+        for (k, allocation) in &self.rasterized {
             if !rasterized_hits.contains(k) {
-                atlas_array.deallocate(*layer, allocation);
+                texture_array.deallocate(allocation);
             }
         }
 
-- 
cgit 


From c0996923c6aab39bb61ca6d3310149c66a73fac8 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Fri, 17 Jan 2020 20:15:20 +0100
Subject: Batch image draw calls into one with multiple instances

---
 wgpu/src/image.rs        | 336 ++++++++++++++++++++++-------------------------
 wgpu/src/image/raster.rs |  12 +-
 wgpu/src/image/vector.rs |  15 +--
 3 files changed, 160 insertions(+), 203 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index f8faa543..9443b876 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -28,7 +28,6 @@ pub struct Pipeline {
     uniforms: wgpu::Buffer,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
-    instances: wgpu::Buffer,
     constants: wgpu::BindGroup,
     texture_layout: wgpu::BindGroupLayout,
     texture_array: TextureArray,
@@ -212,11 +211,6 @@ impl Pipeline {
             .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX)
             .fill_from_slice(&QUAD_INDICES);
 
-        let instances = device.create_buffer(&wgpu::BufferDescriptor {
-            size: mem::size_of::<Instance>() as u64,
-            usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
-        });
-
         let texture_array = TextureArray::new(device);
 
         Pipeline {
@@ -230,7 +224,6 @@ impl Pipeline {
             uniforms: uniforms_buffer,
             vertices,
             indices,
-            instances,
             constants: constant_bind_group,
             texture_layout,
             texture_array,
@@ -257,10 +250,10 @@ impl Pipeline {
         &mut self,
         device: &mut wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        instances: &[Image],
+        images: &[Image],
         transformation: Transformation,
-        _bounds: Rectangle<u32>,
-        _target: &wgpu::TextureView,
+        bounds: Rectangle<u32>,
+        target: &wgpu::TextureView,
         _scale: f32,
     ) {
         let uniforms_buffer = device
@@ -277,7 +270,9 @@ impl Pipeline {
             std::mem::size_of::<Uniforms>() as u64,
         );
 
-        for image in instances {
+        let mut instances: Vec<Instance> = Vec::new();
+
+        for image in images {
             match &image.handle {
                 Handle::Raster(_handle) => {
                     #[cfg(feature = "image")]
@@ -290,13 +285,10 @@ impl Pipeline {
                             encoder,
                             &mut self.texture_array,
                         ) {
-                            self.draw_image(
-                                device,
-                                encoder,
+                            add_instances(
                                 image,
                                 allocation,
-                                _bounds,
-                                _target,
+                                &mut instances,
                             );
                         }
                     }
@@ -315,38 +307,17 @@ impl Pipeline {
                             encoder,
                             &mut self.texture_array,
                         ) {
-                            self.draw_image(
-                                device,
-                                encoder,
+                            add_instances(
                                 image,
                                 allocation,
-                                _bounds,
-                                _target,
+                                &mut instances,
                             );
                         }
                     }
                 }
             }
         }
-    }
-
-    pub fn trim_cache(&mut self) {
-        #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim(&mut self.texture_array);
-
-        #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim(&mut self.texture_array);
-    }
 
-    fn draw_image(
-        &self,
-        device: &mut wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-        image: &Image,
-        allocation: &ImageAllocation,
-        bounds: Rectangle<u32>,
-        target: &wgpu::TextureView,
-    )  {
         let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
             layout: &self.texture_layout,
             bindings: &[wgpu::Binding {
@@ -357,82 +328,10 @@ impl Pipeline {
             }],
         });
 
-        match allocation {
-            ImageAllocation::SingleAllocation(allocation) => {
-                self.draw_allocation(
-                    device,
-                    encoder,
-                    image.position,
-                    image.scale,
-                    allocation,
-                    &texture,
-                    bounds,
-                    target,
-                )
-            }
-            ImageAllocation::MultipleAllocations { mappings, size } => {
-                let scaling_x = image.scale[0] / size.0 as f32;
-                let scaling_y = image.scale[1] / size.1 as f32;
-
-                for mapping in mappings {
-                    let mut position = image.position;
-                    let mut scale = image.scale;
-
-                    position[0] += mapping.src_pos.0 as f32 * scaling_x;
-                    position[1] += mapping.src_pos.1 as f32 * scaling_y;
-                    scale[0] = mapping.allocation.size().0 as f32 * scaling_x;
-                    scale[1] = mapping.allocation.size().1 as f32 * scaling_y;
-
-                    self.draw_allocation(
-                        device,
-                        encoder,
-                        position,
-                        scale,
-                        &mapping.allocation,
-                        &texture,
-                        bounds,
-                        target,
-                    )
-                }
-            }
-            _ => {}
-        }
-    }
-
-    fn draw_allocation(
-        &self,
-        device: &mut wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-        position: [f32; 2],
-        scale: [f32; 2],
-        allocation: &ArrayAllocation,
-        texture: &wgpu::BindGroup,
-        bounds: Rectangle<u32>,
-        target: &wgpu::TextureView,
-    ) {
-        let x = (allocation.position().0 as f32 + 0.5) / (ATLAS_SIZE as f32);
-        let y = (allocation.position().1 as f32 + 0.5) / (ATLAS_SIZE as f32);
-        let w = (allocation.size().0 as f32 - 0.5) / (ATLAS_SIZE as f32);
-        let h = (allocation.size().1 as f32 - 0.5) / (ATLAS_SIZE as f32);
-        let layer = allocation.layer() as f32;
-
-        let instance_buffer = device
-            .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
-            .fill_from_slice(&[Instance {
-                _position: position,
-                _scale: scale,
-                _position_in_atlas: [x, y],
-                _scale_in_atlas: [w, h],
-                _layer: layer,
-            }]);
-
-        encoder.copy_buffer_to_buffer(
-            &instance_buffer,
-            0,
-            &self.instances,
-            0,
-            mem::size_of::<Instance>() as u64,
-        );
+        let instances_buffer = device.create_buffer_mapped(
+            instances.len(),
+            wgpu::BufferUsage::VERTEX,
+        ).fill_from_slice(&instances);
 
         let mut render_pass = encoder.begin_render_pass(
             &wgpu::RenderPassDescriptor {
@@ -460,8 +359,9 @@ impl Pipeline {
         render_pass.set_index_buffer(&self.indices, 0);
         render_pass.set_vertex_buffers(
             0,
-            &[(&self.vertices, 0), (&self.instances, 0)],
+            &[(&self.vertices, 0), (&instances_buffer, 0)],
         );
+
         render_pass.set_scissor_rect(
             bounds.x,
             bounds.y,
@@ -472,9 +372,69 @@ impl Pipeline {
         render_pass.draw_indexed(
             0..QUAD_INDICES.len() as u32,
             0,
-            0..1 as u32,
+            0..instances.len() as u32,
         );
     }
+
+    pub fn trim_cache(&mut self) {
+        #[cfg(feature = "image")]
+        self.raster_cache.borrow_mut().trim(&mut self.texture_array);
+
+        #[cfg(feature = "svg")]
+        self.vector_cache.borrow_mut().trim(&mut self.texture_array);
+    }
+}
+
+fn add_instances(
+    image: &Image,
+    allocation: &ImageAllocation,
+    instances: &mut Vec<Instance>,
+)  {
+    match allocation {
+        ImageAllocation::SingleAllocation(allocation) => {
+            add_instance(image.position, image.scale, allocation, instances);
+        }
+        ImageAllocation::MultipleAllocations { mappings, size } => {
+            let scaling_x = image.scale[0] / size.0 as f32;
+            let scaling_y = image.scale[1] / size.1 as f32;
+
+            for mapping in mappings {
+                let allocation = &mapping.allocation;
+                let mut position = image.position;
+                let mut scale = image.scale;
+
+                position[0] += mapping.src_pos.0 as f32 * scaling_x;
+                position[1] += mapping.src_pos.1 as f32 * scaling_y;
+                scale[0] = allocation.size().0 as f32 * scaling_x;
+                scale[1] = allocation.size().1 as f32 * scaling_y;
+
+                add_instance(position, scale, allocation, instances);
+            }
+        }
+    }
+}
+
+fn add_instance(
+    position: [f32; 2],
+    scale: [f32; 2],
+    allocation: &ArrayAllocation,
+    instances: &mut Vec<Instance>,
+) {
+    let x = (allocation.position().0 as f32 + 0.5) / (ATLAS_SIZE as f32);
+    let y = (allocation.position().1 as f32 + 0.5) / (ATLAS_SIZE as f32);
+    let w = (allocation.size().0 as f32 - 0.5) / (ATLAS_SIZE as f32);
+    let h = (allocation.size().1 as f32 - 0.5) / (ATLAS_SIZE as f32);
+    let layer = allocation.layer() as f32;
+
+    let instance = Instance {
+        _position: position,
+        _scale: scale,
+        _position_in_atlas: [x, y],
+        _scale_in_atlas: [w, h],
+        _layer: layer,
+    };
+
+    instances.push(instance);
 }
 
 pub struct Image {
@@ -501,10 +461,10 @@ pub enum ImageAllocation {
         mappings: Vec<ArrayAllocationMapping>,
         size: (u32, u32),
     },
-    Error,
 }
 
 impl ImageAllocation {
+    #[cfg(feature = "image")]
     pub fn size(&self) -> (u32, u32) {
         match self {
             ImageAllocation::SingleAllocation(allocation) => {
@@ -513,7 +473,6 @@ impl ImageAllocation {
             ImageAllocation::MultipleAllocations { size, .. } => {
                 *size
             }
-            _ => (0, 0)
         }
     }
 }
@@ -522,7 +481,7 @@ impl ImageAllocation {
 pub enum ArrayAllocation {
     AtlasAllocation {
         layer: usize,
-        #[debug_stub = "ReplacementValue"]
+        #[debug_stub = "Allocation"]
         allocation: Allocation,
     },
     WholeLayer {
@@ -563,7 +522,7 @@ impl ArrayAllocation {
 pub enum TextureLayer {
     Whole,
     Atlas(
-        #[debug_stub="ReplacementValue"]
+        #[debug_stub="AtlasAllocator"]
         AtlasAllocator
     ),
     Empty,
@@ -577,7 +536,7 @@ pub struct TextureArray {
 }
 
 impl TextureArray {
-    pub fn new(device: &wgpu::Device) -> Self {
+    fn new(device: &wgpu::Device) -> Self {
         let (width, height) = (ATLAS_SIZE, ATLAS_SIZE);
 
         let extent = wgpu::Extent3d {
@@ -598,32 +557,30 @@ impl TextureArray {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        let size = Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32);
-
         TextureArray {
             texture,
             texture_array_size: 1,
-            layers: vec!(TextureLayer::Atlas(AtlasAllocator::new(size))),
+            layers: vec!(TextureLayer::Empty),
         }
     }
 
-    pub fn allocate(&mut self, size: Size) -> ImageAllocation {
+    fn allocate(&mut self, size: Size) -> Option<ImageAllocation> {
         // Allocate one layer if allocation fits perfectly
         if size.width == ATLAS_SIZE as i32 && size.height == ATLAS_SIZE as i32 {
-            for (i, layer) in &mut self.layers.iter_mut().enumerate() {
+            for (i, layer) in self.layers.iter_mut().enumerate() {
                 if let TextureLayer::Empty = layer
                 {
                     *layer = TextureLayer::Whole;
-                    return ImageAllocation::SingleAllocation(
+                    return Some(ImageAllocation::SingleAllocation(
                         ArrayAllocation::WholeLayer { layer: i }
-                    );
+                    ));
                 }
             }
 
             self.layers.push(TextureLayer::Whole);
-            return ImageAllocation::SingleAllocation(
+            return Some(ImageAllocation::SingleAllocation(
                 ArrayAllocation::WholeLayer { layer: self.layers.len() - 1 }
-            );
+            ));
         }
 
         // Split big allocations across multiple layers
@@ -637,7 +594,11 @@ impl TextureArray {
 
                 while x < size.width {
                     let width = std::cmp::min(size.width - x, ATLAS_SIZE as i32);
-                    if let ImageAllocation::SingleAllocation(allocation) = self.allocate(Size::new(width, height)) {
+                    let allocation = self
+                        .allocate(Size::new(width, height))
+                        .expect("Allocating texture space");
+
+                    if let ImageAllocation::SingleAllocation(allocation) = allocation {
                         let src_pos = (x as u32, y as u32);
                         mappings.push(ArrayAllocationMapping { src_pos, allocation });
                     }
@@ -647,10 +608,10 @@ impl TextureArray {
                 y += height;
             }
 
-            return ImageAllocation::MultipleAllocations {
+            return Some(ImageAllocation::MultipleAllocations {
                 mappings,
                 size: (size.width as u32, size.height as u32),
-            };
+            });
         }
 
         // Try allocating on an existing layer
@@ -658,7 +619,7 @@ impl TextureArray {
             if let TextureLayer::Atlas(allocator) = layer {
                 if let Some(allocation) = allocator.allocate(size.clone()) {
                     let array_allocation = ArrayAllocation::AtlasAllocation { layer: i, allocation };
-                    return ImageAllocation::SingleAllocation(array_allocation);
+                    return Some(ImageAllocation::SingleAllocation(array_allocation));
                 }
             }
         }
@@ -668,19 +629,19 @@ impl TextureArray {
         if let Some(allocation) = allocator.allocate(size) {
             self.layers.push(TextureLayer::Atlas(allocator));
 
-            return ImageAllocation::SingleAllocation(
+            return Some(ImageAllocation::SingleAllocation(
                 ArrayAllocation::AtlasAllocation {
                     layer: self.layers.len() - 1,
                     allocation,
                 }
-            );
+            ));
         }
 
         // One of the above should have worked
-        ImageAllocation::Error
+        None
     }
 
-    pub fn deallocate(&mut self, allocation: &ImageAllocation) {
+    fn deallocate(&mut self, allocation: &ImageAllocation) {
         match allocation {
             ImageAllocation::SingleAllocation(allocation) => {
                 if let Some(layer) = self.layers.get_mut(allocation.layer()) {
@@ -712,7 +673,6 @@ impl TextureArray {
                     }
                 }
             }
-            _ => {}
         }
 
     }
@@ -720,15 +680,17 @@ impl TextureArray {
     fn upload<C, I>(
         &mut self,
         image: &I,
-        allocation: &ImageAllocation,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-    )
+    ) -> ImageAllocation
     where
         I: RawImageData<Chunk = C>,
         C: Copy + 'static,
     {
-        match allocation {
+        let size = Size::new(image.width() as i32, image.height() as i32);
+        let allocation = self.allocate(size).expect("Allocating texture space");
+
+        match &allocation {
             ImageAllocation::SingleAllocation(allocation) => {
                 let data = image.data();
                 let buffer = device
@@ -744,7 +706,7 @@ impl TextureArray {
 
                 self.upload_texture(
                     &buffer,
-                    allocation,
+                    &allocation,
                     encoder,
                 );
             }
@@ -752,6 +714,17 @@ impl TextureArray {
                 let chunks_per_pixel = 4 / std::mem::size_of::<C>();
                 let chunks_per_line = chunks_per_pixel * image.width() as usize;
 
+                let highest_layer = mappings
+                    .iter()
+                    .map(|m| m.allocation.layer() as u32)
+                    .max()
+                    .unwrap_or(0);
+
+                if highest_layer >= self.texture_array_size {
+                    let grow_by = 1 + highest_layer - self.texture_array_size;
+                    self.grow(grow_by, device, encoder);
+                }
+
                 for mapping in mappings {
                     let sub_width = mapping.allocation.size().0 as usize;
                     let sub_height = mapping.allocation.size().1 as usize;
@@ -777,17 +750,6 @@ impl TextureArray {
                         buffer_line.copy_from_slice(sub_line);
                     }
 
-                    let highest_layer = mappings
-                        .iter()
-                        .map(|m| m.allocation.layer() as u32)
-                        .max()
-                        .unwrap_or(0);
-
-                    if highest_layer >= self.texture_array_size {
-                        let grow_by = 1 + highest_layer - self.texture_array_size;
-                        self.grow(grow_by, device, encoder);
-                    }
-
                     self.upload_texture(
                         &buffer.finish(),
                         &mapping.allocation,
@@ -795,9 +757,9 @@ impl TextureArray {
                     );
                 }
             }
-            _ => {}
         }
 
+        allocation
     }
 
     fn upload_texture(
@@ -867,33 +829,43 @@ impl TextureArray {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        encoder.copy_texture_to_texture(
-            wgpu::TextureCopyView {
-                texture: &self.texture,
-                array_layer: 0,
-                mip_level: 0,
-                origin: wgpu::Origin3d {
-                    x: 0.0,
-                    y: 0.0,
-                    z: 0.0,
+        for (i, layer) in self.layers.iter().enumerate() {
+            if i >= old_texture_array_size as usize {
+                break;
+            }
+
+            if let TextureLayer::Empty = layer {
+                continue;
+            }
+
+            encoder.copy_texture_to_texture(
+                wgpu::TextureCopyView {
+                    texture: &self.texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
                 },
-            },
-            wgpu::TextureCopyView {
-                texture: &new_texture,
-                array_layer: 0,
-                mip_level: 0,
-                origin: wgpu::Origin3d {
-                    x: 0.0,
-                    y: 0.0,
-                    z: 0.0,
+                wgpu::TextureCopyView {
+                    texture: &new_texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
                 },
-            },
-            wgpu::Extent3d {
-                width: ATLAS_SIZE,
-                height: ATLAS_SIZE,
-                depth: self.texture_array_size,
-            }
-        );
+                wgpu::Extent3d {
+                    width: ATLAS_SIZE,
+                    height: ATLAS_SIZE,
+                    depth: 1,
+                }
+            );
+        }
 
         self.texture_array_size += grow_by;
         self.texture = new_texture;
@@ -968,7 +940,7 @@ const QUAD_VERTS: [Vertex; 4] = [
 const ATLAS_SIZE: u32 = 4096;
 
 #[repr(C)]
-#[derive(Clone, Copy)]
+#[derive(Debug, Clone, Copy)]
 struct Instance {
     _position: [f32; 2],
     _scale: [f32; 2],
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 387bd23a..bca2ebda 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -3,7 +3,6 @@ use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::Size;
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
@@ -72,17 +71,10 @@ impl Cache {
         encoder: &mut wgpu::CommandEncoder,
         atlas_array: &mut TextureArray,
     ) -> &Memory {
-        let _ = self.load(handle);
-
-        let memory = self.map.get_mut(&handle.id()).unwrap();
+        let memory = self.load(handle);
 
         if let Memory::Host(image) = memory {
-            let (width, height) = image.dimensions();
-            let size = Size::new(width as i32, height as i32);
-
-            let allocation = atlas_array.allocate(size);
-
-            atlas_array.upload(image, &allocation, device, encoder);
+            let allocation = atlas_array.upload(image, device, encoder);
 
             *memory = Memory::Device(allocation);
         }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index a83d1a0a..9bddcc2b 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -3,7 +3,6 @@ use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
 };
-use guillotiere::Size;
 use debug_stub_derive::*;
 
 #[derive(DebugStub)]
@@ -83,25 +82,19 @@ impl Cache {
         // We currently rerasterize the SVG when its size changes. This is slow
         // as heck. A GPU rasterizer like `pathfinder` may perform better.
         // It would be cool to be able to smooth resize the `svg` example.
-        if self.rasterized.get(&(id, width, height)).is_some() {
+        if self.rasterized.contains_key(&(id, width, height)) {
             let _ = self.svg_hits.insert(id);
             let _ = self.rasterized_hits.insert((id, width, height));
 
             return self.rasterized.get(&(id, width, height));
         }
 
-        let _ = self.load(handle);
-
-        match self.svgs.get(&handle.id()).unwrap() {
+        match self.load(handle) {
             Svg::Loaded(tree) => {
                 if width == 0 || height == 0 {
                     return None;
                 }
 
-                let size = Size::new(width as i32, height as i32);
-
-                let array_allocation = texture_array.allocate(size);
-
                 // TODO: Optimize!
                 // We currently rerasterize the SVG when its size changes. This is slow
                 // as heck. A GPU rasterizer like `pathfinder` may perform better.
@@ -121,13 +114,13 @@ impl Cache {
                     &mut canvas,
                 );
 
-                texture_array.upload(&canvas, &array_allocation, device, encoder);
+                let allocation = texture_array.upload(&canvas, device, encoder);
 
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert((id, width, height));
                 let _ = self
                     .rasterized
-                    .insert((id, width, height), array_allocation);
+                    .insert((id, width, height), allocation);
 
                 self.rasterized.get(&(id, width, height))
             }
-- 
cgit 


From 2f695ef9803c9c08f64961f1b9902a661a385160 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Fri, 17 Jan 2020 20:48:49 +0100
Subject: Updated shaders and removed debug_stub_derive dependency

---
 wgpu/Cargo.toml                |   1 -
 wgpu/src/image.rs              |  32 ++++++++++++++++++++++++--------
 wgpu/src/image/raster.rs       |  13 +++++++++++--
 wgpu/src/image/vector.rs       |  24 ++++++++++++++++--------
 wgpu/src/shader/image.frag.spv | Bin 684 -> 584 bytes
 wgpu/src/shader/image.vert.spv | Bin 2324 -> 1596 bytes
 6 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index f8b5bb8c..56839cf0 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -22,7 +22,6 @@ glam = "0.8"
 font-kit = "0.4"
 log = "0.4"
 guillotiere = "0.4"
-debug_stub_derive = "0.3"
 
 [dependencies.image]
 version = "0.22"
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 9443b876..2fd73b54 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -15,7 +15,6 @@ use std::mem;
 use std::cell::RefCell;
 
 use guillotiere::{Allocation, AtlasAllocator, Size};
-use debug_stub_derive::*;
 
 #[derive(Debug)]
 pub struct Pipeline {
@@ -477,11 +476,9 @@ impl ImageAllocation {
     }
 }
 
-#[derive(DebugStub)]
 pub enum ArrayAllocation {
     AtlasAllocation {
         layer: usize,
-        #[debug_stub = "Allocation"]
         allocation: Allocation,
     },
     WholeLayer {
@@ -518,16 +515,35 @@ impl ArrayAllocation {
     }
 }
 
-#[derive(DebugStub)]
+impl std::fmt::Debug for ArrayAllocation {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ArrayAllocation::AtlasAllocation { layer, .. } => {
+                write!(f, "ArrayAllocation::AtlasAllocation {{ layer: {} }}", layer)
+            },
+            ArrayAllocation::WholeLayer { layer } => {
+                write!(f, "ArrayAllocation::WholeLayer {{ layer: {} }}", layer)
+            }
+        }
+    }
+}
+
 pub enum TextureLayer {
     Whole,
-    Atlas(
-        #[debug_stub="AtlasAllocator"]
-        AtlasAllocator
-    ),
+    Atlas(AtlasAllocator),
     Empty,
 }
 
+impl std::fmt::Debug for TextureLayer {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            TextureLayer::Whole => write!(f, "TextureLayer::Whole"),
+            TextureLayer::Atlas(_) => write!(f, "TextureLayer::Atlas"),
+            TextureLayer::Empty => write!(f, "TextureLayer::Empty"),
+        }
+    }
+}
+
 #[derive(Debug)]
 pub struct TextureArray {
     texture: wgpu::Texture,
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index bca2ebda..648df0ff 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -3,9 +3,7 @@ use iced_native::image;
 use std::{
     collections::{HashMap, HashSet},
 };
-use debug_stub_derive::*;
 
-#[derive(DebugStub)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
     Device(ImageAllocation),
@@ -24,6 +22,17 @@ impl Memory {
     }
 }
 
+impl std::fmt::Debug for Memory {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Memory::Host(_) => write!(f, "Memory::Host"),
+            Memory::Device(_) => write!(f, "Memory::Device"),
+            Memory::NotFound => write!(f, "Memory::NotFound"),
+            Memory::Invalid => write!(f, "Memory::Invalid"),
+        }
+    }
+}
+
 #[derive(Debug)]
 pub struct Cache {
     map: HashMap<u64, Memory>,
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 9bddcc2b..a8746566 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -3,14 +3,9 @@ use iced_native::svg;
 use std::{
     collections::{HashMap, HashSet},
 };
-use debug_stub_derive::*;
 
-#[derive(DebugStub)]
 pub enum Svg {
-    Loaded(
-        #[debug_stub="ReplacementValue"]
-        resvg::usvg::Tree
-    ),
+    Loaded(resvg::usvg::Tree),
     NotFound,
 }
 
@@ -27,10 +22,17 @@ impl Svg {
     }
 }
 
-#[derive(DebugStub)]
+impl std::fmt::Debug for Svg {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Svg::Loaded(_) => write!(f, "Svg::Loaded"),
+            Svg::NotFound => write!(f, "Svg::NotFound"),
+        }
+    }
+}
+
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
-    #[debug_stub="ReplacementValue"]
     rasterized: HashMap<(u64, u32, u32), ImageAllocation>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
@@ -144,3 +146,9 @@ impl Cache {
         self.rasterized_hits.clear();
     }
 }
+
+impl std::fmt::Debug for Cache {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "vector::Cache")
+    }
+}
\ No newline at end of file
diff --git a/wgpu/src/shader/image.frag.spv b/wgpu/src/shader/image.frag.spv
index ebee82ac..6a6445b4 100644
Binary files a/wgpu/src/shader/image.frag.spv and b/wgpu/src/shader/image.frag.spv differ
diff --git a/wgpu/src/shader/image.vert.spv b/wgpu/src/shader/image.vert.spv
index da76eca0..cceff73a 100644
Binary files a/wgpu/src/shader/image.vert.spv and b/wgpu/src/shader/image.vert.spv differ
-- 
cgit 


From 8f9f44b9e8ff1f1629d2b19edd2ecdad79e80836 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Sat, 18 Jan 2020 12:49:11 +0100
Subject: When deallocating the last allocation in an allocator mark its layer
 as empty

---
 wgpu/src/image.rs | 47 +++++++++++++++++++++++------------------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 2fd73b54..97e30403 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -660,37 +660,36 @@ impl TextureArray {
     fn deallocate(&mut self, allocation: &ImageAllocation) {
         match allocation {
             ImageAllocation::SingleAllocation(allocation) => {
-                if let Some(layer) = self.layers.get_mut(allocation.layer()) {
-                    match allocation {
-                        ArrayAllocation::WholeLayer { .. } => {
-                            *layer = TextureLayer::Empty;
-                        }
-                        ArrayAllocation::AtlasAllocation { allocation, .. } => {
-                            if let TextureLayer::Atlas(allocator) = layer {
-                                allocator.deallocate(allocation.id);
-                            }
-                        }
-                    }
-                }
+                self.deallocate_single_allocation(allocation);
             }
             ImageAllocation::MultipleAllocations { mappings, .. } => {
                 for mapping in mappings {
-                    if let Some(layer) = self.layers.get_mut(mapping.allocation.layer()) {
-                        match &mapping.allocation {
-                            ArrayAllocation::WholeLayer { .. } => {
-                                *layer = TextureLayer::Empty;
-                            }
-                            ArrayAllocation::AtlasAllocation { allocation, .. } => {
-                                if let TextureLayer::Atlas(allocator) = layer {
-                                    allocator.deallocate(allocation.id);
-                                }
-                            }
+                    self.deallocate_single_allocation(&mapping.allocation);
+                }
+            }
+        }
+    }
+
+    fn deallocate_single_allocation(&mut self, allocation: &ArrayAllocation) {
+        if let Some(layer) = self.layers.get_mut(allocation.layer()) {
+            match allocation {
+                ArrayAllocation::WholeLayer { .. } => {
+                    *layer = TextureLayer::Empty;
+                }
+                ArrayAllocation::AtlasAllocation { allocation, .. } => {
+                    if let TextureLayer::Atlas(allocator) = layer {
+                        allocator.deallocate(allocation.id);
+
+                        let mut empty_allocator = true;
+                        allocator.for_each_allocated_rectangle(|_, _| empty_allocator = false);
+
+                        if empty_allocator {
+                            *layer = TextureLayer::Empty;
                         }
                     }
                 }
             }
         }
-
     }
 
     fn upload<C, I>(
@@ -953,7 +952,7 @@ const QUAD_VERTS: [Vertex; 4] = [
     },
 ];
 
-const ATLAS_SIZE: u32 = 4096;
+const ATLAS_SIZE: u32 = 256;
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
-- 
cgit 


From 4617da2818eb3ecc17b1da9571b7baa15056c026 Mon Sep 17 00:00:00 2001
From: Malte Veerman <malte.veerman@gmail.com>
Date: Sun, 19 Jan 2020 18:06:46 +0100
Subject: Implemented automatic deallocation of texture space for dropped
 allocations

---
 wgpu/src/image.rs        | 155 ++++++++++++++++++++++++++---------------------
 wgpu/src/image/raster.rs |  10 +--
 wgpu/src/image/vector.rs |   8 +--
 3 files changed, 88 insertions(+), 85 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 97e30403..7eaa1ae8 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -9,10 +9,7 @@ use crate::image::raster::Memory;
 use crate::Transformation;
 use iced_native::{image, svg, Rectangle};
 
-use std::mem;
-
-#[cfg(any(feature = "image", feature = "svg"))]
-use std::cell::RefCell;
+use std::{cell::RefCell, mem, rc::Rc};
 
 use guillotiere::{Allocation, AtlasAllocator, Size};
 
@@ -377,10 +374,10 @@ impl Pipeline {
 
     pub fn trim_cache(&mut self) {
         #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim(&mut self.texture_array);
+        self.raster_cache.borrow_mut().trim();
 
         #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim(&mut self.texture_array);
+        self.vector_cache.borrow_mut().trim();
     }
 }
 
@@ -423,14 +420,14 @@ fn add_instance(
     let y = (allocation.position().1 as f32 + 0.5) / (ATLAS_SIZE as f32);
     let w = (allocation.size().0 as f32 - 0.5) / (ATLAS_SIZE as f32);
     let h = (allocation.size().1 as f32 - 0.5) / (ATLAS_SIZE as f32);
-    let layer = allocation.layer() as f32;
+    let layer_index = allocation.layer_index() as f32;
 
     let instance = Instance {
         _position: position,
         _scale: scale,
         _position_in_atlas: [x, y],
         _scale_in_atlas: [w, h],
-        _layer: layer,
+        _layer: layer_index,
     };
 
     instances.push(instance);
@@ -478,11 +475,13 @@ impl ImageAllocation {
 
 pub enum ArrayAllocation {
     AtlasAllocation {
-        layer: usize,
+        layer_index: usize,
+        layer: Rc<RefCell<TextureLayer>>,
         allocation: Allocation,
     },
     WholeLayer {
-        layer: usize,
+        layer_index: usize,
+        layer: Rc<RefCell<TextureLayer>>,
     }
 }
 
@@ -507,10 +506,10 @@ impl ArrayAllocation {
         }
     }
 
-    pub fn layer(&self) -> usize {
+    pub fn layer_index(&self) -> usize {
         match self {
-            ArrayAllocation::AtlasAllocation { layer, .. } => *layer,
-            ArrayAllocation::WholeLayer { layer } => *layer,
+            ArrayAllocation::AtlasAllocation { layer_index, .. } => *layer_index,
+            ArrayAllocation::WholeLayer { layer_index, .. } => *layer_index,
         }
     }
 }
@@ -518,11 +517,34 @@ impl ArrayAllocation {
 impl std::fmt::Debug for ArrayAllocation {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            ArrayAllocation::AtlasAllocation { layer, .. } => {
-                write!(f, "ArrayAllocation::AtlasAllocation {{ layer: {} }}", layer)
+            ArrayAllocation::AtlasAllocation { layer_index, .. } => {
+                write!(f, "ArrayAllocation::AtlasAllocation {{ layer_index: {:} }}", layer_index)
             },
-            ArrayAllocation::WholeLayer { layer } => {
-                write!(f, "ArrayAllocation::WholeLayer {{ layer: {} }}", layer)
+            ArrayAllocation::WholeLayer { layer_index, .. } => {
+                write!(f, "ArrayAllocation::WholeLayer {{ layer_index: {} }}", layer_index)
+            }
+        }
+    }
+}
+
+impl Drop for ArrayAllocation {
+    fn drop(&mut self) {
+        match self {
+            ArrayAllocation::WholeLayer { layer, .. } => {
+                let _ = layer.replace(TextureLayer::Whole);
+            }
+            ArrayAllocation::AtlasAllocation { allocation, layer, .. } => {
+                let mut layer = layer.borrow_mut();
+                if let Some(allocator) = layer.allocator_mut() {
+                    allocator.deallocate(allocation.id);
+
+                    let mut empty_allocator = true;
+                    allocator.for_each_allocated_rectangle(|_, _| empty_allocator = false);
+
+                    if empty_allocator {
+                        *layer = TextureLayer::Empty;
+                    }
+                }
             }
         }
     }
@@ -534,6 +556,23 @@ pub enum TextureLayer {
     Empty,
 }
 
+impl TextureLayer {
+    pub fn is_empty(&self) -> bool {
+        if let TextureLayer::Empty = self {
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn allocator_mut(&mut self) -> Option<&mut AtlasAllocator> {
+        match self {
+            TextureLayer::Atlas(allocator) => Some(allocator),
+            _ => None
+        }
+    }
+}
+
 impl std::fmt::Debug for TextureLayer {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
@@ -544,11 +583,17 @@ impl std::fmt::Debug for TextureLayer {
     }
 }
 
+impl From<AtlasAllocator> for TextureLayer {
+    fn from(allocator: AtlasAllocator) -> Self {
+        TextureLayer::Atlas(allocator)
+    }
+}
+
 #[derive(Debug)]
 pub struct TextureArray {
     texture: wgpu::Texture,
     texture_array_size: u32,
-    layers: Vec<TextureLayer>,
+    layers: Vec<Rc<RefCell<TextureLayer>>>,
 }
 
 impl TextureArray {
@@ -576,7 +621,7 @@ impl TextureArray {
         TextureArray {
             texture,
             texture_array_size: 1,
-            layers: vec!(TextureLayer::Empty),
+            layers: vec!(Rc::new(RefCell::new(TextureLayer::Empty))),
         }
     }
 
@@ -584,18 +629,19 @@ impl TextureArray {
         // Allocate one layer if allocation fits perfectly
         if size.width == ATLAS_SIZE as i32 && size.height == ATLAS_SIZE as i32 {
             for (i, layer) in self.layers.iter_mut().enumerate() {
-                if let TextureLayer::Empty = layer
+                if layer.borrow().is_empty()
                 {
-                    *layer = TextureLayer::Whole;
+                    let _ = layer.replace(TextureLayer::Whole);
                     return Some(ImageAllocation::SingleAllocation(
-                        ArrayAllocation::WholeLayer { layer: i }
+                        ArrayAllocation::WholeLayer { layer: layer.clone(), layer_index: i }
                     ));
                 }
             }
 
-            self.layers.push(TextureLayer::Whole);
+            let layer = Rc::new(RefCell::new(TextureLayer::Whole));
+            self.layers.push(layer.clone());
             return Some(ImageAllocation::SingleAllocation(
-                ArrayAllocation::WholeLayer { layer: self.layers.len() - 1 }
+                ArrayAllocation::WholeLayer { layer, layer_index: self.layers.len() - 1 }
             ));
         }
 
@@ -632,9 +678,13 @@ impl TextureArray {
 
         // Try allocating on an existing layer
         for (i, layer) in self.layers.iter_mut().enumerate() {
-            if let TextureLayer::Atlas(allocator) = layer {
+            if let Some(allocator) = layer.borrow_mut().allocator_mut() {
                 if let Some(allocation) = allocator.allocate(size.clone()) {
-                    let array_allocation = ArrayAllocation::AtlasAllocation { layer: i, allocation };
+                    let array_allocation = ArrayAllocation::AtlasAllocation {
+                        layer: layer.clone(),
+                        layer_index: i,
+                        allocation
+                    };
                     return Some(ImageAllocation::SingleAllocation(array_allocation));
                 }
             }
@@ -643,11 +693,13 @@ impl TextureArray {
         // Create new layer with atlas allocator
         let mut allocator = AtlasAllocator::new(Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32));
         if let Some(allocation) = allocator.allocate(size) {
-            self.layers.push(TextureLayer::Atlas(allocator));
+            let layer = Rc::new(RefCell::new(allocator.into()));
+            self.layers.push(layer.clone());
 
             return Some(ImageAllocation::SingleAllocation(
                 ArrayAllocation::AtlasAllocation {
-                    layer: self.layers.len() - 1,
+                    layer,
+                    layer_index: self.layers.len() - 1,
                     allocation,
                 }
             ));
@@ -657,41 +709,6 @@ impl TextureArray {
         None
     }
 
-    fn deallocate(&mut self, allocation: &ImageAllocation) {
-        match allocation {
-            ImageAllocation::SingleAllocation(allocation) => {
-                self.deallocate_single_allocation(allocation);
-            }
-            ImageAllocation::MultipleAllocations { mappings, .. } => {
-                for mapping in mappings {
-                    self.deallocate_single_allocation(&mapping.allocation);
-                }
-            }
-        }
-    }
-
-    fn deallocate_single_allocation(&mut self, allocation: &ArrayAllocation) {
-        if let Some(layer) = self.layers.get_mut(allocation.layer()) {
-            match allocation {
-                ArrayAllocation::WholeLayer { .. } => {
-                    *layer = TextureLayer::Empty;
-                }
-                ArrayAllocation::AtlasAllocation { allocation, .. } => {
-                    if let TextureLayer::Atlas(allocator) = layer {
-                        allocator.deallocate(allocation.id);
-
-                        let mut empty_allocator = true;
-                        allocator.for_each_allocated_rectangle(|_, _| empty_allocator = false);
-
-                        if empty_allocator {
-                            *layer = TextureLayer::Empty;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     fn upload<C, I>(
         &mut self,
         image: &I,
@@ -715,7 +732,7 @@ impl TextureArray {
                     )
                     .fill_from_slice(data);
 
-                if allocation.layer() >= self.texture_array_size as usize {
+                if allocation.layer_index() >= self.texture_array_size as usize {
                     self.grow(1, device, encoder);
                 }
 
@@ -731,7 +748,7 @@ impl TextureArray {
 
                 let highest_layer = mappings
                     .iter()
-                    .map(|m| m.allocation.layer() as u32)
+                    .map(|m| m.allocation.layer_index() as u32)
                     .max()
                     .unwrap_or(0);
 
@@ -783,7 +800,7 @@ impl TextureArray {
         allocation: &ArrayAllocation,
         encoder: &mut wgpu::CommandEncoder,
     ) {
-        let array_layer = allocation.layer() as u32;
+        let array_layer = allocation.layer_index() as u32;
 
         let (width, height) = allocation.size();
 
@@ -844,12 +861,12 @@ impl TextureArray {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        for (i, layer) in self.layers.iter().enumerate() {
+        for (i, layer) in self.layers.iter_mut().enumerate() {
             if i >= old_texture_array_size as usize {
                 break;
             }
 
-            if let TextureLayer::Empty = layer {
+            if layer.borrow().is_empty() {
                 continue;
             }
 
@@ -952,7 +969,7 @@ const QUAD_VERTS: [Vertex; 4] = [
     },
 ];
 
-const ATLAS_SIZE: u32 = 256;
+const ATLAS_SIZE: u32 = 4096;
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 648df0ff..884dd65a 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -91,17 +91,9 @@ impl Cache {
         memory
     }
 
-    pub fn trim(&mut self, texture_array: &mut TextureArray) {
+    pub fn trim(&mut self) {
         let hits = &self.hits;
 
-        for (id, mem) in &self.map {
-            if let Memory::Device(allocation) = mem {
-                if !hits.contains(&id) {
-                    texture_array.deallocate(allocation);
-                }
-            }
-        }
-
         self.map.retain(|k, _| hits.contains(k));
         self.hits.clear();
     }
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index a8746566..98588e5c 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -130,16 +130,10 @@ impl Cache {
         }
     }
 
-    pub fn trim(&mut self, texture_array: &mut TextureArray) {
+    pub fn trim(&mut self) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
-        for (k, allocation) in &self.rasterized {
-            if !rasterized_hits.contains(k) {
-                texture_array.deallocate(allocation);
-            }
-        }
-
         self.svgs.retain(|k, _| svg_hits.contains(k));
         self.rasterized.retain(|k, _| rasterized_hits.contains(k));
         self.svg_hits.clear();
-- 
cgit 


From 82f0a49062d10f3cbf202a5379c061a2509ec97b Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Tue, 25 Feb 2020 13:27:43 +0100
Subject: Recompile `image` shaders

---
 wgpu/src/shader/image.frag.spv | Bin 584 -> 684 bytes
 wgpu/src/shader/image.vert.spv | Bin 1596 -> 2472 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)

diff --git a/wgpu/src/shader/image.frag.spv b/wgpu/src/shader/image.frag.spv
index 6a6445b4..65b08aa3 100644
Binary files a/wgpu/src/shader/image.frag.spv and b/wgpu/src/shader/image.frag.spv differ
diff --git a/wgpu/src/shader/image.vert.spv b/wgpu/src/shader/image.vert.spv
index cceff73a..192bf6c3 100644
Binary files a/wgpu/src/shader/image.vert.spv and b/wgpu/src/shader/image.vert.spv differ
-- 
cgit 


From 59d45a5440aaa46c7dc8f3dc70c8518167c10418 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 12:34:34 +0100
Subject: Refactor texture atlas

- Split into multiple modules
- Rename some concepts
- Change API details
---
 wgpu/src/image.rs                    | 768 +++++++----------------------------
 wgpu/src/image/raster.rs             |  27 +-
 wgpu/src/image/vector.rs             |  34 +-
 wgpu/src/lib.rs                      |   3 +-
 wgpu/src/renderer.rs                 |   4 +-
 wgpu/src/texture.rs                  |   3 +
 wgpu/src/texture/atlas.rs            | 313 ++++++++++++++
 wgpu/src/texture/atlas/allocation.rs |  35 ++
 wgpu/src/texture/atlas/allocator.rs  |  57 +++
 wgpu/src/texture/atlas/entry.rs      |  25 ++
 wgpu/src/texture/atlas/layer.rs      |  17 +
 11 files changed, 633 insertions(+), 653 deletions(-)
 create mode 100644 wgpu/src/texture.rs
 create mode 100644 wgpu/src/texture/atlas.rs
 create mode 100644 wgpu/src/texture/atlas/allocation.rs
 create mode 100644 wgpu/src/texture/atlas/allocator.rs
 create mode 100644 wgpu/src/texture/atlas/entry.rs
 create mode 100644 wgpu/src/texture/atlas/layer.rs

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 7eaa1ae8..e14b3024 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -3,15 +3,13 @@ mod raster;
 #[cfg(feature = "svg")]
 mod vector;
 
-#[cfg(feature = "image")]
-use crate::image::raster::Memory;
+use crate::{
+    texture::{self, atlas},
+    Transformation,
+};
 
-use crate::Transformation;
 use iced_native::{image, svg, Rectangle};
-
-use std::{cell::RefCell, mem, rc::Rc};
-
-use guillotiere::{Allocation, AtlasAllocator, Size};
+use std::{cell::RefCell, mem};
 
 #[derive(Debug)]
 pub struct Pipeline {
@@ -25,8 +23,10 @@ pub struct Pipeline {
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
     constants: wgpu::BindGroup,
+    texture: wgpu::BindGroup,
+    texture_version: usize,
     texture_layout: wgpu::BindGroupLayout,
-    texture_array: TextureArray,
+    texture_atlas: texture::Atlas,
 }
 
 impl Pipeline {
@@ -207,7 +207,17 @@ impl Pipeline {
             .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX)
             .fill_from_slice(&QUAD_INDICES);
 
-        let texture_array = TextureArray::new(device);
+        let texture_atlas = texture::Atlas::new(device);
+
+        let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
+            layout: &texture_layout,
+            bindings: &[wgpu::Binding {
+                binding: 0,
+                resource: wgpu::BindingResource::TextureView(
+                    &texture_atlas.view(),
+                ),
+            }],
+        });
 
         Pipeline {
             #[cfg(feature = "image")]
@@ -221,8 +231,10 @@ impl Pipeline {
             vertices,
             indices,
             constants: constant_bind_group,
+            texture,
+            texture_version: texture_atlas.layer_count(),
             texture_layout,
-            texture_array,
+            texture_atlas,
         }
     }
 
@@ -252,85 +264,88 @@ impl Pipeline {
         target: &wgpu::TextureView,
         _scale: f32,
     ) {
-        let uniforms_buffer = device
-            .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
-            .fill_from_slice(&[Uniforms {
-                transform: transformation.into(),
-            }]);
+        let mut instances: Vec<Instance> = Vec::new();
 
-        encoder.copy_buffer_to_buffer(
-            &uniforms_buffer,
-            0,
-            &self.uniforms,
-            0,
-            std::mem::size_of::<Uniforms>() as u64,
-        );
+        #[cfg(feature = "image")]
+        let mut raster_cache = self.raster_cache.borrow_mut();
 
-        let mut instances: Vec<Instance> = Vec::new();
+        #[cfg(feature = "svg")]
+        let mut vector_cache = self.vector_cache.borrow_mut();
 
         for image in images {
             match &image.handle {
                 Handle::Raster(_handle) => {
                     #[cfg(feature = "image")]
                     {
-                        let mut raster_cache = self.raster_cache.borrow_mut();
-
-                        if let Memory::Device(allocation) = raster_cache.upload(
+                        if let Some(atlas_entry) = raster_cache.upload(
                             _handle,
                             device,
                             encoder,
-                            &mut self.texture_array,
+                            &mut self.texture_atlas,
                         ) {
-                            add_instances(
-                                image,
-                                allocation,
-                                &mut instances,
-                            );
+                            add_instances(image, atlas_entry, &mut instances);
                         }
-                    }
+                    };
                 }
                 Handle::Vector(_handle) => {
                     #[cfg(feature = "svg")]
                     {
-                        let mut vector_cache = self.vector_cache.borrow_mut();
-
-                        // Upload rasterized svg to texture atlas
-                        if let Some(allocation) = vector_cache.upload(
+                        if let Some(atlas_entry) = vector_cache.upload(
                             _handle,
-                            image.scale,
+                            image.size,
                             _scale,
                             device,
                             encoder,
-                            &mut self.texture_array,
+                            &mut self.texture_atlas,
                         ) {
-                            add_instances(
-                                image,
-                                allocation,
-                                &mut instances,
-                            );
+                            add_instances(image, atlas_entry, &mut instances);
                         }
-                    }
+                    };
                 }
             }
         }
 
-        let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
-            layout: &self.texture_layout,
-            bindings: &[wgpu::Binding {
-                binding: 0,
-                resource: wgpu::BindingResource::TextureView(
-                    &self.texture_array.texture.create_default_view(),
-                ),
-            }],
-        });
+        if instances.is_empty() {
+            return;
+        }
+
+        let texture_version = self.texture_atlas.layer_count();
+
+        if self.texture_version != texture_version {
+            self.texture =
+                device.create_bind_group(&wgpu::BindGroupDescriptor {
+                    layout: &self.texture_layout,
+                    bindings: &[wgpu::Binding {
+                        binding: 0,
+                        resource: wgpu::BindingResource::TextureView(
+                            &self.texture_atlas.view(),
+                        ),
+                    }],
+                });
 
-        let instances_buffer = device.create_buffer_mapped(
-            instances.len(),
-            wgpu::BufferUsage::VERTEX,
-        ).fill_from_slice(&instances);
+            self.texture_version = texture_version;
+        }
 
-        let mut render_pass = encoder.begin_render_pass(
-            &wgpu::RenderPassDescriptor {
+        let uniforms_buffer = device
+            .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
+            .fill_from_slice(&[Uniforms {
+                transform: transformation.into(),
+            }]);
+
+        encoder.copy_buffer_to_buffer(
+            &uniforms_buffer,
+            0,
+            &self.uniforms,
+            0,
+            std::mem::size_of::<Uniforms>() as u64,
+        );
+
+        let instances_buffer = device
+            .create_buffer_mapped(instances.len(), wgpu::BufferUsage::VERTEX)
+            .fill_from_slice(&instances);
+
+        let mut render_pass =
+            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                 color_attachments: &[
                     wgpu::RenderPassColorAttachmentDescriptor {
                         attachment: target,
@@ -346,12 +361,11 @@ impl Pipeline {
                     },
                 ],
                 depth_stencil_attachment: None,
-            },
-        );
+            });
 
         render_pass.set_pipeline(&self.pipeline);
         render_pass.set_bind_group(0, &self.constants, &[]);
-        render_pass.set_bind_group(1, &texture, &[]);
+        render_pass.set_bind_group(1, &self.texture, &[]);
         render_pass.set_index_buffer(&self.indices, 0);
         render_pass.set_vertex_buffers(
             0,
@@ -381,62 +395,10 @@ impl Pipeline {
     }
 }
 
-fn add_instances(
-    image: &Image,
-    allocation: &ImageAllocation,
-    instances: &mut Vec<Instance>,
-)  {
-    match allocation {
-        ImageAllocation::SingleAllocation(allocation) => {
-            add_instance(image.position, image.scale, allocation, instances);
-        }
-        ImageAllocation::MultipleAllocations { mappings, size } => {
-            let scaling_x = image.scale[0] / size.0 as f32;
-            let scaling_y = image.scale[1] / size.1 as f32;
-
-            for mapping in mappings {
-                let allocation = &mapping.allocation;
-                let mut position = image.position;
-                let mut scale = image.scale;
-
-                position[0] += mapping.src_pos.0 as f32 * scaling_x;
-                position[1] += mapping.src_pos.1 as f32 * scaling_y;
-                scale[0] = allocation.size().0 as f32 * scaling_x;
-                scale[1] = allocation.size().1 as f32 * scaling_y;
-
-                add_instance(position, scale, allocation, instances);
-            }
-        }
-    }
-}
-
-fn add_instance(
-    position: [f32; 2],
-    scale: [f32; 2],
-    allocation: &ArrayAllocation,
-    instances: &mut Vec<Instance>,
-) {
-    let x = (allocation.position().0 as f32 + 0.5) / (ATLAS_SIZE as f32);
-    let y = (allocation.position().1 as f32 + 0.5) / (ATLAS_SIZE as f32);
-    let w = (allocation.size().0 as f32 - 0.5) / (ATLAS_SIZE as f32);
-    let h = (allocation.size().1 as f32 - 0.5) / (ATLAS_SIZE as f32);
-    let layer_index = allocation.layer_index() as f32;
-
-    let instance = Instance {
-        _position: position,
-        _scale: scale,
-        _position_in_atlas: [x, y],
-        _scale_in_atlas: [w, h],
-        _layer: layer_index,
-    };
-
-    instances.push(instance);
-}
-
 pub struct Image {
     pub handle: Handle,
     pub position: [f32; 2],
-    pub scale: [f32; 2],
+    pub size: [f32; 2],
 }
 
 pub enum Handle {
@@ -444,508 +406,6 @@ pub enum Handle {
     Vector(svg::Handle),
 }
 
-#[derive(Debug)]
-pub struct ArrayAllocationMapping {
-    src_pos: (u32, u32),
-    allocation: ArrayAllocation,
-}
-
-#[derive(Debug)]
-pub enum ImageAllocation {
-    SingleAllocation(ArrayAllocation),
-    MultipleAllocations {
-        mappings: Vec<ArrayAllocationMapping>,
-        size: (u32, u32),
-    },
-}
-
-impl ImageAllocation {
-    #[cfg(feature = "image")]
-    pub fn size(&self) -> (u32, u32) {
-        match self {
-            ImageAllocation::SingleAllocation(allocation) => {
-                allocation.size()
-            }
-            ImageAllocation::MultipleAllocations { size, .. } => {
-                *size
-            }
-        }
-    }
-}
-
-pub enum ArrayAllocation {
-    AtlasAllocation {
-        layer_index: usize,
-        layer: Rc<RefCell<TextureLayer>>,
-        allocation: Allocation,
-    },
-    WholeLayer {
-        layer_index: usize,
-        layer: Rc<RefCell<TextureLayer>>,
-    }
-}
-
-impl ArrayAllocation {
-    pub fn size(&self) -> (u32, u32) {
-        match self {
-            ArrayAllocation::AtlasAllocation { allocation, .. } => {
-                let size = allocation.rectangle.size();
-                (size.width as u32, size.height as u32)
-            }
-            ArrayAllocation::WholeLayer { .. } => (ATLAS_SIZE, ATLAS_SIZE)
-        }
-    }
-
-    pub fn position(&self) -> (u32, u32) {
-        match self {
-            ArrayAllocation::AtlasAllocation { allocation, .. } => {
-                let min = &allocation.rectangle.min;
-                (min.x as u32, min.y as u32)
-            }
-            ArrayAllocation::WholeLayer { .. } => (0, 0)
-        }
-    }
-
-    pub fn layer_index(&self) -> usize {
-        match self {
-            ArrayAllocation::AtlasAllocation { layer_index, .. } => *layer_index,
-            ArrayAllocation::WholeLayer { layer_index, .. } => *layer_index,
-        }
-    }
-}
-
-impl std::fmt::Debug for ArrayAllocation {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            ArrayAllocation::AtlasAllocation { layer_index, .. } => {
-                write!(f, "ArrayAllocation::AtlasAllocation {{ layer_index: {:} }}", layer_index)
-            },
-            ArrayAllocation::WholeLayer { layer_index, .. } => {
-                write!(f, "ArrayAllocation::WholeLayer {{ layer_index: {} }}", layer_index)
-            }
-        }
-    }
-}
-
-impl Drop for ArrayAllocation {
-    fn drop(&mut self) {
-        match self {
-            ArrayAllocation::WholeLayer { layer, .. } => {
-                let _ = layer.replace(TextureLayer::Whole);
-            }
-            ArrayAllocation::AtlasAllocation { allocation, layer, .. } => {
-                let mut layer = layer.borrow_mut();
-                if let Some(allocator) = layer.allocator_mut() {
-                    allocator.deallocate(allocation.id);
-
-                    let mut empty_allocator = true;
-                    allocator.for_each_allocated_rectangle(|_, _| empty_allocator = false);
-
-                    if empty_allocator {
-                        *layer = TextureLayer::Empty;
-                    }
-                }
-            }
-        }
-    }
-}
-
-pub enum TextureLayer {
-    Whole,
-    Atlas(AtlasAllocator),
-    Empty,
-}
-
-impl TextureLayer {
-    pub fn is_empty(&self) -> bool {
-        if let TextureLayer::Empty = self {
-            true
-        } else {
-            false
-        }
-    }
-
-    pub fn allocator_mut(&mut self) -> Option<&mut AtlasAllocator> {
-        match self {
-            TextureLayer::Atlas(allocator) => Some(allocator),
-            _ => None
-        }
-    }
-}
-
-impl std::fmt::Debug for TextureLayer {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            TextureLayer::Whole => write!(f, "TextureLayer::Whole"),
-            TextureLayer::Atlas(_) => write!(f, "TextureLayer::Atlas"),
-            TextureLayer::Empty => write!(f, "TextureLayer::Empty"),
-        }
-    }
-}
-
-impl From<AtlasAllocator> for TextureLayer {
-    fn from(allocator: AtlasAllocator) -> Self {
-        TextureLayer::Atlas(allocator)
-    }
-}
-
-#[derive(Debug)]
-pub struct TextureArray {
-    texture: wgpu::Texture,
-    texture_array_size: u32,
-    layers: Vec<Rc<RefCell<TextureLayer>>>,
-}
-
-impl TextureArray {
-    fn new(device: &wgpu::Device) -> Self {
-        let (width, height) = (ATLAS_SIZE, ATLAS_SIZE);
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        let texture = device.create_texture(&wgpu::TextureDescriptor {
-            size: extent,
-            array_layer_count: 1,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
-        TextureArray {
-            texture,
-            texture_array_size: 1,
-            layers: vec!(Rc::new(RefCell::new(TextureLayer::Empty))),
-        }
-    }
-
-    fn allocate(&mut self, size: Size) -> Option<ImageAllocation> {
-        // Allocate one layer if allocation fits perfectly
-        if size.width == ATLAS_SIZE as i32 && size.height == ATLAS_SIZE as i32 {
-            for (i, layer) in self.layers.iter_mut().enumerate() {
-                if layer.borrow().is_empty()
-                {
-                    let _ = layer.replace(TextureLayer::Whole);
-                    return Some(ImageAllocation::SingleAllocation(
-                        ArrayAllocation::WholeLayer { layer: layer.clone(), layer_index: i }
-                    ));
-                }
-            }
-
-            let layer = Rc::new(RefCell::new(TextureLayer::Whole));
-            self.layers.push(layer.clone());
-            return Some(ImageAllocation::SingleAllocation(
-                ArrayAllocation::WholeLayer { layer, layer_index: self.layers.len() - 1 }
-            ));
-        }
-
-        // Split big allocations across multiple layers
-        if size.width > ATLAS_SIZE as i32 || size.height > ATLAS_SIZE as i32 {
-            let mut mappings = Vec::new();
-
-            let mut y = 0;
-            while y < size.height {
-                let height = std::cmp::min(size.height - y, ATLAS_SIZE as i32);
-                let mut x = 0;
-
-                while x < size.width {
-                    let width = std::cmp::min(size.width - x, ATLAS_SIZE as i32);
-                    let allocation = self
-                        .allocate(Size::new(width, height))
-                        .expect("Allocating texture space");
-
-                    if let ImageAllocation::SingleAllocation(allocation) = allocation {
-                        let src_pos = (x as u32, y as u32);
-                        mappings.push(ArrayAllocationMapping { src_pos, allocation });
-                    }
-
-                    x += width;
-                }
-                y += height;
-            }
-
-            return Some(ImageAllocation::MultipleAllocations {
-                mappings,
-                size: (size.width as u32, size.height as u32),
-            });
-        }
-
-        // Try allocating on an existing layer
-        for (i, layer) in self.layers.iter_mut().enumerate() {
-            if let Some(allocator) = layer.borrow_mut().allocator_mut() {
-                if let Some(allocation) = allocator.allocate(size.clone()) {
-                    let array_allocation = ArrayAllocation::AtlasAllocation {
-                        layer: layer.clone(),
-                        layer_index: i,
-                        allocation
-                    };
-                    return Some(ImageAllocation::SingleAllocation(array_allocation));
-                }
-            }
-        }
-
-        // Create new layer with atlas allocator
-        let mut allocator = AtlasAllocator::new(Size::new(ATLAS_SIZE as i32, ATLAS_SIZE as i32));
-        if let Some(allocation) = allocator.allocate(size) {
-            let layer = Rc::new(RefCell::new(allocator.into()));
-            self.layers.push(layer.clone());
-
-            return Some(ImageAllocation::SingleAllocation(
-                ArrayAllocation::AtlasAllocation {
-                    layer,
-                    layer_index: self.layers.len() - 1,
-                    allocation,
-                }
-            ));
-        }
-
-        // One of the above should have worked
-        None
-    }
-
-    fn upload<C, I>(
-        &mut self,
-        image: &I,
-        device: &wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-    ) -> ImageAllocation
-    where
-        I: RawImageData<Chunk = C>,
-        C: Copy + 'static,
-    {
-        let size = Size::new(image.width() as i32, image.height() as i32);
-        let allocation = self.allocate(size).expect("Allocating texture space");
-
-        match &allocation {
-            ImageAllocation::SingleAllocation(allocation) => {
-                let data = image.data();
-                let buffer = device
-                    .create_buffer_mapped(
-                        data.len(),
-                        wgpu::BufferUsage::COPY_SRC,
-                    )
-                    .fill_from_slice(data);
-
-                if allocation.layer_index() >= self.texture_array_size as usize {
-                    self.grow(1, device, encoder);
-                }
-
-                self.upload_texture(
-                    &buffer,
-                    &allocation,
-                    encoder,
-                );
-            }
-            ImageAllocation::MultipleAllocations { mappings, .. } => {
-                let chunks_per_pixel = 4 / std::mem::size_of::<C>();
-                let chunks_per_line = chunks_per_pixel * image.width() as usize;
-
-                let highest_layer = mappings
-                    .iter()
-                    .map(|m| m.allocation.layer_index() as u32)
-                    .max()
-                    .unwrap_or(0);
-
-                if highest_layer >= self.texture_array_size {
-                    let grow_by = 1 + highest_layer - self.texture_array_size;
-                    self.grow(grow_by, device, encoder);
-                }
-
-                for mapping in mappings {
-                    let sub_width = mapping.allocation.size().0 as usize;
-                    let sub_height = mapping.allocation.size().1 as usize;
-                    let sub_line_start = mapping.src_pos.0 as usize * chunks_per_pixel;
-                    let sub_line_end = (mapping.src_pos.0 as usize + sub_width) * chunks_per_pixel;
-
-                    let mut sub_lines = image
-                        .data()
-                        .chunks(chunks_per_line)
-                        .skip(mapping.src_pos.1 as usize)
-                        .take(sub_height)
-                        .map(|line| &line[sub_line_start..sub_line_end]);
-
-                    let buffer = device
-                        .create_buffer_mapped(
-                            chunks_per_pixel * sub_width * sub_height,
-                            wgpu::BufferUsage::COPY_SRC,
-                        );
-
-                    let mut buffer_lines = buffer.data.chunks_mut(sub_width * chunks_per_pixel);
-
-                    while let (Some(buffer_line), Some(sub_line)) = (buffer_lines.next(), sub_lines.next()) {
-                        buffer_line.copy_from_slice(sub_line);
-                    }
-
-                    self.upload_texture(
-                        &buffer.finish(),
-                        &mapping.allocation,
-                        encoder,
-                    );
-                }
-            }
-        }
-
-        allocation
-    }
-
-    fn upload_texture(
-        &mut self,
-        buffer: &wgpu::Buffer,
-        allocation: &ArrayAllocation,
-        encoder: &mut wgpu::CommandEncoder,
-    ) {
-        let array_layer = allocation.layer_index() as u32;
-
-        let (width, height) = allocation.size();
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        let (x, y) = allocation.position();
-
-        encoder.copy_buffer_to_texture(
-            wgpu::BufferCopyView {
-                buffer,
-                offset: 0,
-                row_pitch: 4 * width,
-                image_height: height,
-            },
-            wgpu::TextureCopyView {
-                texture: &self.texture,
-                array_layer,
-                mip_level: 0,
-                origin: wgpu::Origin3d {
-                    x: x as f32,
-                    y: y as f32,
-                    z: 0.0,
-                },
-            },
-            extent,
-        );
-    }
-
-    fn grow(
-        &mut self,
-        grow_by: u32,
-        device: &wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-    ) {
-        if grow_by == 0 {
-            return;
-        }
-
-        let old_texture_array_size = self.texture_array_size;
-
-        let new_texture = device.create_texture(&wgpu::TextureDescriptor {
-            size: wgpu::Extent3d {
-                width: ATLAS_SIZE,
-                height: ATLAS_SIZE,
-                depth: 1,
-            },
-            array_layer_count: old_texture_array_size + grow_by,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
-        for (i, layer) in self.layers.iter_mut().enumerate() {
-            if i >= old_texture_array_size as usize {
-                break;
-            }
-
-            if layer.borrow().is_empty() {
-                continue;
-            }
-
-            encoder.copy_texture_to_texture(
-                wgpu::TextureCopyView {
-                    texture: &self.texture,
-                    array_layer: i as u32,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
-                },
-                wgpu::TextureCopyView {
-                    texture: &new_texture,
-                    array_layer: i as u32,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
-                },
-                wgpu::Extent3d {
-                    width: ATLAS_SIZE,
-                    height: ATLAS_SIZE,
-                    depth: 1,
-                }
-            );
-        }
-
-        self.texture_array_size += grow_by;
-        self.texture = new_texture;
-    }
-}
-
-trait RawImageData {
-    type Chunk;
-
-    fn data(&self) -> &[Self::Chunk];
-    fn width(&self) -> u32;
-    fn height(&self) -> u32;
-}
-
-#[cfg(feature = "image")]
-impl RawImageData for ::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>> {
-    type Chunk = u8;
-
-    fn data(&self) -> &[Self::Chunk] {
-        &self
-    }
-
-    fn width(&self) -> u32 {
-        self.dimensions().0
-    }
-
-    fn height(&self) -> u32 {
-        self.dimensions().1
-    }
-}
-
-#[cfg(feature = "svg")]
-impl RawImageData for resvg::raqote::DrawTarget {
-    type Chunk = u32;
-
-    fn data(&self) -> &[Self::Chunk] {
-        self.get_data()
-    }
-
-    fn width(&self) -> u32 {
-        self.width() as u32
-    }
-
-    fn height(&self) -> u32 {
-        self.height() as u32
-    }
-}
-
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct Vertex {
@@ -969,16 +429,14 @@ const QUAD_VERTS: [Vertex; 4] = [
     },
 ];
 
-const ATLAS_SIZE: u32 = 4096;
-
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 struct Instance {
     _position: [f32; 2],
-    _scale: [f32; 2],
+    _size: [f32; 2],
     _position_in_atlas: [f32; 2],
-    _scale_in_atlas: [f32; 2],
-    _layer: f32,
+    _size_in_atlas: [f32; 2],
+    _layer: u32,
 }
 
 #[repr(C)]
@@ -986,3 +444,67 @@ struct Instance {
 struct Uniforms {
     transform: [f32; 16],
 }
+
+fn add_instances(
+    image: &Image,
+    entry: &atlas::Entry,
+    instances: &mut Vec<Instance>,
+) {
+    match entry {
+        atlas::Entry::Contiguous(allocation) => {
+            add_instance(image.position, image.size, allocation, instances);
+        }
+        atlas::Entry::Fragmented { fragments, size } => {
+            let scaling_x = image.size[0] / size.0 as f32;
+            let scaling_y = image.size[1] / size.1 as f32;
+
+            for fragment in fragments {
+                let allocation = &fragment.allocation;
+
+                let [x, y] = image.position;
+                let (fragment_x, fragment_y) = fragment.position;
+                let (fragment_width, fragment_height) = allocation.size();
+
+                let position = [
+                    x + fragment_x as f32 * scaling_x,
+                    y + fragment_y as f32 * scaling_y,
+                ];
+
+                let size = [
+                    fragment_width as f32 * scaling_x,
+                    fragment_height as f32 * scaling_y,
+                ];
+
+                add_instance(position, size, allocation, instances);
+            }
+        }
+    }
+}
+
+#[inline]
+fn add_instance(
+    position: [f32; 2],
+    size: [f32; 2],
+    allocation: &atlas::Allocation,
+    instances: &mut Vec<Instance>,
+) {
+    let (x, y) = allocation.position();
+    let (width, height) = allocation.size();
+    let layer = allocation.layer();
+
+    let instance = Instance {
+        _position: position,
+        _size: size,
+        _position_in_atlas: [
+            x as f32 / atlas::SIZE as f32,
+            y as f32 / atlas::SIZE as f32,
+        ],
+        _size_in_atlas: [
+            width as f32 / atlas::SIZE as f32,
+            height as f32 / atlas::SIZE as f32,
+        ],
+        _layer: layer as u32,
+    };
+
+    instances.push(instance);
+}
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 884dd65a..071d53c8 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,12 +1,10 @@
-use crate::image::{TextureArray, ImageAllocation};
+use crate::texture::atlas::{self, Atlas};
 use iced_native::image;
-use std::{
-    collections::{HashMap, HashSet},
-};
+use std::collections::{HashMap, HashSet};
 
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
-    Device(ImageAllocation),
+    Device(atlas::Entry),
     NotFound,
     Invalid,
 }
@@ -15,7 +13,7 @@ impl Memory {
     pub fn dimensions(&self) -> (u32, u32) {
         match self {
             Memory::Host(image) => image.dimensions(),
-            Memory::Device(allocation) => allocation.size(),
+            Memory::Device(entry) => entry.size(),
             Memory::NotFound => (1, 1),
             Memory::Invalid => (1, 1),
         }
@@ -78,17 +76,26 @@ impl Cache {
         handle: &image::Handle,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        atlas_array: &mut TextureArray,
-    ) -> &Memory {
+        atlas: &mut Atlas,
+    ) -> Option<&atlas::Entry> {
         let memory = self.load(handle);
 
         if let Memory::Host(image) = memory {
-            let allocation = atlas_array.upload(image, device, encoder);
+            let (width, height) = image.dimensions();
+
+            let allocation =
+                atlas.upload(width, height, &image, device, encoder)?;
+
+            dbg!("Uploaded");
 
             *memory = Memory::Device(allocation);
         }
 
-        memory
+        if let Memory::Device(allocation) = memory {
+            Some(allocation)
+        } else {
+            None
+        }
     }
 
     pub fn trim(&mut self) {
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 98588e5c..0dabc9ca 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,8 +1,6 @@
-use crate::image::{TextureArray, ImageAllocation};
+use crate::texture::atlas::{self, Atlas};
 use iced_native::svg;
-use std::{
-    collections::{HashMap, HashSet},
-};
+use std::collections::{HashMap, HashSet};
 
 pub enum Svg {
     Loaded(resvg::usvg::Tree),
@@ -33,7 +31,7 @@ impl std::fmt::Debug for Svg {
 
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
-    rasterized: HashMap<(u64, u32, u32), ImageAllocation>,
+    rasterized: HashMap<(u64, u32, u32), atlas::Entry>,
     svg_hits: HashSet<u64>,
     rasterized_hits: HashSet<(u64, u32, u32)>,
 }
@@ -71,8 +69,8 @@ impl Cache {
         scale: f32,
         device: &wgpu::Device,
         encoder: &mut wgpu::CommandEncoder,
-        texture_array: &mut TextureArray,
-    ) -> Option<&ImageAllocation> {
+        texture_atlas: &mut Atlas,
+    ) -> Option<&atlas::Entry> {
         let id = handle.id();
 
         let (width, height) = (
@@ -104,10 +102,8 @@ impl Cache {
                 let screen_size =
                     resvg::ScreenSize::new(width, height).unwrap();
 
-                let mut canvas = resvg::raqote::DrawTarget::new(
-                    width as i32,
-                    height as i32,
-                );
+                let mut canvas =
+                    resvg::raqote::DrawTarget::new(width as i32, height as i32);
 
                 resvg::backend_raqote::render_to_canvas(
                     tree,
@@ -116,17 +112,21 @@ impl Cache {
                     &mut canvas,
                 );
 
-                let allocation = texture_array.upload(&canvas, device, encoder);
+                let allocation = texture_atlas.upload(
+                    width,
+                    height,
+                    canvas.get_data(),
+                    device,
+                    encoder,
+                )?;
 
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert((id, width, height));
-                let _ = self
-                    .rasterized
-                    .insert((id, width, height), allocation);
+                let _ = self.rasterized.insert((id, width, height), allocation);
 
                 self.rasterized.get(&(id, width, height))
             }
-            Svg::NotFound => None
+            Svg::NotFound => None,
         }
     }
 
@@ -145,4 +145,4 @@ impl std::fmt::Debug for Cache {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "vector::Cache")
     }
-}
\ No newline at end of file
+}
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 4f2b732d..832da31d 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -19,13 +19,14 @@
 //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
 //! [WebGPU API]: https://gpuweb.github.io/gpuweb/
 //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
-#![deny(missing_docs)]
+//#![deny(missing_docs)]
 #![deny(missing_debug_implementations)]
 #![deny(unused_results)]
 #![forbid(unsafe_code)]
 #![forbid(rust_2018_idioms)]
 pub mod defaults;
 pub mod settings;
+pub mod texture;
 pub mod triangle;
 pub mod widget;
 pub mod window;
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index f67dd1eb..b5dce480 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -227,14 +227,14 @@ impl Renderer {
                 layer.images.push(Image {
                     handle: image::Handle::Raster(handle.clone()),
                     position: [bounds.x, bounds.y],
-                    scale: [bounds.width, bounds.height],
+                    size: [bounds.width, bounds.height],
                 });
             }
             Primitive::Svg { handle, bounds } => {
                 layer.images.push(Image {
                     handle: image::Handle::Vector(handle.clone()),
                     position: [bounds.x, bounds.y],
-                    scale: [bounds.width, bounds.height],
+                    size: [bounds.width, bounds.height],
                 });
             }
             Primitive::Mesh2D { origin, buffers } => {
diff --git a/wgpu/src/texture.rs b/wgpu/src/texture.rs
new file mode 100644
index 00000000..00b60bfa
--- /dev/null
+++ b/wgpu/src/texture.rs
@@ -0,0 +1,3 @@
+pub mod atlas;
+
+pub use atlas::Atlas;
diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
new file mode 100644
index 00000000..b950e59b
--- /dev/null
+++ b/wgpu/src/texture/atlas.rs
@@ -0,0 +1,313 @@
+pub mod entry;
+
+mod allocation;
+mod allocator;
+mod layer;
+
+pub use allocation::Allocation;
+pub use entry::Entry;
+pub use layer::Layer;
+
+use allocator::Allocator;
+
+pub const SIZE: u32 = 4096;
+
+#[derive(Debug)]
+pub struct Atlas {
+    texture: wgpu::Texture,
+    texture_view: wgpu::TextureView,
+    layers: Vec<Layer>,
+}
+
+impl Atlas {
+    pub fn new(device: &wgpu::Device) -> Self {
+        let extent = wgpu::Extent3d {
+            width: SIZE,
+            height: SIZE,
+            depth: 1,
+        };
+
+        let texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: 2,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        let texture_view = texture.create_default_view();
+
+        Atlas {
+            texture,
+            texture_view,
+            layers: vec![Layer::Empty, Layer::Empty],
+        }
+    }
+
+    pub fn view(&self) -> &wgpu::TextureView {
+        &self.texture_view
+    }
+
+    pub fn layer_count(&self) -> usize {
+        self.layers.len()
+    }
+
+    pub fn upload<C>(
+        &mut self,
+        width: u32,
+        height: u32,
+        data: &[C],
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) -> Option<Entry>
+    where
+        C: Copy + 'static,
+    {
+        let memory = {
+            let current_size = self.layers.len();
+            let memory = self.allocate(width, height)?;
+
+            // We grow the internal texture after allocating if necessary
+            let new_layers = self.layers.len() - current_size;
+            self.grow(new_layers, device, encoder);
+
+            memory
+        };
+
+        dbg!(&memory);
+
+        let buffer = device
+            .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
+            .fill_from_slice(data);
+
+        match &memory {
+            Entry::Contiguous(allocation) => {
+                self.upload_texture(&buffer, 0, &allocation, encoder);
+            }
+            Entry::Fragmented { fragments, .. } => {
+                for fragment in fragments {
+                    let (x, y) = fragment.allocation.position();
+
+                    let offset =
+                        (y * height + x) as usize * std::mem::size_of::<C>();
+
+                    self.upload_texture(
+                        &buffer,
+                        offset as u64,
+                        &fragment.allocation,
+                        encoder,
+                    );
+                }
+            }
+        }
+
+        Some(memory)
+    }
+
+    fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
+        // Allocate one layer if texture fits perfectly
+        if width == SIZE && height == SIZE {
+            let mut empty_layers = self
+                .layers
+                .iter_mut()
+                .enumerate()
+                .filter(|(_, layer)| layer.is_empty());
+
+            if let Some((i, layer)) = empty_layers.next() {
+                *layer = Layer::Full;
+
+                return Some(Entry::Contiguous(Allocation::Full { layer: i }));
+            }
+
+            self.layers.push(Layer::Full);
+
+            return Some(Entry::Contiguous(Allocation::Full {
+                layer: self.layers.len() - 1,
+            }));
+        }
+
+        // Split big textures across multiple layers
+        if width > SIZE || height > SIZE {
+            let mut fragments = Vec::new();
+            let mut y = 0;
+
+            while y < height {
+                let height = std::cmp::min(height - y, SIZE);
+                let mut x = 0;
+
+                while x < width {
+                    let width = std::cmp::min(width - x, SIZE);
+
+                    let allocation = self.allocate(width, height)?;
+
+                    if let Entry::Contiguous(allocation) = allocation {
+                        fragments.push(entry::Fragment {
+                            position: (x, y),
+                            allocation,
+                        });
+                    }
+
+                    x += width;
+                }
+
+                y += height;
+            }
+
+            return Some(Entry::Fragmented {
+                size: (width, height),
+                fragments,
+            });
+        }
+
+        // Try allocating on an existing layer
+        for (i, layer) in self.layers.iter_mut().enumerate() {
+            match layer {
+                Layer::Empty => {
+                    let mut allocator = Allocator::new(SIZE);
+
+                    if let Some(region) = allocator.allocate(width, height) {
+                        *layer = Layer::Busy(allocator);
+
+                        return Some(Entry::Contiguous(Allocation::Partial {
+                            region,
+                            layer: i,
+                        }));
+                    }
+                }
+                Layer::Busy(allocator) => {
+                    if let Some(region) = allocator.allocate(width, height) {
+                        return Some(Entry::Contiguous(Allocation::Partial {
+                            region,
+                            layer: i,
+                        }));
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        // Create new layer with atlas allocator
+        let mut allocator = Allocator::new(SIZE);
+
+        if let Some(region) = allocator.allocate(width, height) {
+            self.layers.push(Layer::Busy(allocator));
+
+            return Some(Entry::Contiguous(Allocation::Partial {
+                region,
+                layer: self.layers.len() - 1,
+            }));
+        }
+
+        // We ran out of memory (?)
+        None
+    }
+
+    fn upload_texture(
+        &mut self,
+        buffer: &wgpu::Buffer,
+        offset: u64,
+        allocation: &Allocation,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        let (x, y) = allocation.position();
+        let (width, height) = allocation.size();
+        let layer = allocation.layer();
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        encoder.copy_buffer_to_texture(
+            wgpu::BufferCopyView {
+                buffer,
+                offset,
+                row_pitch: 4 * width,
+                image_height: height,
+            },
+            wgpu::TextureCopyView {
+                texture: &self.texture,
+                array_layer: layer as u32,
+                mip_level: 0,
+                origin: wgpu::Origin3d {
+                    x: x as f32,
+                    y: y as f32,
+                    z: 0.0,
+                },
+            },
+            extent,
+        );
+    }
+
+    fn grow(
+        &mut self,
+        amount: usize,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        if amount == 0 {
+            return;
+        }
+
+        let new_texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: wgpu::Extent3d {
+                width: SIZE,
+                height: SIZE,
+                depth: 1,
+            },
+            array_layer_count: (self.layers.len() + amount) as u32,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        for (i, layer) in self.layers.iter_mut().enumerate() {
+            if layer.is_empty() {
+                continue;
+            }
+
+            encoder.copy_texture_to_texture(
+                wgpu::TextureCopyView {
+                    texture: &self.texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::TextureCopyView {
+                    texture: &new_texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::Extent3d {
+                    width: SIZE,
+                    height: SIZE,
+                    depth: 1,
+                },
+            );
+        }
+
+        for _ in 0..amount {
+            self.layers.push(Layer::Empty);
+        }
+
+        self.texture = new_texture;
+    }
+}
diff --git a/wgpu/src/texture/atlas/allocation.rs b/wgpu/src/texture/atlas/allocation.rs
new file mode 100644
index 00000000..e17b3f8c
--- /dev/null
+++ b/wgpu/src/texture/atlas/allocation.rs
@@ -0,0 +1,35 @@
+use crate::texture::atlas::{self, allocator};
+
+#[derive(Debug)]
+pub enum Allocation {
+    Partial {
+        layer: usize,
+        region: allocator::Region,
+    },
+    Full {
+        layer: usize,
+    },
+}
+
+impl Allocation {
+    pub fn position(&self) -> (u32, u32) {
+        match self {
+            Allocation::Partial { region, .. } => region.position(),
+            Allocation::Full { .. } => (0, 0),
+        }
+    }
+
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            Allocation::Partial { region, .. } => region.size(),
+            Allocation::Full { .. } => (atlas::SIZE, atlas::SIZE),
+        }
+    }
+
+    pub fn layer(&self) -> usize {
+        match self {
+            Allocation::Partial { layer, .. } => *layer,
+            Allocation::Full { layer } => *layer,
+        }
+    }
+}
diff --git a/wgpu/src/texture/atlas/allocator.rs b/wgpu/src/texture/atlas/allocator.rs
new file mode 100644
index 00000000..cd710522
--- /dev/null
+++ b/wgpu/src/texture/atlas/allocator.rs
@@ -0,0 +1,57 @@
+use guillotiere::{AtlasAllocator, Size};
+
+pub struct Allocator {
+    raw: AtlasAllocator,
+}
+
+impl Allocator {
+    pub fn new(size: u32) -> Allocator {
+        let raw = AtlasAllocator::new(Size::new(size as i32, size as i32));
+
+        Allocator { raw }
+    }
+
+    pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
+        let allocation = self
+            .raw
+            .allocate(Size::new(width as i32 + 2, height as i32 + 2))?;
+
+        Some(Region(allocation))
+    }
+
+    pub fn deallocate(&mut self, region: Region) {
+        self.raw.deallocate(region.0.id);
+    }
+}
+
+pub struct Region(guillotiere::Allocation);
+
+impl Region {
+    pub fn position(&self) -> (u32, u32) {
+        let rectangle = &self.0.rectangle;
+
+        (rectangle.min.x as u32 + 1, rectangle.min.y as u32 + 1)
+    }
+
+    pub fn size(&self) -> (u32, u32) {
+        let size = self.0.rectangle.size();
+
+        (size.width as u32 - 2, size.height as u32 - 2)
+    }
+}
+
+impl std::fmt::Debug for Allocator {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Allocator")
+    }
+}
+
+impl std::fmt::Debug for Region {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "Region {{ id: {:?}, rectangle: {:?} }}",
+            self.0.id, self.0.rectangle
+        )
+    }
+}
diff --git a/wgpu/src/texture/atlas/entry.rs b/wgpu/src/texture/atlas/entry.rs
new file mode 100644
index 00000000..2c064665
--- /dev/null
+++ b/wgpu/src/texture/atlas/entry.rs
@@ -0,0 +1,25 @@
+use crate::texture::atlas;
+
+#[derive(Debug)]
+pub enum Entry {
+    Contiguous(atlas::Allocation),
+    Fragmented {
+        size: (u32, u32),
+        fragments: Vec<Fragment>,
+    },
+}
+
+impl Entry {
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            Entry::Contiguous(allocation) => allocation.size(),
+            Entry::Fragmented { size, .. } => *size,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct Fragment {
+    pub position: (u32, u32),
+    pub allocation: atlas::Allocation,
+}
diff --git a/wgpu/src/texture/atlas/layer.rs b/wgpu/src/texture/atlas/layer.rs
new file mode 100644
index 00000000..b025d8a1
--- /dev/null
+++ b/wgpu/src/texture/atlas/layer.rs
@@ -0,0 +1,17 @@
+use crate::texture::atlas::Allocator;
+
+#[derive(Debug)]
+pub enum Layer {
+    Empty,
+    Busy(Allocator),
+    Full,
+}
+
+impl Layer {
+    pub fn is_empty(&self) -> bool {
+        match self {
+            Layer::Empty => true,
+            _ => false,
+        }
+    }
+}
-- 
cgit 


From c58d94f3fda40f215254008ec105aeab56085b0e Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 12:52:30 +0100
Subject: Avoid creating a vertex buffer every frame

---
 wgpu/src/image.rs              | 109 ++++++++++++++++++++++++++---------------
 wgpu/src/image/raster.rs       |   2 -
 wgpu/src/shader/image.vert     |   2 +-
 wgpu/src/shader/image.vert.spv | Bin 2472 -> 2504 bytes
 wgpu/src/texture/atlas.rs      |  12 ++---
 5 files changed, 75 insertions(+), 50 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index e14b3024..afff52a6 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -22,6 +22,7 @@ pub struct Pipeline {
     uniforms: wgpu::Buffer,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
+    instances: wgpu::Buffer,
     constants: wgpu::BindGroup,
     texture: wgpu::BindGroup,
     texture_version: usize,
@@ -188,7 +189,7 @@ impl Pipeline {
                             },
                             wgpu::VertexAttributeDescriptor {
                                 shader_location: 5,
-                                format: wgpu::VertexFormat::Float,
+                                format: wgpu::VertexFormat::Uint,
                                 offset: 4 * 8,
                             },
                         ],
@@ -207,6 +208,11 @@ impl Pipeline {
             .create_buffer_mapped(QUAD_INDICES.len(), wgpu::BufferUsage::INDEX)
             .fill_from_slice(&QUAD_INDICES);
 
+        let instances = device.create_buffer(&wgpu::BufferDescriptor {
+            size: mem::size_of::<Instance>() as u64 * Instance::MAX as u64,
+            usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
+        });
+
         let texture_atlas = texture::Atlas::new(device);
 
         let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
@@ -230,6 +236,7 @@ impl Pipeline {
             uniforms: uniforms_buffer,
             vertices,
             indices,
+            instances,
             constants: constant_bind_group,
             texture,
             texture_version: texture_atlas.layer_count(),
@@ -341,49 +348,67 @@ impl Pipeline {
         );
 
         let instances_buffer = device
-            .create_buffer_mapped(instances.len(), wgpu::BufferUsage::VERTEX)
+            .create_buffer_mapped(instances.len(), wgpu::BufferUsage::COPY_SRC)
             .fill_from_slice(&instances);
 
-        let mut render_pass =
-            encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-                color_attachments: &[
-                    wgpu::RenderPassColorAttachmentDescriptor {
-                        attachment: target,
-                        resolve_target: None,
-                        load_op: wgpu::LoadOp::Load,
-                        store_op: wgpu::StoreOp::Store,
-                        clear_color: wgpu::Color {
-                            r: 0.0,
-                            g: 0.0,
-                            b: 0.0,
-                            a: 0.0,
+        let mut i = 0;
+        let total = instances.len();
+
+        while i < total {
+            let end = (i + Instance::MAX).min(total);
+            let amount = end - i;
+
+            encoder.copy_buffer_to_buffer(
+                &instances_buffer,
+                (i * std::mem::size_of::<Instance>()) as u64,
+                &self.instances,
+                0,
+                (amount * std::mem::size_of::<Instance>()) as u64,
+            );
+
+            let mut render_pass =
+                encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+                    color_attachments: &[
+                        wgpu::RenderPassColorAttachmentDescriptor {
+                            attachment: target,
+                            resolve_target: None,
+                            load_op: wgpu::LoadOp::Load,
+                            store_op: wgpu::StoreOp::Store,
+                            clear_color: wgpu::Color {
+                                r: 0.0,
+                                g: 0.0,
+                                b: 0.0,
+                                a: 0.0,
+                            },
                         },
-                    },
-                ],
-                depth_stencil_attachment: None,
-            });
-
-        render_pass.set_pipeline(&self.pipeline);
-        render_pass.set_bind_group(0, &self.constants, &[]);
-        render_pass.set_bind_group(1, &self.texture, &[]);
-        render_pass.set_index_buffer(&self.indices, 0);
-        render_pass.set_vertex_buffers(
-            0,
-            &[(&self.vertices, 0), (&instances_buffer, 0)],
-        );
-
-        render_pass.set_scissor_rect(
-            bounds.x,
-            bounds.y,
-            bounds.width,
-            bounds.height,
-        );
+                    ],
+                    depth_stencil_attachment: None,
+                });
 
-        render_pass.draw_indexed(
-            0..QUAD_INDICES.len() as u32,
-            0,
-            0..instances.len() as u32,
-        );
+            render_pass.set_pipeline(&self.pipeline);
+            render_pass.set_bind_group(0, &self.constants, &[]);
+            render_pass.set_bind_group(1, &self.texture, &[]);
+            render_pass.set_index_buffer(&self.indices, 0);
+            render_pass.set_vertex_buffers(
+                0,
+                &[(&self.vertices, 0), (&self.instances, 0)],
+            );
+
+            render_pass.set_scissor_rect(
+                bounds.x,
+                bounds.y,
+                bounds.width,
+                bounds.height,
+            );
+
+            render_pass.draw_indexed(
+                0..QUAD_INDICES.len() as u32,
+                0,
+                0..amount as u32,
+            );
+
+            i += Instance::MAX;
+        }
     }
 
     pub fn trim_cache(&mut self) {
@@ -439,6 +464,10 @@ struct Instance {
     _layer: u32,
 }
 
+impl Instance {
+    pub const MAX: usize = 1_000;
+}
+
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 struct Uniforms {
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 071d53c8..8d2f342e 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -86,8 +86,6 @@ impl Cache {
             let allocation =
                 atlas.upload(width, height, &image, device, encoder)?;
 
-            dbg!("Uploaded");
-
             *memory = Memory::Device(allocation);
         }
 
diff --git a/wgpu/src/shader/image.vert b/wgpu/src/shader/image.vert
index 0ce7dd6b..dab53cfe 100644
--- a/wgpu/src/shader/image.vert
+++ b/wgpu/src/shader/image.vert
@@ -5,7 +5,7 @@ layout(location = 1) in vec2 i_Pos;
 layout(location = 2) in vec2 i_Scale;
 layout(location = 3) in vec2 i_Atlas_Pos;
 layout(location = 4) in vec2 i_Atlas_Scale;
-layout(location = 5) in float i_Layer;
+layout(location = 5) in uint i_Layer;
 
 layout (set = 0, binding = 0) uniform Globals {
     mat4 u_Transform;
diff --git a/wgpu/src/shader/image.vert.spv b/wgpu/src/shader/image.vert.spv
index 192bf6c3..21f5db2d 100644
Binary files a/wgpu/src/shader/image.vert.spv and b/wgpu/src/shader/image.vert.spv differ
diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
index b950e59b..bf528dc9 100644
--- a/wgpu/src/texture/atlas.rs
+++ b/wgpu/src/texture/atlas.rs
@@ -67,24 +67,22 @@ impl Atlas {
     where
         C: Copy + 'static,
     {
-        let memory = {
+        let entry = {
             let current_size = self.layers.len();
-            let memory = self.allocate(width, height)?;
+            let entry = self.allocate(width, height)?;
 
             // We grow the internal texture after allocating if necessary
             let new_layers = self.layers.len() - current_size;
             self.grow(new_layers, device, encoder);
 
-            memory
+            entry
         };
 
-        dbg!(&memory);
-
         let buffer = device
             .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
             .fill_from_slice(data);
 
-        match &memory {
+        match &entry {
             Entry::Contiguous(allocation) => {
                 self.upload_texture(&buffer, 0, &allocation, encoder);
             }
@@ -105,7 +103,7 @@ impl Atlas {
             }
         }
 
-        Some(memory)
+        Some(entry)
     }
 
     fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
-- 
cgit 


From 48d70280eb4f5908f1c9339bebdfbab856d55ae1 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 18:49:46 +0100
Subject: Fix multiple issues from the refactoring

- Update texture view on grow
- Fix atlas texture coordinates
- Fix fragmented uploads
---
 wgpu/src/image.rs                   | 10 +++---
 wgpu/src/image/raster.rs            |  5 ++-
 wgpu/src/texture/atlas.rs           | 52 +++++++++++++++++++------------
 wgpu/src/texture/atlas/allocator.rs | 61 +++++++++++++++++++++++++++----------
 4 files changed, 86 insertions(+), 42 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index afff52a6..1ffa50d2 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -319,6 +319,8 @@ impl Pipeline {
         let texture_version = self.texture_atlas.layer_count();
 
         if self.texture_version != texture_version {
+            log::info!("Atlas has grown. Recreating bind group...");
+
             self.texture =
                 device.create_bind_group(&wgpu::BindGroupDescriptor {
                     layout: &self.texture_layout,
@@ -525,12 +527,12 @@ fn add_instance(
         _position: position,
         _size: size,
         _position_in_atlas: [
-            x as f32 / atlas::SIZE as f32,
-            y as f32 / atlas::SIZE as f32,
+            (x as f32 + 0.5) / atlas::SIZE as f32,
+            (y as f32 + 0.5) / atlas::SIZE as f32,
         ],
         _size_in_atlas: [
-            width as f32 / atlas::SIZE as f32,
-            height as f32 / atlas::SIZE as f32,
+            (width as f32 - 0.5) / atlas::SIZE as f32,
+            (height as f32 - 0.5) / atlas::SIZE as f32,
         ],
         _layer: layer as u32,
     };
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 8d2f342e..b19da582 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -83,10 +83,9 @@ impl Cache {
         if let Memory::Host(image) = memory {
             let (width, height) = image.dimensions();
 
-            let allocation =
-                atlas.upload(width, height, &image, device, encoder)?;
+            let entry = atlas.upload(width, height, &image, device, encoder)?;
 
-            *memory = Memory::Device(allocation);
+            *memory = Memory::Device(entry);
         }
 
         if let Memory::Device(allocation) = memory {
diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
index bf528dc9..3d4e81c1 100644
--- a/wgpu/src/texture/atlas.rs
+++ b/wgpu/src/texture/atlas.rs
@@ -10,7 +10,7 @@ pub use layer::Layer;
 
 use allocator::Allocator;
 
-pub const SIZE: u32 = 4096;
+pub const SIZE: u32 = 2048;
 
 #[derive(Debug)]
 pub struct Atlas {
@@ -78,24 +78,33 @@ impl Atlas {
             entry
         };
 
+        log::info!("Allocated atlas entry: {:?}", entry);
+
         let buffer = device
             .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
             .fill_from_slice(data);
 
         match &entry {
             Entry::Contiguous(allocation) => {
-                self.upload_texture(&buffer, 0, &allocation, encoder);
+                self.upload_allocation(
+                    &buffer,
+                    width,
+                    height,
+                    0,
+                    &allocation,
+                    encoder,
+                );
             }
             Entry::Fragmented { fragments, .. } => {
                 for fragment in fragments {
-                    let (x, y) = fragment.allocation.position();
-
-                    let offset =
-                        (y * height + x) as usize * std::mem::size_of::<C>();
+                    let (x, y) = fragment.position;
+                    let offset = (y * width + x) as usize * 4;
 
-                    self.upload_texture(
+                    self.upload_allocation(
                         &buffer,
-                        offset as u64,
+                        width,
+                        height,
+                        offset,
                         &fragment.allocation,
                         encoder,
                     );
@@ -103,6 +112,8 @@ impl Atlas {
             }
         }
 
+        log::info!("Current atlas: {:?}", &self);
+
         Some(entry)
     }
 
@@ -204,10 +215,12 @@ impl Atlas {
         None
     }
 
-    fn upload_texture(
+    fn upload_allocation(
         &mut self,
         buffer: &wgpu::Buffer,
-        offset: u64,
+        image_width: u32,
+        image_height: u32,
+        offset: usize,
         allocation: &Allocation,
         encoder: &mut wgpu::CommandEncoder,
     ) {
@@ -224,9 +237,9 @@ impl Atlas {
         encoder.copy_buffer_to_texture(
             wgpu::BufferCopyView {
                 buffer,
-                offset,
-                row_pitch: 4 * width,
-                image_height: height,
+                offset: offset as u64,
+                row_pitch: 4 * image_width,
+                image_height,
             },
             wgpu::TextureCopyView {
                 texture: &self.texture,
@@ -258,7 +271,7 @@ impl Atlas {
                 height: SIZE,
                 depth: 1,
             },
-            array_layer_count: (self.layers.len() + amount) as u32,
+            array_layer_count: self.layers.len() as u32,
             mip_level_count: 1,
             sample_count: 1,
             dimension: wgpu::TextureDimension::D2,
@@ -268,7 +281,11 @@ impl Atlas {
                 | wgpu::TextureUsage::SAMPLED,
         });
 
-        for (i, layer) in self.layers.iter_mut().enumerate() {
+        let amount_to_copy = self.layers.len() - amount;
+
+        for (i, layer) in
+            self.layers.iter_mut().take(amount_to_copy).enumerate()
+        {
             if layer.is_empty() {
                 continue;
             }
@@ -302,10 +319,7 @@ impl Atlas {
             );
         }
 
-        for _ in 0..amount {
-            self.layers.push(Layer::Empty);
-        }
-
         self.texture = new_texture;
+        self.texture_view = self.texture.create_default_view();
     }
 }
diff --git a/wgpu/src/texture/atlas/allocator.rs b/wgpu/src/texture/atlas/allocator.rs
index cd710522..ad111212 100644
--- a/wgpu/src/texture/atlas/allocator.rs
+++ b/wgpu/src/texture/atlas/allocator.rs
@@ -2,41 +2,70 @@ use guillotiere::{AtlasAllocator, Size};
 
 pub struct Allocator {
     raw: AtlasAllocator,
+    size: u32,
 }
 
 impl Allocator {
+    const PADDING: u32 = 1;
+
     pub fn new(size: u32) -> Allocator {
         let raw = AtlasAllocator::new(Size::new(size as i32, size as i32));
 
-        Allocator { raw }
+        Allocator { raw, size }
     }
 
     pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
-        let allocation = self
-            .raw
-            .allocate(Size::new(width as i32 + 2, height as i32 + 2))?;
+        let padding = (
+            if width + Self::PADDING * 2 < self.size {
+                Self::PADDING
+            } else {
+                0
+            },
+            if height + Self::PADDING * 2 < self.size {
+                Self::PADDING
+            } else {
+                0
+            },
+        );
+
+        let allocation = self.raw.allocate(Size::new(
+            (width + padding.0 * 2) as i32,
+            (height + padding.1 * 2) as i32,
+        ))?;
 
-        Some(Region(allocation))
+        Some(Region {
+            allocation,
+            padding,
+        })
     }
 
     pub fn deallocate(&mut self, region: Region) {
-        self.raw.deallocate(region.0.id);
+        self.raw.deallocate(region.allocation.id);
     }
 }
 
-pub struct Region(guillotiere::Allocation);
+pub struct Region {
+    allocation: guillotiere::Allocation,
+    padding: (u32, u32),
+}
 
 impl Region {
     pub fn position(&self) -> (u32, u32) {
-        let rectangle = &self.0.rectangle;
+        let rectangle = &self.allocation.rectangle;
 
-        (rectangle.min.x as u32 + 1, rectangle.min.y as u32 + 1)
+        (
+            rectangle.min.x as u32 + self.padding.0,
+            rectangle.min.y as u32 + self.padding.1,
+        )
     }
 
     pub fn size(&self) -> (u32, u32) {
-        let size = self.0.rectangle.size();
+        let size = self.allocation.rectangle.size();
 
-        (size.width as u32 - 2, size.height as u32 - 2)
+        (
+            size.width as u32 - self.padding.0 * 2,
+            size.height as u32 - self.padding.1 * 2,
+        )
     }
 }
 
@@ -48,10 +77,10 @@ impl std::fmt::Debug for Allocator {
 
 impl std::fmt::Debug for Region {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(
-            f,
-            "Region {{ id: {:?}, rectangle: {:?} }}",
-            self.0.id, self.0.rectangle
-        )
+        f.debug_struct("Region")
+            .field("id", &self.allocation.id)
+            .field("rectangle", &self.allocation.rectangle)
+            .field("padding", &self.padding)
+            .finish()
     }
 }
-- 
cgit 


From d06d06e05096e0145be74fd02d67eada0a1665a1 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:10:19 +0100
Subject: Deallocate atlas entries and remove padding

---
 wgpu/src/image.rs                   |  8 +++---
 wgpu/src/image/raster.rs            | 15 ++++++++--
 wgpu/src/image/vector.rs            | 12 ++++++--
 wgpu/src/texture/atlas.rs           | 38 ++++++++++++++++++++++++-
 wgpu/src/texture/atlas/allocator.rs | 57 +++++++++++++------------------------
 5 files changed, 84 insertions(+), 46 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 1ffa50d2..7155b540 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -415,10 +415,10 @@ impl Pipeline {
 
     pub fn trim_cache(&mut self) {
         #[cfg(feature = "image")]
-        self.raster_cache.borrow_mut().trim();
+        self.raster_cache.borrow_mut().trim(&mut self.texture_atlas);
 
         #[cfg(feature = "svg")]
-        self.vector_cache.borrow_mut().trim();
+        self.vector_cache.borrow_mut().trim(&mut self.texture_atlas);
     }
 }
 
@@ -531,8 +531,8 @@ fn add_instance(
             (y as f32 + 0.5) / atlas::SIZE as f32,
         ],
         _size_in_atlas: [
-            (width as f32 - 0.5) / atlas::SIZE as f32,
-            (height as f32 - 0.5) / atlas::SIZE as f32,
+            (width as f32 - 1.0) / atlas::SIZE as f32,
+            (height as f32 - 1.0) / atlas::SIZE as f32,
         ],
         _layer: layer as u32,
     };
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index b19da582..cae8e065 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -95,10 +95,21 @@ impl Cache {
         }
     }
 
-    pub fn trim(&mut self) {
+    pub fn trim(&mut self, atlas: &mut Atlas) {
         let hits = &self.hits;
 
-        self.map.retain(|k, _| hits.contains(k));
+        self.map.retain(|k, memory| {
+            let retain = hits.contains(k);
+
+            if !retain {
+                if let Memory::Device(entry) = memory {
+                    atlas.remove(entry);
+                }
+            }
+
+            retain
+        });
+
         self.hits.clear();
     }
 
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 0dabc9ca..e7eb4906 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -130,12 +130,20 @@ impl Cache {
         }
     }
 
-    pub fn trim(&mut self) {
+    pub fn trim(&mut self, atlas: &mut Atlas) {
         let svg_hits = &self.svg_hits;
         let rasterized_hits = &self.rasterized_hits;
 
         self.svgs.retain(|k, _| svg_hits.contains(k));
-        self.rasterized.retain(|k, _| rasterized_hits.contains(k));
+        self.rasterized.retain(|k, entry| {
+            let retain = rasterized_hits.contains(k);
+
+            if !retain {
+                atlas.remove(entry);
+            }
+
+            retain
+        });
         self.svg_hits.clear();
         self.rasterized_hits.clear();
     }
diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
index 3d4e81c1..a76c035b 100644
--- a/wgpu/src/texture/atlas.rs
+++ b/wgpu/src/texture/atlas.rs
@@ -112,11 +112,47 @@ impl Atlas {
             }
         }
 
-        log::info!("Current atlas: {:?}", &self);
+        log::info!("Current atlas: {:?}", self);
 
         Some(entry)
     }
 
+    pub fn remove(&mut self, entry: &Entry) {
+        log::info!("Removing atlas entry: {:?}", entry);
+
+        match entry {
+            Entry::Contiguous(allocation) => {
+                self.deallocate(allocation);
+            }
+            Entry::Fragmented { fragments, .. } => {
+                for fragment in fragments {
+                    self.deallocate(&fragment.allocation);
+                }
+            }
+        }
+    }
+
+    fn deallocate(&mut self, allocation: &Allocation) {
+        log::info!("Deallocating atlas: {:?}", allocation);
+
+        match allocation {
+            Allocation::Full { layer } => {
+                self.layers[*layer] = Layer::Empty;
+            }
+            Allocation::Partial { layer, region } => {
+                let layer = &mut self.layers[*layer];
+
+                if let Layer::Busy(allocator) = layer {
+                    allocator.deallocate(region);
+
+                    if allocator.is_empty() {
+                        *layer = Layer::Empty;
+                    }
+                }
+            }
+        }
+    }
+
     fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
         // Allocate one layer if texture fits perfectly
         if width == SIZE && height == SIZE {
diff --git a/wgpu/src/texture/atlas/allocator.rs b/wgpu/src/texture/atlas/allocator.rs
index ad111212..7a4ff5b1 100644
--- a/wgpu/src/texture/atlas/allocator.rs
+++ b/wgpu/src/texture/atlas/allocator.rs
@@ -2,70 +2,54 @@ use guillotiere::{AtlasAllocator, Size};
 
 pub struct Allocator {
     raw: AtlasAllocator,
-    size: u32,
+    allocations: usize,
 }
 
 impl Allocator {
-    const PADDING: u32 = 1;
-
     pub fn new(size: u32) -> Allocator {
         let raw = AtlasAllocator::new(Size::new(size as i32, size as i32));
 
-        Allocator { raw, size }
+        Allocator {
+            raw,
+            allocations: 0,
+        }
     }
 
     pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
-        let padding = (
-            if width + Self::PADDING * 2 < self.size {
-                Self::PADDING
-            } else {
-                0
-            },
-            if height + Self::PADDING * 2 < self.size {
-                Self::PADDING
-            } else {
-                0
-            },
-        );
-
-        let allocation = self.raw.allocate(Size::new(
-            (width + padding.0 * 2) as i32,
-            (height + padding.1 * 2) as i32,
-        ))?;
-
-        Some(Region {
-            allocation,
-            padding,
-        })
+        let allocation =
+            self.raw.allocate(Size::new(width as i32, height as i32))?;
+
+        self.allocations += 1;
+
+        Some(Region { allocation })
     }
 
-    pub fn deallocate(&mut self, region: Region) {
+    pub fn deallocate(&mut self, region: &Region) {
         self.raw.deallocate(region.allocation.id);
+
+        self.allocations = self.allocations.saturating_sub(1);
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.allocations == 0
     }
 }
 
 pub struct Region {
     allocation: guillotiere::Allocation,
-    padding: (u32, u32),
 }
 
 impl Region {
     pub fn position(&self) -> (u32, u32) {
         let rectangle = &self.allocation.rectangle;
 
-        (
-            rectangle.min.x as u32 + self.padding.0,
-            rectangle.min.y as u32 + self.padding.1,
-        )
+        (rectangle.min.x as u32, rectangle.min.y as u32)
     }
 
     pub fn size(&self) -> (u32, u32) {
         let size = self.allocation.rectangle.size();
 
-        (
-            size.width as u32 - self.padding.0 * 2,
-            size.height as u32 - self.padding.1 * 2,
-        )
+        (size.width as u32, size.height as u32)
     }
 }
 
@@ -80,7 +64,6 @@ impl std::fmt::Debug for Region {
         f.debug_struct("Region")
             .field("id", &self.allocation.id)
             .field("rectangle", &self.allocation.rectangle)
-            .field("padding", &self.padding)
             .finish()
     }
 }
-- 
cgit 


From 883a9f22e2e868fa0f85b1ac251392c12bf83696 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:11:01 +0100
Subject: Add `env_logger` to `svg` example

---
 examples/svg/Cargo.toml  |  1 +
 examples/svg/src/main.rs | 11 ++++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/examples/svg/Cargo.toml b/examples/svg/Cargo.toml
index d8f83ac2..161ee6a8 100644
--- a/examples/svg/Cargo.toml
+++ b/examples/svg/Cargo.toml
@@ -7,3 +7,4 @@ publish = false
 
 [dependencies]
 iced = { path = "../..", features = ["svg"] }
+env_logger = "0.7"
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 1fb80534..811fdfb5 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,6 +1,8 @@
 use iced::{Column, Container, Element, Length, Sandbox, Settings, Svg};
 
 pub fn main() {
+    env_logger::init();
+
     Tiger::run(Settings::default())
 }
 
@@ -22,9 +24,12 @@ impl Sandbox for Tiger {
 
     fn view(&mut self) -> Element<()> {
         let content = Column::new().padding(20).push(
-            Svg::new(format!("{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR")))
-                .width(Length::Fill)
-                .height(Length::Fill),
+            Svg::new(format!(
+                "{}/resources/tiger.svg",
+                env!("CARGO_MANIFEST_DIR")
+            ))
+            .width(Length::Fill)
+            .height(Length::Fill),
         );
 
         Container::new(content)
-- 
cgit 


From 6cb7fb6d52a25dc69f44c0ed1bd8d0254b6b213f Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:35:39 +0100
Subject: Remove unused code warnings in `iced_wgpu::image`

---
 wgpu/src/image.rs | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 7155b540..dc19cfbf 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -3,13 +3,16 @@ mod raster;
 #[cfg(feature = "svg")]
 mod vector;
 
-use crate::{
-    texture::{self, atlas},
-    Transformation,
-};
+use crate::{texture, Transformation};
 
 use iced_native::{image, svg, Rectangle};
-use std::{cell::RefCell, mem};
+use std::mem;
+
+#[cfg(any(feature = "image", feature = "svg"))]
+use std::cell::RefCell;
+
+#[cfg(any(feature = "image", feature = "svg"))]
+use crate::texture::atlas;
 
 #[derive(Debug)]
 pub struct Pipeline {
@@ -271,7 +274,7 @@ impl Pipeline {
         target: &wgpu::TextureView,
         _scale: f32,
     ) {
-        let mut instances: Vec<Instance> = Vec::new();
+        let instances: &mut Vec<Instance> = &mut Vec::new();
 
         #[cfg(feature = "image")]
         let mut raster_cache = self.raster_cache.borrow_mut();
@@ -290,7 +293,7 @@ impl Pipeline {
                             encoder,
                             &mut self.texture_atlas,
                         ) {
-                            add_instances(image, atlas_entry, &mut instances);
+                            add_instances(image, atlas_entry, instances);
                         }
                     };
                 }
@@ -305,7 +308,7 @@ impl Pipeline {
                             encoder,
                             &mut self.texture_atlas,
                         ) {
-                            add_instances(image, atlas_entry, &mut instances);
+                            add_instances(image, atlas_entry, instances);
                         }
                     };
                 }
@@ -476,6 +479,7 @@ struct Uniforms {
     transform: [f32; 16],
 }
 
+#[cfg(any(feature = "image", feature = "svg"))]
 fn add_instances(
     image: &Image,
     entry: &atlas::Entry,
@@ -512,6 +516,7 @@ fn add_instances(
     }
 }
 
+#[cfg(any(feature = "image", feature = "svg"))]
 #[inline]
 fn add_instance(
     position: [f32; 2],
-- 
cgit 


From deedf6e8b66c5564c020dd61b72fdc71d8dd1eae Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:36:52 +0100
Subject: Make new `texture` module private for now

---
 wgpu/src/lib.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 832da31d..a807b44d 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -19,14 +19,13 @@
 //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
 //! [WebGPU API]: https://gpuweb.github.io/gpuweb/
 //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
-//#![deny(missing_docs)]
+#![deny(missing_docs)]
 #![deny(missing_debug_implementations)]
 #![deny(unused_results)]
 #![forbid(unsafe_code)]
 #![forbid(rust_2018_idioms)]
 pub mod defaults;
 pub mod settings;
-pub mod texture;
 pub mod triangle;
 pub mod widget;
 pub mod window;
@@ -37,6 +36,7 @@ mod quad;
 mod renderer;
 mod target;
 mod text;
+mod texture;
 mod transformation;
 mod viewport;
 
-- 
cgit 


From 271725faa585ba14207a9bb27ab95fe27473279d Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:47:27 +0100
Subject: Derive `Debug` for `raster::Memory`

---
 wgpu/src/image/raster.rs | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index cae8e065..883c32f7 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -2,6 +2,7 @@ use crate::texture::atlas::{self, Atlas};
 use iced_native::image;
 use std::collections::{HashMap, HashSet};
 
+#[derive(Debug)]
 pub enum Memory {
     Host(::image::ImageBuffer<::image::Bgra<u8>, Vec<u8>>),
     Device(atlas::Entry),
@@ -20,17 +21,6 @@ impl Memory {
     }
 }
 
-impl std::fmt::Debug for Memory {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Memory::Host(_) => write!(f, "Memory::Host"),
-            Memory::Device(_) => write!(f, "Memory::Device"),
-            Memory::NotFound => write!(f, "Memory::NotFound"),
-            Memory::Invalid => write!(f, "Memory::Invalid"),
-        }
-    }
-}
-
 #[derive(Debug)]
 pub struct Cache {
     map: HashMap<u64, Memory>,
-- 
cgit 


From bb397cc66808dad14eace4fa40edf2e9bebf42bb Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:47:37 +0100
Subject: Move `Debug` implementation for `vector::Svg`

---
 wgpu/src/image/vector.rs | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index e7eb4906..6b12df54 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -20,15 +20,7 @@ impl Svg {
     }
 }
 
-impl std::fmt::Debug for Svg {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Svg::Loaded(_) => write!(f, "Svg::Loaded"),
-            Svg::NotFound => write!(f, "Svg::NotFound"),
-        }
-    }
-}
-
+#[derive(Debug)]
 pub struct Cache {
     svgs: HashMap<u64, Svg>,
     rasterized: HashMap<(u64, u32, u32), atlas::Entry>,
@@ -149,8 +141,11 @@ impl Cache {
     }
 }
 
-impl std::fmt::Debug for Cache {
+impl std::fmt::Debug for Svg {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "vector::Cache")
+        match self {
+            Svg::Loaded(_) => write!(f, "Svg::Loaded"),
+            Svg::NotFound => write!(f, "Svg::NotFound"),
+        }
     }
 }
-- 
cgit 


From fc55e3a3df9e08d361985b01528d2a6095d98aba Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 26 Feb 2020 20:50:32 +0100
Subject: Move `Atlas::deallocate` after `allocate`

---
 wgpu/src/texture/atlas.rs | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
index a76c035b..86a5ff49 100644
--- a/wgpu/src/texture/atlas.rs
+++ b/wgpu/src/texture/atlas.rs
@@ -132,27 +132,6 @@ impl Atlas {
         }
     }
 
-    fn deallocate(&mut self, allocation: &Allocation) {
-        log::info!("Deallocating atlas: {:?}", allocation);
-
-        match allocation {
-            Allocation::Full { layer } => {
-                self.layers[*layer] = Layer::Empty;
-            }
-            Allocation::Partial { layer, region } => {
-                let layer = &mut self.layers[*layer];
-
-                if let Layer::Busy(allocator) = layer {
-                    allocator.deallocate(region);
-
-                    if allocator.is_empty() {
-                        *layer = Layer::Empty;
-                    }
-                }
-            }
-        }
-    }
-
     fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
         // Allocate one layer if texture fits perfectly
         if width == SIZE && height == SIZE {
@@ -251,6 +230,27 @@ impl Atlas {
         None
     }
 
+    fn deallocate(&mut self, allocation: &Allocation) {
+        log::info!("Deallocating atlas: {:?}", allocation);
+
+        match allocation {
+            Allocation::Full { layer } => {
+                self.layers[*layer] = Layer::Empty;
+            }
+            Allocation::Partial { layer, region } => {
+                let layer = &mut self.layers[*layer];
+
+                if let Layer::Busy(allocator) = layer {
+                    allocator.deallocate(region);
+
+                    if allocator.is_empty() {
+                        *layer = Layer::Empty;
+                    }
+                }
+            }
+        }
+    }
+
     fn upload_allocation(
         &mut self,
         buffer: &wgpu::Buffer,
-- 
cgit 


From 4e7159c22c6be90f61aa715d5eb6811f805cb597 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Fri, 28 Feb 2020 14:38:42 +0100
Subject: Stop creating image pipeline when unnecessary

---
 wgpu/src/image.rs                    |  74 +++----
 wgpu/src/image/atlas.rs              | 361 +++++++++++++++++++++++++++++++++++
 wgpu/src/image/atlas/allocation.rs   |  35 ++++
 wgpu/src/image/atlas/allocator.rs    |  69 +++++++
 wgpu/src/image/atlas/entry.rs        |  26 +++
 wgpu/src/image/atlas/layer.rs        |  17 ++
 wgpu/src/image/raster.rs             |   2 +-
 wgpu/src/image/vector.rs             |   2 +-
 wgpu/src/lib.rs                      |   9 +-
 wgpu/src/renderer.rs                 | 103 ++++++----
 wgpu/src/texture.rs                  |   3 -
 wgpu/src/texture/atlas.rs            | 361 -----------------------------------
 wgpu/src/texture/atlas/allocation.rs |  35 ----
 wgpu/src/texture/atlas/allocator.rs  |  69 -------
 wgpu/src/texture/atlas/entry.rs      |  25 ---
 wgpu/src/texture/atlas/layer.rs      |  17 --
 16 files changed, 618 insertions(+), 590 deletions(-)
 create mode 100644 wgpu/src/image/atlas.rs
 create mode 100644 wgpu/src/image/atlas/allocation.rs
 create mode 100644 wgpu/src/image/atlas/allocator.rs
 create mode 100644 wgpu/src/image/atlas/entry.rs
 create mode 100644 wgpu/src/image/atlas/layer.rs
 delete mode 100644 wgpu/src/texture.rs
 delete mode 100644 wgpu/src/texture/atlas.rs
 delete mode 100644 wgpu/src/texture/atlas/allocation.rs
 delete mode 100644 wgpu/src/texture/atlas/allocator.rs
 delete mode 100644 wgpu/src/texture/atlas/entry.rs
 delete mode 100644 wgpu/src/texture/atlas/layer.rs

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index dc19cfbf..d3603676 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -1,18 +1,23 @@
+mod atlas;
+
 #[cfg(feature = "image")]
 mod raster;
+
 #[cfg(feature = "svg")]
 mod vector;
 
-use crate::{texture, Transformation};
+use crate::Transformation;
+use atlas::Atlas;
 
-use iced_native::{image, svg, Rectangle};
+use iced_native::Rectangle;
+use std::cell::RefCell;
 use std::mem;
 
-#[cfg(any(feature = "image", feature = "svg"))]
-use std::cell::RefCell;
+#[cfg(feature = "image")]
+use iced_native::image;
 
-#[cfg(any(feature = "image", feature = "svg"))]
-use crate::texture::atlas;
+#[cfg(feature = "svg")]
+use iced_native::svg;
 
 #[derive(Debug)]
 pub struct Pipeline {
@@ -30,7 +35,7 @@ pub struct Pipeline {
     texture: wgpu::BindGroup,
     texture_version: usize,
     texture_layout: wgpu::BindGroupLayout,
-    texture_atlas: texture::Atlas,
+    texture_atlas: Atlas,
 }
 
 impl Pipeline {
@@ -216,7 +221,7 @@ impl Pipeline {
             usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,
         });
 
-        let texture_atlas = texture::Atlas::new(device);
+        let texture_atlas = Atlas::new(device);
 
         let texture = device.create_bind_group(&wgpu::BindGroupDescriptor {
             layout: &texture_layout,
@@ -284,33 +289,29 @@ impl Pipeline {
 
         for image in images {
             match &image.handle {
-                Handle::Raster(_handle) => {
-                    #[cfg(feature = "image")]
-                    {
-                        if let Some(atlas_entry) = raster_cache.upload(
-                            _handle,
-                            device,
-                            encoder,
-                            &mut self.texture_atlas,
-                        ) {
-                            add_instances(image, atlas_entry, instances);
-                        }
-                    };
+                #[cfg(feature = "image")]
+                Handle::Raster(handle) => {
+                    if let Some(atlas_entry) = raster_cache.upload(
+                        handle,
+                        device,
+                        encoder,
+                        &mut self.texture_atlas,
+                    ) {
+                        add_instances(image, atlas_entry, instances);
+                    }
                 }
-                Handle::Vector(_handle) => {
-                    #[cfg(feature = "svg")]
-                    {
-                        if let Some(atlas_entry) = vector_cache.upload(
-                            _handle,
-                            image.size,
-                            _scale,
-                            device,
-                            encoder,
-                            &mut self.texture_atlas,
-                        ) {
-                            add_instances(image, atlas_entry, instances);
-                        }
-                    };
+                #[cfg(feature = "svg")]
+                Handle::Vector(handle) => {
+                    if let Some(atlas_entry) = vector_cache.upload(
+                        handle,
+                        image.size,
+                        _scale,
+                        device,
+                        encoder,
+                        &mut self.texture_atlas,
+                    ) {
+                        add_instances(image, atlas_entry, instances);
+                    }
                 }
             }
         }
@@ -432,7 +433,10 @@ pub struct Image {
 }
 
 pub enum Handle {
+    #[cfg(feature = "image")]
     Raster(image::Handle),
+
+    #[cfg(feature = "svg")]
     Vector(svg::Handle),
 }
 
@@ -479,7 +483,6 @@ struct Uniforms {
     transform: [f32; 16],
 }
 
-#[cfg(any(feature = "image", feature = "svg"))]
 fn add_instances(
     image: &Image,
     entry: &atlas::Entry,
@@ -516,7 +519,6 @@ fn add_instances(
     }
 }
 
-#[cfg(any(feature = "image", feature = "svg"))]
 #[inline]
 fn add_instance(
     position: [f32; 2],
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
new file mode 100644
index 00000000..86a5ff49
--- /dev/null
+++ b/wgpu/src/image/atlas.rs
@@ -0,0 +1,361 @@
+pub mod entry;
+
+mod allocation;
+mod allocator;
+mod layer;
+
+pub use allocation::Allocation;
+pub use entry::Entry;
+pub use layer::Layer;
+
+use allocator::Allocator;
+
+pub const SIZE: u32 = 2048;
+
+#[derive(Debug)]
+pub struct Atlas {
+    texture: wgpu::Texture,
+    texture_view: wgpu::TextureView,
+    layers: Vec<Layer>,
+}
+
+impl Atlas {
+    pub fn new(device: &wgpu::Device) -> Self {
+        let extent = wgpu::Extent3d {
+            width: SIZE,
+            height: SIZE,
+            depth: 1,
+        };
+
+        let texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: extent,
+            array_layer_count: 2,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        let texture_view = texture.create_default_view();
+
+        Atlas {
+            texture,
+            texture_view,
+            layers: vec![Layer::Empty, Layer::Empty],
+        }
+    }
+
+    pub fn view(&self) -> &wgpu::TextureView {
+        &self.texture_view
+    }
+
+    pub fn layer_count(&self) -> usize {
+        self.layers.len()
+    }
+
+    pub fn upload<C>(
+        &mut self,
+        width: u32,
+        height: u32,
+        data: &[C],
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) -> Option<Entry>
+    where
+        C: Copy + 'static,
+    {
+        let entry = {
+            let current_size = self.layers.len();
+            let entry = self.allocate(width, height)?;
+
+            // We grow the internal texture after allocating if necessary
+            let new_layers = self.layers.len() - current_size;
+            self.grow(new_layers, device, encoder);
+
+            entry
+        };
+
+        log::info!("Allocated atlas entry: {:?}", entry);
+
+        let buffer = device
+            .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
+            .fill_from_slice(data);
+
+        match &entry {
+            Entry::Contiguous(allocation) => {
+                self.upload_allocation(
+                    &buffer,
+                    width,
+                    height,
+                    0,
+                    &allocation,
+                    encoder,
+                );
+            }
+            Entry::Fragmented { fragments, .. } => {
+                for fragment in fragments {
+                    let (x, y) = fragment.position;
+                    let offset = (y * width + x) as usize * 4;
+
+                    self.upload_allocation(
+                        &buffer,
+                        width,
+                        height,
+                        offset,
+                        &fragment.allocation,
+                        encoder,
+                    );
+                }
+            }
+        }
+
+        log::info!("Current atlas: {:?}", self);
+
+        Some(entry)
+    }
+
+    pub fn remove(&mut self, entry: &Entry) {
+        log::info!("Removing atlas entry: {:?}", entry);
+
+        match entry {
+            Entry::Contiguous(allocation) => {
+                self.deallocate(allocation);
+            }
+            Entry::Fragmented { fragments, .. } => {
+                for fragment in fragments {
+                    self.deallocate(&fragment.allocation);
+                }
+            }
+        }
+    }
+
+    fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
+        // Allocate one layer if texture fits perfectly
+        if width == SIZE && height == SIZE {
+            let mut empty_layers = self
+                .layers
+                .iter_mut()
+                .enumerate()
+                .filter(|(_, layer)| layer.is_empty());
+
+            if let Some((i, layer)) = empty_layers.next() {
+                *layer = Layer::Full;
+
+                return Some(Entry::Contiguous(Allocation::Full { layer: i }));
+            }
+
+            self.layers.push(Layer::Full);
+
+            return Some(Entry::Contiguous(Allocation::Full {
+                layer: self.layers.len() - 1,
+            }));
+        }
+
+        // Split big textures across multiple layers
+        if width > SIZE || height > SIZE {
+            let mut fragments = Vec::new();
+            let mut y = 0;
+
+            while y < height {
+                let height = std::cmp::min(height - y, SIZE);
+                let mut x = 0;
+
+                while x < width {
+                    let width = std::cmp::min(width - x, SIZE);
+
+                    let allocation = self.allocate(width, height)?;
+
+                    if let Entry::Contiguous(allocation) = allocation {
+                        fragments.push(entry::Fragment {
+                            position: (x, y),
+                            allocation,
+                        });
+                    }
+
+                    x += width;
+                }
+
+                y += height;
+            }
+
+            return Some(Entry::Fragmented {
+                size: (width, height),
+                fragments,
+            });
+        }
+
+        // Try allocating on an existing layer
+        for (i, layer) in self.layers.iter_mut().enumerate() {
+            match layer {
+                Layer::Empty => {
+                    let mut allocator = Allocator::new(SIZE);
+
+                    if let Some(region) = allocator.allocate(width, height) {
+                        *layer = Layer::Busy(allocator);
+
+                        return Some(Entry::Contiguous(Allocation::Partial {
+                            region,
+                            layer: i,
+                        }));
+                    }
+                }
+                Layer::Busy(allocator) => {
+                    if let Some(region) = allocator.allocate(width, height) {
+                        return Some(Entry::Contiguous(Allocation::Partial {
+                            region,
+                            layer: i,
+                        }));
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        // Create new layer with atlas allocator
+        let mut allocator = Allocator::new(SIZE);
+
+        if let Some(region) = allocator.allocate(width, height) {
+            self.layers.push(Layer::Busy(allocator));
+
+            return Some(Entry::Contiguous(Allocation::Partial {
+                region,
+                layer: self.layers.len() - 1,
+            }));
+        }
+
+        // We ran out of memory (?)
+        None
+    }
+
+    fn deallocate(&mut self, allocation: &Allocation) {
+        log::info!("Deallocating atlas: {:?}", allocation);
+
+        match allocation {
+            Allocation::Full { layer } => {
+                self.layers[*layer] = Layer::Empty;
+            }
+            Allocation::Partial { layer, region } => {
+                let layer = &mut self.layers[*layer];
+
+                if let Layer::Busy(allocator) = layer {
+                    allocator.deallocate(region);
+
+                    if allocator.is_empty() {
+                        *layer = Layer::Empty;
+                    }
+                }
+            }
+        }
+    }
+
+    fn upload_allocation(
+        &mut self,
+        buffer: &wgpu::Buffer,
+        image_width: u32,
+        image_height: u32,
+        offset: usize,
+        allocation: &Allocation,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        let (x, y) = allocation.position();
+        let (width, height) = allocation.size();
+        let layer = allocation.layer();
+
+        let extent = wgpu::Extent3d {
+            width,
+            height,
+            depth: 1,
+        };
+
+        encoder.copy_buffer_to_texture(
+            wgpu::BufferCopyView {
+                buffer,
+                offset: offset as u64,
+                row_pitch: 4 * image_width,
+                image_height,
+            },
+            wgpu::TextureCopyView {
+                texture: &self.texture,
+                array_layer: layer as u32,
+                mip_level: 0,
+                origin: wgpu::Origin3d {
+                    x: x as f32,
+                    y: y as f32,
+                    z: 0.0,
+                },
+            },
+            extent,
+        );
+    }
+
+    fn grow(
+        &mut self,
+        amount: usize,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+    ) {
+        if amount == 0 {
+            return;
+        }
+
+        let new_texture = device.create_texture(&wgpu::TextureDescriptor {
+            size: wgpu::Extent3d {
+                width: SIZE,
+                height: SIZE,
+                depth: 1,
+            },
+            array_layer_count: self.layers.len() as u32,
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: wgpu::TextureDimension::D2,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            usage: wgpu::TextureUsage::COPY_DST
+                | wgpu::TextureUsage::COPY_SRC
+                | wgpu::TextureUsage::SAMPLED,
+        });
+
+        let amount_to_copy = self.layers.len() - amount;
+
+        for (i, layer) in
+            self.layers.iter_mut().take(amount_to_copy).enumerate()
+        {
+            if layer.is_empty() {
+                continue;
+            }
+
+            encoder.copy_texture_to_texture(
+                wgpu::TextureCopyView {
+                    texture: &self.texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::TextureCopyView {
+                    texture: &new_texture,
+                    array_layer: i as u32,
+                    mip_level: 0,
+                    origin: wgpu::Origin3d {
+                        x: 0.0,
+                        y: 0.0,
+                        z: 0.0,
+                    },
+                },
+                wgpu::Extent3d {
+                    width: SIZE,
+                    height: SIZE,
+                    depth: 1,
+                },
+            );
+        }
+
+        self.texture = new_texture;
+        self.texture_view = self.texture.create_default_view();
+    }
+}
diff --git a/wgpu/src/image/atlas/allocation.rs b/wgpu/src/image/atlas/allocation.rs
new file mode 100644
index 00000000..59b7239f
--- /dev/null
+++ b/wgpu/src/image/atlas/allocation.rs
@@ -0,0 +1,35 @@
+use crate::image::atlas::{self, allocator};
+
+#[derive(Debug)]
+pub enum Allocation {
+    Partial {
+        layer: usize,
+        region: allocator::Region,
+    },
+    Full {
+        layer: usize,
+    },
+}
+
+impl Allocation {
+    pub fn position(&self) -> (u32, u32) {
+        match self {
+            Allocation::Partial { region, .. } => region.position(),
+            Allocation::Full { .. } => (0, 0),
+        }
+    }
+
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            Allocation::Partial { region, .. } => region.size(),
+            Allocation::Full { .. } => (atlas::SIZE, atlas::SIZE),
+        }
+    }
+
+    pub fn layer(&self) -> usize {
+        match self {
+            Allocation::Partial { layer, .. } => *layer,
+            Allocation::Full { layer } => *layer,
+        }
+    }
+}
diff --git a/wgpu/src/image/atlas/allocator.rs b/wgpu/src/image/atlas/allocator.rs
new file mode 100644
index 00000000..7a4ff5b1
--- /dev/null
+++ b/wgpu/src/image/atlas/allocator.rs
@@ -0,0 +1,69 @@
+use guillotiere::{AtlasAllocator, Size};
+
+pub struct Allocator {
+    raw: AtlasAllocator,
+    allocations: usize,
+}
+
+impl Allocator {
+    pub fn new(size: u32) -> Allocator {
+        let raw = AtlasAllocator::new(Size::new(size as i32, size as i32));
+
+        Allocator {
+            raw,
+            allocations: 0,
+        }
+    }
+
+    pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
+        let allocation =
+            self.raw.allocate(Size::new(width as i32, height as i32))?;
+
+        self.allocations += 1;
+
+        Some(Region { allocation })
+    }
+
+    pub fn deallocate(&mut self, region: &Region) {
+        self.raw.deallocate(region.allocation.id);
+
+        self.allocations = self.allocations.saturating_sub(1);
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.allocations == 0
+    }
+}
+
+pub struct Region {
+    allocation: guillotiere::Allocation,
+}
+
+impl Region {
+    pub fn position(&self) -> (u32, u32) {
+        let rectangle = &self.allocation.rectangle;
+
+        (rectangle.min.x as u32, rectangle.min.y as u32)
+    }
+
+    pub fn size(&self) -> (u32, u32) {
+        let size = self.allocation.rectangle.size();
+
+        (size.width as u32, size.height as u32)
+    }
+}
+
+impl std::fmt::Debug for Allocator {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Allocator")
+    }
+}
+
+impl std::fmt::Debug for Region {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Region")
+            .field("id", &self.allocation.id)
+            .field("rectangle", &self.allocation.rectangle)
+            .finish()
+    }
+}
diff --git a/wgpu/src/image/atlas/entry.rs b/wgpu/src/image/atlas/entry.rs
new file mode 100644
index 00000000..0310fc54
--- /dev/null
+++ b/wgpu/src/image/atlas/entry.rs
@@ -0,0 +1,26 @@
+use crate::image::atlas;
+
+#[derive(Debug)]
+pub enum Entry {
+    Contiguous(atlas::Allocation),
+    Fragmented {
+        size: (u32, u32),
+        fragments: Vec<Fragment>,
+    },
+}
+
+impl Entry {
+    #[cfg(feature = "image")]
+    pub fn size(&self) -> (u32, u32) {
+        match self {
+            Entry::Contiguous(allocation) => allocation.size(),
+            Entry::Fragmented { size, .. } => *size,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct Fragment {
+    pub position: (u32, u32),
+    pub allocation: atlas::Allocation,
+}
diff --git a/wgpu/src/image/atlas/layer.rs b/wgpu/src/image/atlas/layer.rs
new file mode 100644
index 00000000..b1084ed9
--- /dev/null
+++ b/wgpu/src/image/atlas/layer.rs
@@ -0,0 +1,17 @@
+use crate::image::atlas::Allocator;
+
+#[derive(Debug)]
+pub enum Layer {
+    Empty,
+    Busy(Allocator),
+    Full,
+}
+
+impl Layer {
+    pub fn is_empty(&self) -> bool {
+        match self {
+            Layer::Empty => true,
+            _ => false,
+        }
+    }
+}
diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs
index 883c32f7..3edec57e 100644
--- a/wgpu/src/image/raster.rs
+++ b/wgpu/src/image/raster.rs
@@ -1,4 +1,4 @@
-use crate::texture::atlas::{self, Atlas};
+use crate::image::atlas::{self, Atlas};
 use iced_native::image;
 use std::collections::{HashMap, HashSet};
 
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 6b12df54..bae0f82f 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -1,4 +1,4 @@
-use crate::texture::atlas::{self, Atlas};
+use crate::image::atlas::{self, Atlas};
 use iced_native::svg;
 use std::collections::{HashMap, HashSet};
 
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index a807b44d..1d63abbf 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -30,13 +30,11 @@ pub mod triangle;
 pub mod widget;
 pub mod window;
 
-mod image;
 mod primitive;
 mod quad;
 mod renderer;
 mod target;
 mod text;
-mod texture;
 mod transformation;
 mod viewport;
 
@@ -52,6 +50,11 @@ pub use viewport::Viewport;
 #[doc(no_inline)]
 pub use widget::*;
 
-pub(crate) use self::image::Image;
 pub(crate) use quad::Quad;
 pub(crate) use transformation::Transformation;
+
+#[cfg(any(feature = "image", feature = "svg"))]
+mod image;
+
+#[cfg(any(feature = "image", feature = "svg"))]
+pub(crate) use self::image::Image;
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index b5dce480..d9ef9fc4 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -1,7 +1,11 @@
 use crate::{
-    image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
-    Target, Transformation,
+    quad, text, triangle, Defaults, Primitive, Quad, Settings, Target,
+    Transformation,
 };
+
+#[cfg(any(feature = "image", feature = "svg"))]
+use crate::{image, Image};
+
 use iced_native::{
     layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
     Widget,
@@ -16,18 +20,22 @@ mod widget;
 #[derive(Debug)]
 pub struct Renderer {
     quad_pipeline: quad::Pipeline,
-    image_pipeline: image::Pipeline,
     text_pipeline: text::Pipeline,
-    triangle_pipeline: crate::triangle::Pipeline,
+    triangle_pipeline: triangle::Pipeline,
+
+    #[cfg(any(feature = "image", feature = "svg"))]
+    image_pipeline: image::Pipeline,
 }
 
 struct Layer<'a> {
     bounds: Rectangle<u32>,
     offset: Vector<u32>,
     quads: Vec<Quad>,
-    images: Vec<Image>,
     meshes: Vec<(Point, Arc<triangle::Mesh2D>)>,
     text: Vec<wgpu_glyph::Section<'a>>,
+
+    #[cfg(any(feature = "image", feature = "svg"))]
+    images: Vec<Image>,
 }
 
 impl<'a> Layer<'a> {
@@ -36,9 +44,11 @@ impl<'a> Layer<'a> {
             bounds,
             offset,
             quads: Vec::new(),
-            images: Vec::new(),
             text: Vec::new(),
             meshes: Vec::new(),
+
+            #[cfg(any(feature = "image", feature = "svg"))]
+            images: Vec::new(),
         }
     }
 }
@@ -51,19 +61,22 @@ impl Renderer {
         let text_pipeline =
             text::Pipeline::new(device, settings.format, settings.default_font);
         let quad_pipeline = quad::Pipeline::new(device, settings.format);
-        let image_pipeline =
-            crate::image::Pipeline::new(device, settings.format);
         let triangle_pipeline = triangle::Pipeline::new(
             device,
             settings.format,
             settings.antialiasing,
         );
 
+        #[cfg(any(feature = "image", feature = "svg"))]
+        let image_pipeline = image::Pipeline::new(device, settings.format);
+
         Self {
             quad_pipeline,
-            image_pipeline,
             text_pipeline,
             triangle_pipeline,
+
+            #[cfg(any(feature = "image", feature = "svg"))]
+            image_pipeline,
         }
     }
 
@@ -116,6 +129,7 @@ impl Renderer {
             );
         }
 
+        #[cfg(any(feature = "image", feature = "svg"))]
         self.image_pipeline.trim_cache();
 
         *mouse_cursor
@@ -223,20 +237,6 @@ impl Renderer {
                     border_color: border_color.into_linear(),
                 });
             }
-            Primitive::Image { handle, bounds } => {
-                layer.images.push(Image {
-                    handle: image::Handle::Raster(handle.clone()),
-                    position: [bounds.x, bounds.y],
-                    size: [bounds.width, bounds.height],
-                });
-            }
-            Primitive::Svg { handle, bounds } => {
-                layer.images.push(Image {
-                    handle: image::Handle::Vector(handle.clone()),
-                    position: [bounds.x, bounds.y],
-                    size: [bounds.width, bounds.height],
-                });
-            }
             Primitive::Mesh2D { origin, buffers } => {
                 layer.meshes.push((*origin, buffers.clone()));
             }
@@ -264,6 +264,28 @@ impl Renderer {
                     layers.push(new_layer);
                 }
             }
+
+            #[cfg(feature = "image")]
+            Primitive::Image { handle, bounds } => {
+                layer.images.push(Image {
+                    handle: image::Handle::Raster(handle.clone()),
+                    position: [bounds.x, bounds.y],
+                    size: [bounds.width, bounds.height],
+                });
+            }
+            #[cfg(not(feature = "image"))]
+            Primitive::Image { .. } => {}
+
+            #[cfg(feature = "svg")]
+            Primitive::Svg { handle, bounds } => {
+                layer.images.push(Image {
+                    handle: image::Handle::Vector(handle.clone()),
+                    position: [bounds.x, bounds.y],
+                    size: [bounds.width, bounds.height],
+                });
+            }
+            #[cfg(not(feature = "svg"))]
+            Primitive::Svg { .. } => {}
         }
     }
 
@@ -346,23 +368,26 @@ impl Renderer {
             );
         }
 
-        if layer.images.len() > 0 {
-            let translated_and_scaled = transformation
-                * Transformation::scale(scale_factor, scale_factor)
-                * Transformation::translate(
-                    -(layer.offset.x as f32),
-                    -(layer.offset.y as f32),
+        #[cfg(any(feature = "image", feature = "svg"))]
+        {
+            if layer.images.len() > 0 {
+                let translated_and_scaled = transformation
+                    * Transformation::scale(scale_factor, scale_factor)
+                    * Transformation::translate(
+                        -(layer.offset.x as f32),
+                        -(layer.offset.y as f32),
+                    );
+
+                self.image_pipeline.draw(
+                    device,
+                    encoder,
+                    &layer.images,
+                    translated_and_scaled,
+                    bounds,
+                    target,
+                    scale_factor,
                 );
-
-            self.image_pipeline.draw(
-                device,
-                encoder,
-                &layer.images,
-                translated_and_scaled,
-                bounds,
-                target,
-                scale_factor,
-            );
+            }
         }
 
         if layer.text.len() > 0 {
diff --git a/wgpu/src/texture.rs b/wgpu/src/texture.rs
deleted file mode 100644
index 00b60bfa..00000000
--- a/wgpu/src/texture.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub mod atlas;
-
-pub use atlas::Atlas;
diff --git a/wgpu/src/texture/atlas.rs b/wgpu/src/texture/atlas.rs
deleted file mode 100644
index 86a5ff49..00000000
--- a/wgpu/src/texture/atlas.rs
+++ /dev/null
@@ -1,361 +0,0 @@
-pub mod entry;
-
-mod allocation;
-mod allocator;
-mod layer;
-
-pub use allocation::Allocation;
-pub use entry::Entry;
-pub use layer::Layer;
-
-use allocator::Allocator;
-
-pub const SIZE: u32 = 2048;
-
-#[derive(Debug)]
-pub struct Atlas {
-    texture: wgpu::Texture,
-    texture_view: wgpu::TextureView,
-    layers: Vec<Layer>,
-}
-
-impl Atlas {
-    pub fn new(device: &wgpu::Device) -> Self {
-        let extent = wgpu::Extent3d {
-            width: SIZE,
-            height: SIZE,
-            depth: 1,
-        };
-
-        let texture = device.create_texture(&wgpu::TextureDescriptor {
-            size: extent,
-            array_layer_count: 2,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
-        let texture_view = texture.create_default_view();
-
-        Atlas {
-            texture,
-            texture_view,
-            layers: vec![Layer::Empty, Layer::Empty],
-        }
-    }
-
-    pub fn view(&self) -> &wgpu::TextureView {
-        &self.texture_view
-    }
-
-    pub fn layer_count(&self) -> usize {
-        self.layers.len()
-    }
-
-    pub fn upload<C>(
-        &mut self,
-        width: u32,
-        height: u32,
-        data: &[C],
-        device: &wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-    ) -> Option<Entry>
-    where
-        C: Copy + 'static,
-    {
-        let entry = {
-            let current_size = self.layers.len();
-            let entry = self.allocate(width, height)?;
-
-            // We grow the internal texture after allocating if necessary
-            let new_layers = self.layers.len() - current_size;
-            self.grow(new_layers, device, encoder);
-
-            entry
-        };
-
-        log::info!("Allocated atlas entry: {:?}", entry);
-
-        let buffer = device
-            .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
-            .fill_from_slice(data);
-
-        match &entry {
-            Entry::Contiguous(allocation) => {
-                self.upload_allocation(
-                    &buffer,
-                    width,
-                    height,
-                    0,
-                    &allocation,
-                    encoder,
-                );
-            }
-            Entry::Fragmented { fragments, .. } => {
-                for fragment in fragments {
-                    let (x, y) = fragment.position;
-                    let offset = (y * width + x) as usize * 4;
-
-                    self.upload_allocation(
-                        &buffer,
-                        width,
-                        height,
-                        offset,
-                        &fragment.allocation,
-                        encoder,
-                    );
-                }
-            }
-        }
-
-        log::info!("Current atlas: {:?}", self);
-
-        Some(entry)
-    }
-
-    pub fn remove(&mut self, entry: &Entry) {
-        log::info!("Removing atlas entry: {:?}", entry);
-
-        match entry {
-            Entry::Contiguous(allocation) => {
-                self.deallocate(allocation);
-            }
-            Entry::Fragmented { fragments, .. } => {
-                for fragment in fragments {
-                    self.deallocate(&fragment.allocation);
-                }
-            }
-        }
-    }
-
-    fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {
-        // Allocate one layer if texture fits perfectly
-        if width == SIZE && height == SIZE {
-            let mut empty_layers = self
-                .layers
-                .iter_mut()
-                .enumerate()
-                .filter(|(_, layer)| layer.is_empty());
-
-            if let Some((i, layer)) = empty_layers.next() {
-                *layer = Layer::Full;
-
-                return Some(Entry::Contiguous(Allocation::Full { layer: i }));
-            }
-
-            self.layers.push(Layer::Full);
-
-            return Some(Entry::Contiguous(Allocation::Full {
-                layer: self.layers.len() - 1,
-            }));
-        }
-
-        // Split big textures across multiple layers
-        if width > SIZE || height > SIZE {
-            let mut fragments = Vec::new();
-            let mut y = 0;
-
-            while y < height {
-                let height = std::cmp::min(height - y, SIZE);
-                let mut x = 0;
-
-                while x < width {
-                    let width = std::cmp::min(width - x, SIZE);
-
-                    let allocation = self.allocate(width, height)?;
-
-                    if let Entry::Contiguous(allocation) = allocation {
-                        fragments.push(entry::Fragment {
-                            position: (x, y),
-                            allocation,
-                        });
-                    }
-
-                    x += width;
-                }
-
-                y += height;
-            }
-
-            return Some(Entry::Fragmented {
-                size: (width, height),
-                fragments,
-            });
-        }
-
-        // Try allocating on an existing layer
-        for (i, layer) in self.layers.iter_mut().enumerate() {
-            match layer {
-                Layer::Empty => {
-                    let mut allocator = Allocator::new(SIZE);
-
-                    if let Some(region) = allocator.allocate(width, height) {
-                        *layer = Layer::Busy(allocator);
-
-                        return Some(Entry::Contiguous(Allocation::Partial {
-                            region,
-                            layer: i,
-                        }));
-                    }
-                }
-                Layer::Busy(allocator) => {
-                    if let Some(region) = allocator.allocate(width, height) {
-                        return Some(Entry::Contiguous(Allocation::Partial {
-                            region,
-                            layer: i,
-                        }));
-                    }
-                }
-                _ => {}
-            }
-        }
-
-        // Create new layer with atlas allocator
-        let mut allocator = Allocator::new(SIZE);
-
-        if let Some(region) = allocator.allocate(width, height) {
-            self.layers.push(Layer::Busy(allocator));
-
-            return Some(Entry::Contiguous(Allocation::Partial {
-                region,
-                layer: self.layers.len() - 1,
-            }));
-        }
-
-        // We ran out of memory (?)
-        None
-    }
-
-    fn deallocate(&mut self, allocation: &Allocation) {
-        log::info!("Deallocating atlas: {:?}", allocation);
-
-        match allocation {
-            Allocation::Full { layer } => {
-                self.layers[*layer] = Layer::Empty;
-            }
-            Allocation::Partial { layer, region } => {
-                let layer = &mut self.layers[*layer];
-
-                if let Layer::Busy(allocator) = layer {
-                    allocator.deallocate(region);
-
-                    if allocator.is_empty() {
-                        *layer = Layer::Empty;
-                    }
-                }
-            }
-        }
-    }
-
-    fn upload_allocation(
-        &mut self,
-        buffer: &wgpu::Buffer,
-        image_width: u32,
-        image_height: u32,
-        offset: usize,
-        allocation: &Allocation,
-        encoder: &mut wgpu::CommandEncoder,
-    ) {
-        let (x, y) = allocation.position();
-        let (width, height) = allocation.size();
-        let layer = allocation.layer();
-
-        let extent = wgpu::Extent3d {
-            width,
-            height,
-            depth: 1,
-        };
-
-        encoder.copy_buffer_to_texture(
-            wgpu::BufferCopyView {
-                buffer,
-                offset: offset as u64,
-                row_pitch: 4 * image_width,
-                image_height,
-            },
-            wgpu::TextureCopyView {
-                texture: &self.texture,
-                array_layer: layer as u32,
-                mip_level: 0,
-                origin: wgpu::Origin3d {
-                    x: x as f32,
-                    y: y as f32,
-                    z: 0.0,
-                },
-            },
-            extent,
-        );
-    }
-
-    fn grow(
-        &mut self,
-        amount: usize,
-        device: &wgpu::Device,
-        encoder: &mut wgpu::CommandEncoder,
-    ) {
-        if amount == 0 {
-            return;
-        }
-
-        let new_texture = device.create_texture(&wgpu::TextureDescriptor {
-            size: wgpu::Extent3d {
-                width: SIZE,
-                height: SIZE,
-                depth: 1,
-            },
-            array_layer_count: self.layers.len() as u32,
-            mip_level_count: 1,
-            sample_count: 1,
-            dimension: wgpu::TextureDimension::D2,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            usage: wgpu::TextureUsage::COPY_DST
-                | wgpu::TextureUsage::COPY_SRC
-                | wgpu::TextureUsage::SAMPLED,
-        });
-
-        let amount_to_copy = self.layers.len() - amount;
-
-        for (i, layer) in
-            self.layers.iter_mut().take(amount_to_copy).enumerate()
-        {
-            if layer.is_empty() {
-                continue;
-            }
-
-            encoder.copy_texture_to_texture(
-                wgpu::TextureCopyView {
-                    texture: &self.texture,
-                    array_layer: i as u32,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
-                },
-                wgpu::TextureCopyView {
-                    texture: &new_texture,
-                    array_layer: i as u32,
-                    mip_level: 0,
-                    origin: wgpu::Origin3d {
-                        x: 0.0,
-                        y: 0.0,
-                        z: 0.0,
-                    },
-                },
-                wgpu::Extent3d {
-                    width: SIZE,
-                    height: SIZE,
-                    depth: 1,
-                },
-            );
-        }
-
-        self.texture = new_texture;
-        self.texture_view = self.texture.create_default_view();
-    }
-}
diff --git a/wgpu/src/texture/atlas/allocation.rs b/wgpu/src/texture/atlas/allocation.rs
deleted file mode 100644
index e17b3f8c..00000000
--- a/wgpu/src/texture/atlas/allocation.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use crate::texture::atlas::{self, allocator};
-
-#[derive(Debug)]
-pub enum Allocation {
-    Partial {
-        layer: usize,
-        region: allocator::Region,
-    },
-    Full {
-        layer: usize,
-    },
-}
-
-impl Allocation {
-    pub fn position(&self) -> (u32, u32) {
-        match self {
-            Allocation::Partial { region, .. } => region.position(),
-            Allocation::Full { .. } => (0, 0),
-        }
-    }
-
-    pub fn size(&self) -> (u32, u32) {
-        match self {
-            Allocation::Partial { region, .. } => region.size(),
-            Allocation::Full { .. } => (atlas::SIZE, atlas::SIZE),
-        }
-    }
-
-    pub fn layer(&self) -> usize {
-        match self {
-            Allocation::Partial { layer, .. } => *layer,
-            Allocation::Full { layer } => *layer,
-        }
-    }
-}
diff --git a/wgpu/src/texture/atlas/allocator.rs b/wgpu/src/texture/atlas/allocator.rs
deleted file mode 100644
index 7a4ff5b1..00000000
--- a/wgpu/src/texture/atlas/allocator.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use guillotiere::{AtlasAllocator, Size};
-
-pub struct Allocator {
-    raw: AtlasAllocator,
-    allocations: usize,
-}
-
-impl Allocator {
-    pub fn new(size: u32) -> Allocator {
-        let raw = AtlasAllocator::new(Size::new(size as i32, size as i32));
-
-        Allocator {
-            raw,
-            allocations: 0,
-        }
-    }
-
-    pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
-        let allocation =
-            self.raw.allocate(Size::new(width as i32, height as i32))?;
-
-        self.allocations += 1;
-
-        Some(Region { allocation })
-    }
-
-    pub fn deallocate(&mut self, region: &Region) {
-        self.raw.deallocate(region.allocation.id);
-
-        self.allocations = self.allocations.saturating_sub(1);
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.allocations == 0
-    }
-}
-
-pub struct Region {
-    allocation: guillotiere::Allocation,
-}
-
-impl Region {
-    pub fn position(&self) -> (u32, u32) {
-        let rectangle = &self.allocation.rectangle;
-
-        (rectangle.min.x as u32, rectangle.min.y as u32)
-    }
-
-    pub fn size(&self) -> (u32, u32) {
-        let size = self.allocation.rectangle.size();
-
-        (size.width as u32, size.height as u32)
-    }
-}
-
-impl std::fmt::Debug for Allocator {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "Allocator")
-    }
-}
-
-impl std::fmt::Debug for Region {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Region")
-            .field("id", &self.allocation.id)
-            .field("rectangle", &self.allocation.rectangle)
-            .finish()
-    }
-}
diff --git a/wgpu/src/texture/atlas/entry.rs b/wgpu/src/texture/atlas/entry.rs
deleted file mode 100644
index 2c064665..00000000
--- a/wgpu/src/texture/atlas/entry.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use crate::texture::atlas;
-
-#[derive(Debug)]
-pub enum Entry {
-    Contiguous(atlas::Allocation),
-    Fragmented {
-        size: (u32, u32),
-        fragments: Vec<Fragment>,
-    },
-}
-
-impl Entry {
-    pub fn size(&self) -> (u32, u32) {
-        match self {
-            Entry::Contiguous(allocation) => allocation.size(),
-            Entry::Fragmented { size, .. } => *size,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub struct Fragment {
-    pub position: (u32, u32),
-    pub allocation: atlas::Allocation,
-}
diff --git a/wgpu/src/texture/atlas/layer.rs b/wgpu/src/texture/atlas/layer.rs
deleted file mode 100644
index b025d8a1..00000000
--- a/wgpu/src/texture/atlas/layer.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use crate::texture::atlas::Allocator;
-
-#[derive(Debug)]
-pub enum Layer {
-    Empty,
-    Busy(Allocator),
-    Full,
-}
-
-impl Layer {
-    pub fn is_empty(&self) -> bool {
-        match self {
-            Layer::Empty => true,
-            _ => false,
-        }
-    }
-}
-- 
cgit 


From 88d4cd097044077127e1b3aa8fcb04afed185491 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Fri, 28 Feb 2020 14:41:07 +0100
Subject: Remove unnecessary `pub(crate) use`

---
 wgpu/src/lib.rs      | 3 ---
 wgpu/src/renderer.rs | 2 +-
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 1d63abbf..4e0cbc60 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -55,6 +55,3 @@ pub(crate) use transformation::Transformation;
 
 #[cfg(any(feature = "image", feature = "svg"))]
 mod image;
-
-#[cfg(any(feature = "image", feature = "svg"))]
-pub(crate) use self::image::Image;
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index d9ef9fc4..1da19b1a 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -4,7 +4,7 @@ use crate::{
 };
 
 #[cfg(any(feature = "image", feature = "svg"))]
-use crate::{image, Image};
+use crate::image::{self, Image};
 
 use iced_native::{
     layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
-- 
cgit