Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"82453e4eae38222ff1b6bf3d77f6c2762696ca8e7a53a201d91606be33742962","LICENSE":"7e98bed7f100747aaae7d82ab94fbe6b76fd6b84b09a3aaa413bff8196d0fbb1","README.md":"c0a3e362882f80a5dea3cbbe3af38c8456a848862369309cfd52ca45596bb970","src/allocator.rs":"4e43bf9a57f9ada5c22eb8f1a933854a5a31dc853097df2b17b3477e64de34f3","src/lib.rs":"f3d209600643006e6d226a1c407f34d7fbcc10aa7bdccf149bda78a3837134df","src/recording.rs":"2c1625d9e1333d297fc43f8cbd94032dbfd7b54f25e319b0ee480a0115f74db5"},"package":"b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"}

36
vendor/guillotiere/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,36 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "guillotiere"
version = "0.6.2"
authors = ["Nicolas Silva <nical@fastmail.com>"]
description = "A dynamic 2D texture atlas allocator with fast deallocation."
documentation = "https://docs.rs/guillotiere/"
keywords = ["2d", "graphics"]
license = "MIT/Apache-2.0"
repository = "https://github.com/nical/guillotiere"
[dependencies.euclid]
version = "0.22.0"
[dependencies.serde]
version = "1.0"
features = ["serde_derive"]
optional = true
[dependencies.svg_fmt]
version = "0.4.1"
[features]
checks = []
serialization = ["serde", "euclid/serde"]

21
vendor/guillotiere/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Nicolas Silva
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

56
vendor/guillotiere/README.md vendored Normal file
View File

