Skip to content

Commit

Permalink
Merge pull request #1 from alteous/fix-large-buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaddyDC authored Dec 13, 2023
2 parents 71467af + 53cf364 commit 90e45af
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 62 deletions.
24 changes: 14 additions & 10 deletions examples/export/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use gltf_json as json;
use std::{fs, mem};

use json::validation::Checked::Valid;
use json::validation::USize64;
use std::borrow::Cow;
use std::io::Write;

Expand Down Expand Up @@ -37,7 +38,7 @@ fn bounding_coords(points: &[Vertex]) -> ([f32; 3], [f32; 3]) {
(min, max)
}

fn align_to_multiple_of_four(n: &mut u32) {
fn align_to_multiple_of_four(n: &mut usize) {
*n = (*n + 3) & !3;
}

Expand Down Expand Up @@ -71,9 +72,9 @@ fn export(output: Output) {

let (min, max) = bounding_coords(&triangle_vertices);

let buffer_length = (triangle_vertices.len() * mem::size_of::<Vertex>()) as u64;
let buffer_length = triangle_vertices.len() * mem::size_of::<Vertex>();
let buffer = json::Buffer {
byte_length: buffer_length,
byte_length: USize64::from(buffer_length),
extensions: Default::default(),
extras: Default::default(),
name: None,
Expand All @@ -87,16 +88,16 @@ fn export(output: Output) {
buffer: json::Index::new(0),
byte_length: buffer.byte_length,
byte_offset: None,
byte_stride: Some(mem::size_of::<Vertex>() as u64),
byte_stride: Some(json::buffer::Stride(mem::size_of::<Vertex>())),
extensions: Default::default(),
extras: Default::default(),
name: None,
target: Some(Valid(json::buffer::Target::ArrayBuffer)),
};
let positions = json::Accessor {
buffer_view: Some(json::Index::new(0)),
byte_offset: Some(0),
count: triangle_vertices.len() as u32,
byte_offset: Some(USize64(0)),
count: USize64::from(triangle_vertices.len()),
component_type: Valid(json::accessor::GenericComponentType(
json::accessor::ComponentType::F32,
)),
Expand All @@ -111,8 +112,8 @@ fn export(output: Output) {
};
let colors = json::Accessor {
buffer_view: Some(json::Index::new(0)),
byte_offset: Some((3 * mem::size_of::<f32>()) as u32),
count: triangle_vertices.len() as u32,
byte_offset: Some(USize64::from(3 * mem::size_of::<f32>())),
count: USize64::from(triangle_vertices.len()),
component_type: Valid(json::accessor::GenericComponentType(
json::accessor::ComponentType::F32,
)),
Expand Down Expand Up @@ -192,13 +193,16 @@ fn export(output: Output) {
}
Output::Binary => {
let json_string = json::serialize::to_string(&root).expect("Serialization error");
let mut json_offset = json_string.len() as u32;
let mut json_offset = json_string.len();
align_to_multiple_of_four(&mut json_offset);
let glb = gltf::binary::Glb {
header: gltf::binary::Header {
magic: *b"glTF",
version: 2,
length: json_offset + buffer_length as u32, // This may truncate long buffers
// N.B., the size of binary glTF file is limited to range of `u32`.
length: (json_offset + buffer_length)
.try_into()
.expect("file size exceeds binary glTF limit"),
},
bin: Some(Cow::Owned(to_padded_byte_vector(triangle_vertices))),
json: Cow::Owned(json_string.into_bytes()),
Expand Down
12 changes: 6 additions & 6 deletions gltf-json/src/accessor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::validation::{Checked, Error, Validate};
use crate::validation::{Checked, Error, USize64, Validate};
use crate::{buffer, extensions, Extras, Index, Path, Root};
use gltf_derive::Validate;
use serde::{de, ser};
Expand Down Expand Up @@ -94,7 +94,7 @@ pub mod sparse {

/// The offset relative to the start of the parent `BufferView` in bytes.
#[serde(default, rename = "byteOffset")]
pub byte_offset: u32,
pub byte_offset: USize64,

/// The data type of each index.
#[serde(rename = "componentType")]
Expand All @@ -115,7 +115,7 @@ pub mod sparse {
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Sparse {
/// The number of attributes encoded in this sparse accessor.
pub count: u32,
pub count: USize64,

/// Index array of size `count` that points to those accessor attributes
/// that deviate from their initialization value.
Expand Down Expand Up @@ -154,7 +154,7 @@ pub mod sparse {

/// The offset relative to the start of the parent buffer view in bytes.
#[serde(default, rename = "byteOffset")]
pub byte_offset: u32,
pub byte_offset: USize64,

/// Extension specific data.
#[serde(default, skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -183,11 +183,11 @@ pub struct Accessor {
/// This field can be omitted in sparse accessors.
#[serde(default, rename = "byteOffset")]
#[serde(skip_serializing_if = "Option::is_none")]
pub byte_offset: Option<u32>,
pub byte_offset: Option<USize64>,

/// The number of components within the buffer view - not to be confused
/// with the number of bytes in the buffer view.
pub count: u32,
pub count: USize64,

/// The data type of components in the attribute.
#[serde(rename = "componentType")]
Expand Down
32 changes: 24 additions & 8 deletions gltf-json/src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::validation::Checked;
use crate::{extensions, Extras, Index};
use crate::validation::{Checked, Error, USize64, Validate};
use crate::{extensions, Extras, Index, Path, Root};
use gltf_derive::Validate;
use serde::{de, ser};
use serde_derive::{Deserialize, Serialize};
Expand All @@ -12,10 +12,10 @@ pub const ARRAY_BUFFER: u32 = 34_962;
pub const ELEMENT_ARRAY_BUFFER: u32 = 34_963;

/// The minimum byte stride.
pub const MIN_BYTE_STRIDE: u32 = 4;
pub const MIN_BYTE_STRIDE: usize = 4;

/// The maximum byte stride.
pub const MAX_BYTE_STRIDE: u32 = 252;
pub const MAX_BYTE_STRIDE: usize = 252;

/// All valid GPU buffer targets.
pub const VALID_TARGETS: &[u32] = &[ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER];
Expand All @@ -42,12 +42,28 @@ impl ser::Serialize for Target {
}
}

/// Distance between individual items in a buffer view, measured in bytes.
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Stride(pub usize);

impl Validate for Stride {
fn validate<P, R>(&self, _root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if self.0 < MIN_BYTE_STRIDE || self.0 > MAX_BYTE_STRIDE {
report(&path, Error::Invalid);
}
}
}

/// A buffer points to binary data representing geometry, animations, or skins.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Buffer {
/// The length of the buffer in bytes.
#[serde(default, rename = "byteLength")]
pub byte_length: u64,
pub byte_length: USize64,

/// Optional user-defined name for this object.
#[cfg(feature = "names")]
Expand Down Expand Up @@ -81,22 +97,22 @@ pub struct View {

/// The length of the `BufferView` in bytes.
#[serde(rename = "byteLength")]
pub byte_length: u64,
pub byte_length: USize64,

/// Offset into the parent buffer in bytes.
#[serde(
default,
rename = "byteOffset",
skip_serializing_if = "Option::is_none"
)]
pub byte_offset: Option<u64>,
pub byte_offset: Option<USize64>,

/// The stride in bytes between vertex attributes or other interleavable data.
///
/// When zero, data is assumed to be tightly packed.
#[serde(rename = "byteStride")]
#[serde(skip_serializing_if = "Option::is_none")]
pub byte_stride: Option<u64>,
pub byte_stride: Option<Stride>,

/// Optional user-defined name for this object.
#[cfg(feature = "names")]
Expand Down
43 changes: 42 additions & 1 deletion gltf-json/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub enum Error {

/// Some required data has been omitted.
Missing,

/// A memory size or offset exceeds the system limits.
Oversize,
}

/// Specifies a type that has been pre-validated during deserialization or otherwise.
Expand Down Expand Up @@ -103,6 +106,44 @@ impl<T> Validate for Checked<T> {
}
}

/// Validates the suitability of 64-bit byte offsets/sizes on 32-bit systems.
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
PartialEq,
serde_derive::Deserialize,
serde_derive::Serialize,
)]
pub struct USize64(pub u64);

impl From<u64> for USize64 {
fn from(value: u64) -> Self {
Self(value)
}
}

impl From<usize> for USize64 {
fn from(value: usize) -> Self {
Self(value as u64)
}
}

impl Validate for USize64 {
fn validate<P, R>(&self, _root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if usize::try_from(self.0).is_err() {
report(&path, Error::Oversize);
}
}
}

impl<K: ToString + Validate, V: Validate> Validate for BTreeMap<K, V> {
fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
where
Expand Down Expand Up @@ -174,6 +215,7 @@ impl std::fmt::Display for Error {
Error::IndexOutOfBounds => "Index out of bounds",
Error::Invalid => "Invalid value",
Error::Missing => "Missing data",
Error::Oversize => "Size exceeds system limits",
}
)
}
Expand All @@ -182,7 +224,6 @@ impl std::fmt::Display for Error {
// These types are assumed to be always valid.
impl Validate for bool {}
impl Validate for u32 {}
impl Validate for u64 {}
impl Validate for i32 {}
impl Validate for f32 {}
impl Validate for [f32; 3] {}
Expand Down
4 changes: 2 additions & 2 deletions src/accessor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ impl<'a> Accessor<'a> {
pub fn offset(&self) -> usize {
// TODO: Change this function to return Option<usize> in the next
// version and return None for sparse accessors.
self.json.byte_offset.unwrap_or(0) as usize
self.json.byte_offset.unwrap_or_default().0 as usize
}

/// Returns the number of components within the buffer view - not to be confused
/// with the number of bytes in the buffer view.
pub fn count(&self) -> usize {
self.json.count as usize
self.json.count.0 as usize
}

/// Returns the data type of components in the attribute.
Expand Down
12 changes: 6 additions & 6 deletions src/accessor/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ impl<'a> Indices<'a> {
}

/// The offset relative to the start of the parent buffer view in bytes.
pub fn offset(&self) -> u32 {
self.json.byte_offset
pub fn offset(&self) -> usize {
self.json.byte_offset.0 as usize
}

/// The data type of each index.
Expand Down Expand Up @@ -73,8 +73,8 @@ impl<'a> Sparse<'a> {
}

/// Returns the number of attributes encoded in this sparse accessor.
pub fn count(&self) -> u32 {
self.json.count
pub fn count(&self) -> usize {
self.json.count.0 as usize
}

/// Returns an index array of size `count` that points to those accessor
Expand Down Expand Up @@ -120,8 +120,8 @@ impl<'a> Values<'a> {
}

/// The offset relative to the start of the parent buffer view in bytes.
pub fn offset(&self) -> u32 {
self.json.byte_offset
pub fn offset(&self) -> usize {
self.json.byte_offset.0 as usize
}

/// Optional application specific data.
Expand Down
10 changes: 5 additions & 5 deletions src/accessor/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ fn buffer_view_slice<'a, 's>(
view: buffer::View<'a>,
get_buffer_data: &dyn Fn(buffer::Buffer<'a>) -> Option<&'s [u8]>,
) -> Option<&'s [u8]> {
let start = usize::try_from(view.offset()).ok()?;
let end = usize::try_from(start as u64 + view.length()).ok()?;
let start = view.offset();
let end = start + view.length();
get_buffer_data(view.buffer()).and_then(|slice| slice.get(start..end))
}

Expand Down Expand Up @@ -303,14 +303,14 @@ impl<'a, 's, T: Item> Iter<'s, T> {

let indices = sparse.indices();
let values = sparse.values();
let sparse_count = sparse.count() as usize;
let sparse_count = sparse.count();

let index_iter = {
let view = indices.view();
let index_size = indices.index_type().size();
let stride = view.stride().unwrap_or(index_size);

let start = indices.offset() as usize;
let start = indices.offset();
let end = start + stride * (sparse_count - 1) + index_size;
let subslice = buffer_view_slice(view, &get_buffer_data)
.and_then(|slice| slice.get(start..end))?;
Expand All @@ -332,7 +332,7 @@ impl<'a, 's, T: Item> Iter<'s, T> {
let view = values.view();
let stride = view.stride().unwrap_or(mem::size_of::<T>());

let start = values.offset() as usize;
let start = values.offset();
let end = start + stride * (sparse_count - 1) + mem::size_of::<T>();
let subslice = buffer_view_slice(view, &get_buffer_data)
.and_then(|slice| slice.get(start..end))?;
Expand Down
Loading

0 comments on commit 90e45af

Please sign in to comment.