Fix multiple issues from the refactoring

- Update texture view on grow
- Fix atlas texture coordinates
- Fix fragmented uploads
This commit is contained in:
Héctor Ramón Jiménez 2020-02-26 18:49:46 +01:00
parent c58d94f3fd
commit 48d70280eb
4 changed files with 86 additions and 42 deletions

View file

@ -319,6 +319,8 @@ impl Pipeline {
let texture_version = self.texture_atlas.layer_count(); let texture_version = self.texture_atlas.layer_count();
if self.texture_version != texture_version { if self.texture_version != texture_version {
log::info!("Atlas has grown. Recreating bind group...");
self.texture = self.texture =
device.create_bind_group(&wgpu::BindGroupDescriptor { device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.texture_layout, layout: &self.texture_layout,
@ -525,12 +527,12 @@ fn add_instance(
_position: position, _position: position,
_size: size, _size: size,
_position_in_atlas: [ _position_in_atlas: [
x as f32 / atlas::SIZE as f32, (x as f32 + 0.5) / atlas::SIZE as f32,
y as f32 / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32,
], ],
_size_in_atlas: [ _size_in_atlas: [
width as f32 / atlas::SIZE as f32, (width as f32 - 0.5) / atlas::SIZE as f32,
height as f32 / atlas::SIZE as f32, (height as f32 - 0.5) / atlas::SIZE as f32,
], ],
_layer: layer as u32, _layer: layer as u32,
}; };

View file

@ -83,10 +83,9 @@ impl Cache {
if let Memory::Host(image) = memory { if let Memory::Host(image) = memory {
let (width, height) = image.dimensions(); let (width, height) = image.dimensions();
let allocation = let entry = atlas.upload(width, height, &image, device, encoder)?;
atlas.upload(width, height, &image, device, encoder)?;
*memory = Memory::Device(allocation); *memory = Memory::Device(entry);
} }
if let Memory::Device(allocation) = memory { if let Memory::Device(allocation) = memory {

View file

@ -10,7 +10,7 @@ pub use layer::Layer;
use allocator::Allocator; use allocator::Allocator;
pub const SIZE: u32 = 4096; pub const SIZE: u32 = 2048;
#[derive(Debug)] #[derive(Debug)]
pub struct Atlas { pub struct Atlas {
@ -78,24 +78,33 @@ impl Atlas {
entry entry
}; };
log::info!("Allocated atlas entry: {:?}", entry);
let buffer = device let buffer = device
.create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC) .create_buffer_mapped(data.len(), wgpu::BufferUsage::COPY_SRC)
.fill_from_slice(data); .fill_from_slice(data);
match &entry { match &entry {
Entry::Contiguous(allocation) => { Entry::Contiguous(allocation) => {
self.upload_texture(&buffer, 0, &allocation, encoder); self.upload_allocation(
&buffer,
width,
height,
0,
&allocation,
encoder,
);
} }
Entry::Fragmented { fragments, .. } => { Entry::Fragmented { fragments, .. } => {
for fragment in fragments { for fragment in fragments {
let (x, y) = fragment.allocation.position(); let (x, y) = fragment.position;
let offset = (y * width + x) as usize * 4;
let offset = self.upload_allocation(
(y * height + x) as usize * std::mem::size_of::<C>();
self.upload_texture(
&buffer, &buffer,
offset as u64, width,
height,
offset,
&fragment.allocation, &fragment.allocation,
encoder, encoder,
); );
@ -103,6 +112,8 @@ impl Atlas {
} }
} }
log::info!("Current atlas: {:?}", &self);
Some(entry) Some(entry)
} }
@ -204,10 +215,12 @@ impl Atlas {
None None
} }
fn upload_texture( fn upload_allocation(
&mut self, &mut self,
buffer: &wgpu::Buffer, buffer: &wgpu::Buffer,
offset: u64, image_width: u32,
image_height: u32,
offset: usize,
allocation: &Allocation, allocation: &Allocation,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
) { ) {
@ -224,9 +237,9 @@ impl Atlas {
encoder.copy_buffer_to_texture( encoder.copy_buffer_to_texture(
wgpu::BufferCopyView { wgpu::BufferCopyView {
buffer, buffer,
offset, offset: offset as u64,
row_pitch: 4 * width, row_pitch: 4 * image_width,
image_height: height, image_height,
}, },
wgpu::TextureCopyView { wgpu::TextureCopyView {
texture: &self.texture, texture: &self.texture,
@ -258,7 +271,7 @@ impl Atlas {
height: SIZE, height: SIZE,
depth: 1, depth: 1,
}, },
array_layer_count: (self.layers.len() + amount) as u32, array_layer_count: self.layers.len() as u32,
mip_level_count: 1, mip_level_count: 1,
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
@ -268,7 +281,11 @@ impl Atlas {
| wgpu::TextureUsage::SAMPLED, | 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() { if layer.is_empty() {
continue; continue;
} }
@ -302,10 +319,7 @@ impl Atlas {
); );
} }
for _ in 0..amount {
self.layers.push(Layer::Empty);
}
self.texture = new_texture; self.texture = new_texture;
self.texture_view = self.texture.create_default_view();
} }
} }

View file

@ -2,41 +2,70 @@ use guillotiere::{AtlasAllocator, Size};
pub struct Allocator { pub struct Allocator {
raw: AtlasAllocator, raw: AtlasAllocator,
size: u32,
} }
impl Allocator { impl Allocator {
const PADDING: u32 = 1;
pub fn new(size: u32) -> Allocator { pub fn new(size: u32) -> Allocator {
let raw = AtlasAllocator::new(Size::new(size as i32, size as i32)); 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> { pub fn allocate(&mut self, width: u32, height: u32) -> Option<Region> {
let allocation = self let padding = (
.raw if width + Self::PADDING * 2 < self.size {
.allocate(Size::new(width as i32 + 2, height as i32 + 2))?; Self::PADDING
} else {
0
},
if height + Self::PADDING * 2 < self.size {
Self::PADDING
} else {
0
},
);
Some(Region(allocation)) let allocation = self.raw.allocate(Size::new(
(width + padding.0 * 2) as i32,
(height + padding.1 * 2) as i32,
))?;
Some(Region {
allocation,
padding,
})
} }
pub fn deallocate(&mut self, region: Region) { 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 { impl Region {
pub fn position(&self) -> (u32, u32) { 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) { 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 { impl std::fmt::Debug for Region {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( f.debug_struct("Region")
f, .field("id", &self.allocation.id)
"Region {{ id: {:?}, rectangle: {:?} }}", .field("rectangle", &self.allocation.rectangle)
self.0.id, self.0.rectangle .field("padding", &self.padding)
) .finish()
} }
} }