@@ -0,0 +1,56 @@
# Guillotière
<p align="center">
<a href="https://crates.io/crates/guillotiere">
<img src="http://meritbadge.herokuapp.com/guillotiere" alt="crates.io">
</a>
<a href="https://travis-ci.org/nical/guillotiere">
<img src="https://img.shields.io/travis/nical/guillotiere/master.svg" alt="Travis Build Status">
</a>
<a href="https://docs.rs/guillotiere">
<img src="https://docs.rs/guillotiere/badge.svg" alt="documentation">
</a>
</p>
A dynamic texture atlas allocator with fast deallocation and rectangle coalescing.
## Motivation
The ability to dynamically batch textures together is important for some graphics rendering scenarios (for example [WebRender](https://github.com/servo/webrender)).
A challenging aspect of dynamic atlas allocation is the need to coalesce free rectangles after deallocation to defragment the available space.
Some atlas allocators perform this task by examining all possible pairs of free rectangles and test if they can be merged, which is prohibitively expensive for real-time applications.
Guillotière solves this problem by internally maintaining a data structure that allows constant time access to neighbor rectangles and greatly speeds up the coalesing operation.
The details of how this works are explained in the [`AtlasAllocator` documentation](https://docs.rs/guillotiere/*/guillotiere/struct.AtlasAllocator.html).
## Example
```rust
use guillotiere::*;
let mut atlas = AtlasAllocator::new(size2(1000, 1000));
let a = atlas.allocate(size2(100, 1000)).unwrap();
let b = atlas.allocate(size2(900, 200)).unwrap();
atlas.deallocate(a.id);
let c = atlas.allocate(size2(300, 200)).unwrap();
assert_eq!(c.rectangle, atlas[c.id]);
atlas.deallocate(c.id);
atlas.deallocate(b.id);
```
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

1754
vendor/guillotiere/src/allocator.rs vendored Normal file

File diff suppressed because it is too large Load Diff

14
vendor/guillotiere/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,14 @@
#[cfg(feature = "serialization")]
#[macro_use]
pub extern crate serde;
pub extern crate euclid;
mod allocator;
//pub mod recording;
pub use crate::allocator::*;
pub use euclid::{point2, size2};
pub type Point = euclid::default::Point2D<i32>;
pub type Size = euclid::default::Size2D<i32>;
pub type Rectangle = euclid::default::Box2D<i32>;

510
vendor/guillotiere/src/recording.rs vendored Normal file
View File

@@ -0,0 +1,510 @@
use crate::*;
use std::collections::HashMap;
pub struct RecordingAllocator {
allocator: AtlasAllocator,
recorder: Recorder,
// Assign unique ids to recorded events. This simplifies a few things, later on.
id_map: HashMap<AllocId, AllocId>,
next_id: u32,
}
impl RecordingAllocator {
/// Create an atlas allocator.
pub fn new(size: Size) -> Self {
RecordingAllocator {
allocator: AtlasAllocator::new(size),
recorder: Recorder {
events: Vec::new(),
initial_size: size,
options: DEFAULT_OPTIONS,
},
id_map: HashMap::new(),
next_id: 0,
}
}
/// Create an atlas allocator with the provided options.
pub fn with_options(size: Size, options: &AllocatorOptions) -> Self {
RecordingAllocator {
allocator: AtlasAllocator::with_options(size, options),
recorder: Recorder {
events: Vec::new(),
initial_size: size,
options: *options,
},
id_map: HashMap::new(),
next_id: 0,
}
}
/// The total size of the atlas.
pub fn size(&self) -> Size {
self.allocator.size()
}
/// Allocate a rectangle in the atlas.
pub fn allocate(&mut self, requested_size: Size) -> Option<Allocation> {
let res = self.allocator.allocate(requested_size).map(|res| {
let id = AllocId(self.next_id);
self.next_id += 1;
self.id_map.insert(id, res.id);
println!(" alloc {:?} (was {:?})", id, res.id);
Allocation { id, ..res }
});
self.recorder.record(Event::Allocate(requested_size, res.map(|r| r.id)));
res
}
/// Deallocate a rectangle in the atlas.
pub fn deallocate(&mut self, node_id: AllocId) {
if let Some(actual_id) = self.id_map.get(&node_id) {
println!(" dealloc {:?} (was {:?})", node_id, actual_id);
self.allocator.deallocate(*actual_id);
self.recorder.record(Event::Deallocate(node_id));
}
}
/// Recompute the allocations in the atlas and returns a list of the changes.
///
/// Previous ids and rectangles are not valid anymore after this operation as each id/rectangle
/// pair is assigned to new values which are communicated in the returned change list.
/// Rearranging the atlas can help reduce fragmentation.
pub fn rearrange(&mut self) -> ChangeList {
let changes = self.allocator.rearrange();
let remapped = self.remap_changelist(&changes);
self.recorder.record(Event::Rearrange(remapped.clone()));
remapped
}
/// Identical to `AtlasAllocator::rearrange`, also allowing to change the size of the atlas.
pub fn resize_and_rearrange(&mut self, new_size: Size) -> ChangeList {
let changes = self.allocator.resize_and_rearrange(new_size);
let remapped = self.remap_changelist(&changes);
self.recorder.record(Event::ResizeAndRearrange(new_size, remapped.clone()));
remapped
}
fn remap_changelist(&mut self, changes: &ChangeList) -> ChangeList {
let mut remapped = ChangeList {
changes: Vec::new(),
failures: Vec::new(),
};
let prev_id_map = std::mem::replace(&mut self.id_map, HashMap::new());
self.id_map.clear();
for change in &changes.changes {
let mut id = None;
for (k, v) in &prev_id_map {
if *v == change.old.id {
id = Some(*k);
break;
}
}
let id = id.unwrap();
self.id_map.insert(id, change.new.id);
remapped.changes.push(Change {
old: Allocation { id, ..change.old },
new: Allocation { id, ..change.new },
});
}
for failure in &changes.failures {
let mut id = None;
for (k, v) in &prev_id_map {
if *v == failure.id {
id = Some(*k);
break;
}
}
remapped.failures.push(Allocation {
id : id.unwrap(),
rectangle: failure.rectangle,
});
}
remapped
}
pub fn grow(&mut self, new_size: Size) {
self.recorder.record(Event::Grow(new_size));
self.allocator.grow(new_size);
}
pub fn for_each_free_rectangle<F>(&self, callback: F)
where
F: FnMut(&Rectangle),
{
self.allocator.for_each_free_rectangle(callback);
}
pub fn for_each_allocated_rectangle<F>(&self, callback: F)
where
F: FnMut(AllocId, &Rectangle),
{
self.allocator.for_each_allocated_rectangle(callback);
}
}
#[derive(Clone, Debug)]
pub enum Event {
Allocate(Size, Option<AllocId>),
Deallocate(AllocId),
Grow(Size),
Rearrange(ChangeList),
ResizeAndRearrange(Size, ChangeList),
}
pub struct Recorder {
events: Vec<Event>,
initial_size: Size,
options: AllocatorOptions,
}
impl Recorder {
pub fn record(&mut self, event: Event) {
self.events.push(event);
}
pub fn finish(&mut self) -> Recording {
Recording {
events: std::mem::replace(&mut self.events, Vec::new()),
options: self.options,
initial_size: self.initial_size,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplayStats {
allocations: u32,
deallocations: u32,
failed_allocations: u32,
}
#[derive(Clone)]
pub struct Recording {
initial_size: Size,
events: Vec<Event>,
options: AllocatorOptions,
}
impl Recording {
pub fn replay(&self) -> std::thread::Result<ReplayStats> {
//println!("--------------- replay");
std::panic::catch_unwind(|| {
let mut stats = ReplayStats {
allocations: 0,
deallocations: 0,
failed_allocations: 0,
};
let mut allocator = AtlasAllocator::with_options(self.initial_size, &self.options);
let mut id_remap: HashMap<AllocId, Option<AllocId>> = HashMap::default();
for evt in &self.events {
match *evt {
Event::Allocate(size, recorded_id) => {
let alloc = allocator.allocate(size);
match alloc {
Some(_) => {
stats.allocations += 1;
}
None => {
stats.failed_allocations += 1;
}
}
if let Some(recorded_id) = recorded_id {
id_remap.insert(recorded_id, alloc.map(|alloc| alloc.id));
}
//println!("+ alloc {:?} ({:?})", recorded_id, alloc.map(|alloc| alloc.id));
}
Event::Deallocate(recorded_id) => {
if let Some(Some(id)) = id_remap.remove(&recorded_id) {
//println!("- dealloc {:?} ({:?})", recorded_id, id);
allocator.deallocate(id);
stats.deallocations += 1;
}
}
Event::Grow(size) => {
allocator.grow(size);
}
Event::Rearrange(ref recorded_changes) => {
//println!(" *** rearrange");
let changes = allocator.rearrange();
Recording::apply_changelists(&mut id_remap, &recorded_changes, &changes);
}
Event::ResizeAndRearrange(new_size, ref recorded_changes) => {
let changes = allocator.resize_and_rearrange(new_size);
Recording::apply_changelists(&mut id_remap, recorded_changes, &changes);
}
}
}
stats
})
}
fn apply_changelists(
id_remap: &mut HashMap<AllocId, Option<AllocId>>,
recorded: &ChangeList,
new: &ChangeList,
) {
for change in &recorded.changes {
if let Some(id) = id_remap.remove(&change.old.id) {
//println!(" * {:?} -> {:?}", change.old.id, change.new.id);
id_remap.insert(change.new.id, id);
}
}
for remapped in id_remap.values_mut() {
for change in &new.changes {
if *remapped == Some(change.old.id) {
//println!(" ** {:?} -> {:?}", remapped.unwrap(), change.new.id);
*remapped = Some(change.new.id);
break;
}
}
for failure in &new.failures {
if *remapped == Some(failure.id) {
//println!(" ** failed {:?}", remapped.unwrap());
*remapped = None;
break;
}
}
}
}
pub fn remove_event(&mut self, index: usize) {
//println!("remove {:?}", self.events[index]);
self.events.remove(index);
}
pub fn find_reduced_testcase(&self) -> Recording {
let mut recording = self.clone();
let mut i = 0;
loop {
if i >= recording.events.len() {
recording.remap_ids();
return recording;
}
let mut reduced = recording.clone();
reduced.events.remove(i);
if !reduced.replay().is_ok() {
recording = reduced;
} else {
i += 1;
}
}
}
pub fn write_testcase(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
writeln!(output, "#[test]")?;
writeln!(output, "fn reduced_testcase() {{")?;
writeln!(output, " let options = AllocatorOptions {{")?;
writeln!(output, " snap_size: {},", self.options.snap_size)?;
writeln!(
output,
" small_size_threshold: {},",
self.options.small_size_threshold
)?;
writeln!(
output,
" large_size_threshold: {},",
self.options.large_size_threshold
)?;
writeln!(output, " }};")?;
writeln!(
output,
" let size = size2({}, {});",
self.initial_size.width, self.initial_size.height
)?;
writeln!(
output,
" let mut allocator = AtlasAllocator::with_options(size, options);"
)?;
let mut next_identifier = self.events.len() as u32;
for event in &self.events {
match *event {
Event::Allocate(size, res) => {
let identifier = res.map(|id| id.to_u32()).unwrap_or_else(|| {
next_identifier += 1;
next_identifier
});
writeln!(
output,
" let r{} = allocator.allocate(size2({}, {}));",
identifier, size.width, size.height,
)?;
}
Event::Deallocate(id) => {
writeln!(
output,
" allocator.deallocate(r{}.unwrap().id);",
id.to_u32()
)?;
}
Event::Grow(size) => {
writeln!(
output,
" allocator.grow(size2({}, {}));",
size.width, size.height
)?;
}
Event::Rearrange(_) => {
writeln!(output, " allocator.rearrange();")?;
}
Event::ResizeAndRearrange(size, _) => {
writeln!(
output,
" allocator.resize_and_rearrange(size2({}, {}));",
size.width, size.height
)?;
}
}
}
writeln!(output, "}}")?;
Ok(())
}
fn remap_ids(&mut self) {
let mut allocator = AtlasAllocator::with_options(self.initial_size, &self.options);
let mut id_remap: HashMap<AllocId, Option<AllocId>> = HashMap::default();
let mut idx = 0;
while idx < self.events.len() {
match self.events[idx] {
Event::Allocate(size, ref mut recorded_id) => {
let key = *recorded_id;
let alloc = allocator.allocate(size);
let actual_id = alloc.map(|alloc| alloc.id);
*recorded_id = actual_id;
if let Some(key) = key {
id_remap.insert(key, actual_id);
}
}
Event::Deallocate(ref mut recorded_id) => match id_remap.remove(recorded_id) {
Some(Some(id)) => {
allocator.deallocate(id);
*recorded_id = id;
}
_ => {
self.events.remove(idx);
continue;
}
},
Event::Grow(size) => {
allocator.grow(size);
}
Event::Rearrange(ref mut recorded_changes) => {
let changes = allocator.rearrange();
Recording::apply_changelists(&mut id_remap, recorded_changes, &changes);
*recorded_changes = changes;
}
Event::ResizeAndRearrange(new_size, ref mut recorded_changes) => {
let changes = allocator.resize_and_rearrange(new_size);
Recording::apply_changelists(&mut id_remap, recorded_changes, &changes);
*recorded_changes = changes;
}
}
idx += 1
}
}
}
#[test]
fn recording_random_test() {
let mut atlas = RecordingAllocator::with_options(
size2(1000, 1000),
&AllocatorOptions {
snap_size: 5,
..DEFAULT_OPTIONS
},
);
let a = 1103515245;
let c = 12345;
let m = usize::pow(2, 31);
let mut seed: usize = 37;
let mut rand = || {
seed = (a * seed + c) % m;
seed
};
let mut allocated = Vec::new();
for i in 0..50000 {
if i % 10000 == 10 {
let cl = atlas.rearrange();
assert!(cl.failures.is_empty());
for changes in &cl.changes {
for id in &mut allocated {
if *id == changes.old.id {
*id = changes.new.id;
break;
}
}
}
}
if rand() % 5 > 2 && !allocated.is_empty() {
let nth = rand() % allocated.len();
let id = allocated[nth];
allocated.remove(nth);
atlas.deallocate(id);
} else {
let size = size2((rand() % 300) as i32 + 5, (rand() % 300) as i32 + 5);
if let Some(alloc) = atlas.allocate(size) {
allocated.push(alloc.id);
}
}
}
while let Some(id) = allocated.pop() {
atlas.deallocate(id);
}
let mut recording = atlas.recorder.finish();
recording.replay().unwrap();
recording.remap_ids();
recording.replay().unwrap();
for i in 0..100 {
recording.remove_event(i * 27);
}
recording.replay().unwrap();
recording.remap_ids();
recording.replay().unwrap();
}