typedarrayview.

This commit is contained in:
Syoyo Fujita
2025-08-31 04:04:27 +09:00
parent 770040a14a
commit 731e458ae1
8 changed files with 1933 additions and 15 deletions

View File

@@ -435,6 +435,7 @@ set(TINYUSDZ_SOURCES
${PROJECT_SOURCE_DIR}/src/uuid-gen.hh
${PROJECT_SOURCE_DIR}/src/parser-timing.cc
${PROJECT_SOURCE_DIR}/src/sha256.cc
${PROJECT_SOURCE_DIR}/src/typed-array.cc
)

249
src/nonstd/span.hpp Normal file
View File

@@ -0,0 +1,249 @@
//
// Simple span implementation for C++14 compatibility
//
// Based on C++20 std::span but simplified for TinyUSDZ needs
//
#ifndef NONSTD_SPAN_HPP
#define NONSTD_SPAN_HPP
#include <cstddef>
#include <iterator>
#include <type_traits>
#ifndef NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
# define NONSTD_SPAN_CONFIG_NO_EXCEPTIONS 0
# else
# define NONSTD_SPAN_CONFIG_NO_EXCEPTIONS 1
# endif
#endif
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
# include <stdexcept>
#endif
namespace nonstd {
constexpr const std::ptrdiff_t dynamic_extent = -1;
template<typename T, std::ptrdiff_t Extent = dynamic_extent>
class span {
public:
// Member types
using element_type = T;
using value_type = typename std::remove_cv<T>::type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::ptrdiff_t extent = Extent;
// Constructors
constexpr span() noexcept
: data_(nullptr), size_(0) {
static_assert(Extent <= 0, "Cannot default construct a fixed-size span with positive extent");
}
constexpr span(pointer ptr, size_type count)
: data_(ptr), size_(count) {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (Extent != dynamic_extent && count != static_cast<size_type>(Extent)) {
throw std::logic_error("span size mismatch");
}
#endif
}
constexpr span(pointer first, pointer last)
: data_(first), size_(static_cast<size_type>(last - first)) {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (Extent != dynamic_extent && size_ != static_cast<size_type>(Extent)) {
throw std::logic_error("span size mismatch");
}
#endif
}
template<std::size_t N>
constexpr span(element_type (&arr)[N]) noexcept
: data_(arr), size_(N) {
static_assert(Extent == dynamic_extent || N == static_cast<std::size_t>(Extent),
"Array size must match span extent");
}
template<typename Container,
typename = typename std::enable_if<
!std::is_array<Container>::value &&
!std::is_same<Container, span>::value &&
std::is_convertible<decltype(std::declval<Container>().data()), pointer>::value
>::type>
constexpr span(Container& cont)
: data_(cont.data()), size_(cont.size()) {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (Extent != dynamic_extent && cont.size() != static_cast<size_type>(Extent)) {
throw std::logic_error("container size mismatch");
}
#endif
}
template<typename Container,
typename = typename std::enable_if<
!std::is_array<Container>::value &&
!std::is_same<Container, span>::value &&
std::is_convertible<decltype(std::declval<const Container>().data()), pointer>::value
>::type>
constexpr span(const Container& cont)
: data_(cont.data()), size_(cont.size()) {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (Extent != dynamic_extent && cont.size() != static_cast<size_type>(Extent)) {
throw std::logic_error("container size mismatch");
}
#endif
}
// Copy constructor
constexpr span(const span& other) noexcept = default;
// Assignment
constexpr span& operator=(const span& other) noexcept = default;
// Element access
constexpr reference operator[](size_type idx) const {
return data_[idx];
}
constexpr reference front() const {
return data_[0];
}
constexpr reference back() const {
return data_[size_ - 1];
}
constexpr pointer data() const noexcept {
return data_;
}
// Iterators
constexpr iterator begin() const noexcept {
return data_;
}
constexpr iterator end() const noexcept {
return data_ + size_;
}
constexpr const_iterator cbegin() const noexcept {
return data_;
}
constexpr const_iterator cend() const noexcept {
return data_ + size_;
}
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
constexpr reverse_iterator rend() const noexcept {
return reverse_iterator(begin());
}
constexpr const_reverse_iterator crbegin() const noexcept {
return const_reverse_iterator(cend());
}
constexpr const_reverse_iterator crend() const noexcept {
return const_reverse_iterator(cbegin());
}
// Observers
constexpr size_type size() const noexcept {
return size_;
}
constexpr size_type size_bytes() const noexcept {
return size_ * sizeof(element_type);
}
constexpr bool empty() const noexcept {
return size_ == 0;
}
// Subviews
constexpr span<element_type, dynamic_extent> first(size_type count) const {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (count > size_) {
throw std::out_of_range("span::first: count out of range");
}
#endif
return span<element_type, dynamic_extent>(data_, count);
}
constexpr span<element_type, dynamic_extent> last(size_type count) const {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (count > size_) {
throw std::out_of_range("span::last: count out of range");
}
#endif
return span<element_type, dynamic_extent>(data_ + (size_ - count), count);
}
constexpr span<element_type, dynamic_extent> subspan(size_type offset,
size_type count = static_cast<size_type>(dynamic_extent)) const {
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (offset > size_) {
throw std::out_of_range("span::subspan: offset out of range");
}
#endif
size_type actual_count = (count == static_cast<size_type>(dynamic_extent)) ? (size_ - offset) : count;
#if ! NONSTD_SPAN_CONFIG_NO_EXCEPTIONS
if (offset + actual_count > size_) {
throw std::out_of_range("span::subspan: count exceeds span bounds");
}
#endif
return span<element_type, dynamic_extent>(data_ + offset, actual_count);
}
private:
pointer data_;
size_type size_;
};
// Note: Deduction guides are C++17 feature, not available in C++14
// Helper functions
template<typename T>
constexpr span<T> make_span(T* ptr, typename span<T>::size_type count) {
return span<T>(ptr, count);
}
template<typename T>
constexpr span<T> make_span(T* first, T* last) {
return span<T>(first, last);
}
template<typename T, std::size_t N>
constexpr span<T, static_cast<std::ptrdiff_t>(N)> make_span(T (&arr)[N]) {
return span<T, static_cast<std::ptrdiff_t>(N)>(arr);
}
template<typename Container>
constexpr span<typename Container::value_type> make_span(Container& cont) {
return span<typename Container::value_type>(cont);
}
template<typename Container>
constexpr span<const typename Container::value_type> make_span(const Container& cont) {
return span<const typename Container::value_type>(cont);
}
} // namespace nonstd
#endif // NONSTD_SPAN_HPP

View File

@@ -2621,6 +2621,101 @@ class Attribute {
return get(t, dst, tinterp);
}
/// @brief Get TypedArrayView to the underlying array data of this Attribute.
///
/// Returns a zero-copy view over array data for scalar (default) values only.
/// This method does NOT support timesamples - only works with default values.
/// For non-array types or timesamples, returns an empty view.
///
/// The view provides efficient access to array data without copying, enabling
/// memory-optimized processing of vertex attributes, indices, and other array data.
///
/// @tparam T The desired element type for the view
/// @param strict_cast If true, requires exact type match; if false, allows compatible role type casting
/// @return TypedArrayView<const T> - may be empty if type conversion not possible or attribute has timesamples
///
/// Example:
/// ```cpp
/// // Get view of vertex positions as float3
/// auto positions_view = position_attr.get_value_view<value::float3>();
/// if (!positions_view.empty()) {
/// for (const auto& pos : positions_view) {
/// // Process position without copying data
/// }
/// }
///
/// // Get view as compatible role type
/// auto normals_view = normal_attr.get_value_view<value::vector3f>(); // float3 -> vector3f
/// ```
template <typename T>
TypedArrayView<const T> get_value_view(bool strict_cast = false) const {
// Only support scalar (default) values, not timesamples
if (has_timesamples()) {
return TypedArrayView<const T>(); // Empty view for timesamples
}
if (is_blocked()) {
return TypedArrayView<const T>(); // Empty view for blocked attributes
}
if (is_connection()) {
return TypedArrayView<const T>(); // Empty view for connections
}
if (!has_value()) {
return TypedArrayView<const T>(); // Empty view if no value
}
// Get the underlying value and create a view using Value::as_view()
const primvar::PrimVar& pvar = get_var();
const value::Value& val = pvar.value_raw();
return val.as_view<T>(strict_cast);
}
/// @brief Mutable version of get_value_view() for write access to array data.
///
/// Same as get_value_view() but returns a mutable view that allows modification
/// of the underlying array data. Only works with scalar (default) values.
///
/// @tparam T The desired element type for the view
/// @param strict_cast If true, requires exact type match; if false, allows compatible role type casting
/// @return TypedArrayView<T> - may be empty if type conversion not possible or attribute has timesamples
///
/// Example:
/// ```cpp
/// // Get mutable view and modify data in-place
/// auto positions_view = position_attr.get_value_view<value::float3>();
/// if (!positions_view.empty()) {
/// positions_view[0] = {1.0f, 2.0f, 3.0f}; // Modifies original data
/// }
/// ```
template <typename T>
TypedArrayView<T> get_value_view(bool strict_cast = false) {
// Only support scalar (default) values, not timesamples
if (has_timesamples()) {
return TypedArrayView<T>(); // Empty view for timesamples
}
if (is_blocked()) {
return TypedArrayView<T>(); // Empty view for blocked attributes
}
if (is_connection()) {
return TypedArrayView<T>(); // Empty view for connections
}
if (!has_value()) {
return TypedArrayView<T>(); // Empty view if no value
}
// Get the underlying value and create a view using Value::as_view()
primvar::PrimVar& pvar = get_var();
value::Value& val = pvar.value_raw();
return val.as_view<T>(strict_cast);
}
const AttrMeta &metas() const { return _metas; }
AttrMeta &metas() { return _metas; }

View File

@@ -7439,19 +7439,13 @@ size_t RenderScene::estimate_memory_usage() const {
// Estimate containers
total += nodes.capacity() * sizeof(Node);
for (const auto& node : nodes) {
// TODO: Add detailed Node memory estimation if needed
}
(void)nodes; // Suppress unused variable warning
total += images.capacity() * sizeof(TextureImage);
for (const auto& image : images) {
// TODO: Add detailed TextureImage memory estimation if needed
}
(void)images; // Suppress unused variable warning
total += materials.capacity() * sizeof(RenderMaterial);
for (const auto& material : materials) {
// TODO: Add detailed RenderMaterial memory estimation if needed
}
(void)materials; // Suppress unused variable warning
total += cameras.capacity() * sizeof(RenderCamera);
total += lights.capacity() * sizeof(RenderLight);
@@ -7470,14 +7464,10 @@ size_t RenderScene::estimate_memory_usage() const {
}
total += animations.capacity() * sizeof(Animation);
for (const auto& animation : animations) {
// TODO: Add detailed Animation memory estimation if needed
}
(void)animations; // Suppress unused variable warning
total += skeletons.capacity() * sizeof(SkelHierarchy);
for (const auto& skeleton : skeletons) {
// TODO: Add detailed SkelHierarchy memory estimation if needed
}
(void)skeletons; // Suppress unused variable warning
total += buffers.capacity() * sizeof(BufferData);
for (const auto& buffer : buffers) {

View File

@@ -0,0 +1,491 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2024 - Present, Syoyo Fujita.
// Copyright 2024 - Present, Light Transport Entertainment Inc.
///
/// @file typed-array-render-data.hh
/// @brief TypedArray-enhanced render-data structures for memory optimization
///
/// This header provides memory-optimized versions of Tydra render-data structures
/// that leverage TypedArray for reduced buffer copies and in-place transformations.
///
#pragma once
#include "render-data.hh"
#include "../typed-array.hh"
#include <memory>
#include <type_traits>
namespace tinyusdz {
namespace tydra {
namespace typed_array_opt {
///
/// Enhanced BufferData that uses TypedArray for memory optimization
///
template<typename T = uint8_t>
struct TypedBufferData {
ComponentType componentType{ComponentType::UInt8};
TypedArray<T> data;
// Constructor from existing BufferData
TypedBufferData(const BufferData& buffer) {
componentType = buffer.componentType;
if (!buffer.data.empty()) {
data = TypedArray<T>(reinterpret_cast<const T*>(buffer.data.data()),
buffer.data.size() / sizeof(T));
}
}
// Constructor with TypedArray
TypedBufferData(ComponentType type, TypedArray<T>&& typed_data)
: componentType(type), data(std::move(typed_data)) {}
// In-place type conversion methods
template<typename N>
TypedBufferData<N> transform_to(std::function<void(const T&, N&)> converter) && {
static_assert(sizeof(T) >= sizeof(N), "Can only transform to smaller or equal size types");
auto new_data = data.template transform_1to1<N>(converter);
return TypedBufferData<N>(componentType, std::move(new_data));
}
template<typename N>
TypedBufferData<N> expand_to(std::function<void(const T&, N&)> converter) && {
auto new_data = data.template transform_expand<N>(converter);
return TypedBufferData<N>(componentType, std::move(new_data));
}
// Get span view for efficient access
nonstd::span<T> span() { return data.span(); }
nonstd::span<const T> span() const { return data.span(); }
// Memory usage estimation
size_t estimate_memory_usage() const {
return sizeof(TypedBufferData) + data.storage().capacity();
}
};
///
/// Enhanced VertexAttribute using TypedArray for optimized vertex processing
///
struct TypedVertexAttribute {
std::string name;
VertexAttributeFormat format{VertexAttributeFormat::Vec3};
uint32_t elementSize{1};
uint32_t stride{0};
VertexVariability variability{VertexVariability::Vertex};
uint64_t handle{0};
// Type-erased storage using variant of common vertex data types
struct Storage {
enum Type {
FLOAT, VEC2, VEC3, VEC4,
INT, IVEC2, IVEC3, IVEC4,
UINT8, UINT16, UINT32
} type;
// Storage for different types
std::unique_ptr<TypedArray<float>> float_data;
std::unique_ptr<TypedArray<value::float2>> vec2_data;
std::unique_ptr<TypedArray<value::float3>> vec3_data;
std::unique_ptr<TypedArray<value::float4>> vec4_data;
std::unique_ptr<TypedArray<int32_t>> int_data;
std::unique_ptr<TypedArray<uint8_t>> uint8_data;
std::unique_ptr<TypedArray<uint16_t>> uint16_data;
std::unique_ptr<TypedArray<uint32_t>> uint32_data;
template<typename T>
void set_data(TypedArray<T>&& data) {
if constexpr (std::is_same_v<T, float>) {
type = FLOAT;
float_data = std::make_unique<TypedArray<float>>(std::move(data));
} else if constexpr (std::is_same_v<T, value::float2>) {
type = VEC2;
vec2_data = std::make_unique<TypedArray<value::float2>>(std::move(data));
} else if constexpr (std::is_same_v<T, value::float3>) {
type = VEC3;
vec3_data = std::make_unique<TypedArray<value::float3>>(std::move(data));
} else if constexpr (std::is_same_v<T, value::float4>) {
type = VEC4;
vec4_data = std::make_unique<TypedArray<value::float4>>(std::move(data));
} else if constexpr (std::is_same_v<T, int32_t>) {
type = INT;
int_data = std::make_unique<TypedArray<int32_t>>(std::move(data));
} else if constexpr (std::is_same_v<T, uint8_t>) {
type = UINT8;
uint8_data = std::make_unique<TypedArray<uint8_t>>(std::move(data));
} else if constexpr (std::is_same_v<T, uint16_t>) {
type = UINT16;
uint16_data = std::make_unique<TypedArray<uint16_t>>(std::move(data));
} else if constexpr (std::is_same_v<T, uint32_t>) {
type = UINT32;
uint32_data = std::make_unique<TypedArray<uint32_t>>(std::move(data));
}
}
template<typename T>
TypedArray<T>* get_data() {
if constexpr (std::is_same_v<T, float>) {
return (type == FLOAT) ? float_data.get() : nullptr;
} else if constexpr (std::is_same_v<T, value::float3>) {
return (type == VEC3) ? vec3_data.get() : nullptr;
} // ... add other types as needed
return nullptr;
}
size_t vertex_count() const {
switch(type) {
case FLOAT: return float_data ? float_data->size() : 0;
case VEC2: return vec2_data ? vec2_data->size() : 0;
case VEC3: return vec3_data ? vec3_data->size() : 0;
case VEC4: return vec4_data ? vec4_data->size() : 0;
case INT: return int_data ? int_data->size() : 0;
case UINT8: return uint8_data ? uint8_data->size() : 0;
case UINT16: return uint16_data ? uint16_data->size() : 0;
case UINT32: return uint32_data ? uint32_data->size() : 0;
}
return 0;
}
size_t estimate_memory_usage() const {
switch(type) {
case FLOAT: return float_data ? float_data->storage().capacity() : 0;
case VEC2: return vec2_data ? vec2_data->storage().capacity() : 0;
case VEC3: return vec3_data ? vec3_data->storage().capacity() : 0;
case VEC4: return vec4_data ? vec4_data->storage().capacity() : 0;
case INT: return int_data ? int_data->storage().capacity() : 0;
case UINT8: return uint8_data ? uint8_data->storage().capacity() : 0;
case UINT16: return uint16_data ? uint16_data->storage().capacity() : 0;
case UINT32: return uint32_data ? uint32_data->storage().capacity() : 0;
}
return 0;
}
} storage;
std::vector<uint32_t> indices; // Dedicated index buffer
// Constructor from existing VertexAttribute
TypedVertexAttribute(const VertexAttribute& vattr) {
name = vattr.name;
format = vattr.format;
elementSize = vattr.elementSize;
stride = vattr.stride;
variability = vattr.variability;
handle = vattr.handle;
indices = vattr.indices;
// Convert raw data to appropriate TypedArray based on format
convert_from_raw_data(vattr);
}
// Accessors
size_t vertex_count() const { return storage.vertex_count(); }
bool empty() const { return vertex_count() == 0; }
size_t num_bytes() const { return storage.estimate_memory_usage(); }
// Type conversion utilities
template<typename SrcT, typename DstT>
void transform_data_inplace(std::function<void(const SrcT&, DstT&)> converter) {
auto* src_data = storage.template get_data<SrcT>();
if (!src_data) return;
if constexpr (sizeof(SrcT) >= sizeof(DstT)) {
// Shrinking transform - can do in-place
auto dst_data = src_data->template transform_1to1<DstT>(converter);
storage.template set_data<DstT>(std::move(dst_data));
} else {
// Expanding transform - need buffer growth
auto dst_data = src_data->template transform_expand<DstT>(converter);
storage.template set_data<DstT>(std::move(dst_data));
}
}
// Precision reduction for memory optimization
void reduce_to_half_precision() {
if (storage.type == Storage::VEC3) {
// Convert float3 to half3 (uint16_t x3)
auto* vec3_data = storage.get_data<value::float3>();
if (vec3_data) {
auto half_data = vec3_data->template transform_1to1<uint16_t>([](const value::float3& src, uint16_t& dst) {
// Simple float to half conversion (would need proper half-float library)
dst = static_cast<uint16_t>(src[0] * 65535.0f); // Simplified
});
storage.set_data(std::move(half_data));
format = VertexAttributeFormat::Half3;
}
}
}
// Quantize floating point vertex colors to 8-bit
void quantize_vertex_colors() {
if (storage.type == Storage::VEC3) {
auto* vec3_data = storage.get_data<value::float3>();
if (vec3_data) {
auto uint8_data = vec3_data->template transform_1to1<uint8_t>([](const value::float3& color, uint8_t& quantized) {
quantized = static_cast<uint8_t>(std::clamp(color[0] * 255.0f, 0.0f, 255.0f));
});
storage.set_data(std::move(uint8_data));
format = VertexAttributeFormat::Byte3;
}
}
}
size_t estimate_memory_usage() const {
return sizeof(TypedVertexAttribute) + storage.estimate_memory_usage() +
indices.capacity() * sizeof(uint32_t);
}
private:
void convert_from_raw_data(const VertexAttribute& vattr) {
switch(vattr.format) {
case VertexAttributeFormat::Float: {
auto float_array = TypedArray<float>(
reinterpret_cast<const float*>(vattr.data.data()),
vattr.data.size() / sizeof(float)
);
storage.set_data(std::move(float_array));
break;
}
case VertexAttributeFormat::Vec3: {
auto vec3_array = TypedArray<value::float3>(
reinterpret_cast<const value::float3*>(vattr.data.data()),
vattr.data.size() / sizeof(value::float3)
);
storage.set_data(std::move(vec3_array));
break;
}
case VertexAttributeFormat::Vec2: {
auto vec2_array = TypedArray<value::float2>(
reinterpret_cast<const value::float2*>(vattr.data.data()),
vattr.data.size() / sizeof(value::float2)
);
storage.set_data(std::move(vec2_array));
break;
}
// Add other format conversions as needed
default: {
// Fallback to uint8_t for unknown formats
auto uint8_array = TypedArray<uint8_t>(vattr.data.data(), vattr.data.size());
storage.set_data(std::move(uint8_array));
break;
}
}
}
};
///
/// Memory-optimized RenderMesh using TypedArray
///
struct TypedRenderMesh {
std::string prim_name;
std::string abs_path;
std::string display_name;
bool is_single_indexable{false};
bool doubleSided{false};
bool is_rightHanded{true};
// Vertex data using TypedArray for efficient storage and transformation
TypedArray<value::float3> points;
// Vertex attributes using TypedVertexAttribute
TypedVertexAttribute normals;
std::unordered_map<uint32_t, TypedVertexAttribute> texcoords;
TypedVertexAttribute tangents;
TypedVertexAttribute binormals;
TypedVertexAttribute vertex_colors;
TypedVertexAttribute vertex_opacities;
// Index buffers
TypedArray<uint32_t> usd_face_vertex_indices;
TypedArray<uint32_t> usd_face_vertex_counts;
TypedArray<uint32_t> triangulated_face_vertex_indices;
TypedArray<uint32_t> triangulated_face_vertex_counts;
// Material and display properties
int material_id{-1};
int backface_material_id{-1};
value::color3f displayColor{0.18f, 0.18f, 0.18f};
float displayOpacity{1.0f};
uint64_t handle{0};
// Constructor from existing RenderMesh
TypedRenderMesh(const RenderMesh& mesh)
: prim_name(mesh.prim_name)
, abs_path(mesh.abs_path)
, display_name(mesh.display_name)
, is_single_indexable(mesh.is_single_indexable)
, doubleSided(mesh.doubleSided)
, is_rightHanded(mesh.is_rightHanded)
, normals(mesh.normals)
, tangents(mesh.tangents)
, binormals(mesh.binormals)
, vertex_colors(mesh.vertex_colors)
, vertex_opacities(mesh.vertex_opacities)
, material_id(mesh.material_id)
, backface_material_id(mesh.backface_material_id)
, displayColor(mesh.displayColor)
, displayOpacity(mesh.displayOpacity)
, handle(mesh.handle) {
// Convert vertex data
if (!mesh.points.empty()) {
points = TypedArray<value::float3>(mesh.points.data(), mesh.points.size());
}
// Convert texcoords
for (const auto& [slot_id, texcoord] : mesh.texcoords) {
texcoords[slot_id] = TypedVertexAttribute(texcoord);
}
// Convert index data
if (!mesh.usdFaceVertexIndices.empty()) {
usd_face_vertex_indices = TypedArray<uint32_t>(
mesh.usdFaceVertexIndices.data(), mesh.usdFaceVertexIndices.size());
}
if (!mesh.usdFaceVertexCounts.empty()) {
usd_face_vertex_counts = TypedArray<uint32_t>(
mesh.usdFaceVertexCounts.data(), mesh.usdFaceVertexCounts.size());
}
if (!mesh.triangulatedFaceVertexIndices.empty()) {
triangulated_face_vertex_indices = TypedArray<uint32_t>(
mesh.triangulatedFaceVertexIndices.data(), mesh.triangulatedFaceVertexIndices.size());
}
if (!mesh.triangulatedFaceVertexCounts.empty()) {
triangulated_face_vertex_counts = TypedArray<uint32_t>(
mesh.triangulatedFaceVertexCounts.data(), mesh.triangulatedFaceVertexCounts.size());
}
}
// Memory optimization methods
void optimize_memory() {
points.shrink_to_fit();
usd_face_vertex_indices.shrink_to_fit();
usd_face_vertex_counts.shrink_to_fit();
triangulated_face_vertex_indices.shrink_to_fit();
triangulated_face_vertex_counts.shrink_to_fit();
}
// Precision reduction for memory savings
void reduce_precision() {
normals.reduce_to_half_precision();
tangents.reduce_to_half_precision();
binormals.reduce_to_half_precision();
vertex_colors.quantize_vertex_colors();
for (auto& [slot_id, texcoord] : texcoords) {
texcoord.reduce_to_half_precision();
}
}
// Convert vertex data types for different rendering pipelines
void convert_indices_to_16bit() {
// Convert 32-bit indices to 16-bit if vertex count allows
if (points.size() < 65536) {
if (!usd_face_vertex_indices.empty()) {
auto indices_16 = usd_face_vertex_indices.transform_1to1<uint16_t>(
[](const uint32_t& src, uint16_t& dst) {
dst = static_cast<uint16_t>(src);
});
// Would need to store as uint16 array...
}
}
}
// Estimate memory usage
size_t estimate_memory_usage() const {
size_t total = sizeof(TypedRenderMesh);
total += points.storage().capacity();
total += normals.estimate_memory_usage();
total += tangents.estimate_memory_usage();
total += binormals.estimate_memory_usage();
total += vertex_colors.estimate_memory_usage();
total += vertex_opacities.estimate_memory_usage();
total += usd_face_vertex_indices.storage().capacity();
total += usd_face_vertex_counts.storage().capacity();
total += triangulated_face_vertex_indices.storage().capacity();
total += triangulated_face_vertex_counts.storage().capacity();
for (const auto& [slot_id, texcoord] : texcoords) {
total += texcoord.estimate_memory_usage();
}
return total;
}
// In-place coordinate system conversion
void transform_coordinate_system(const value::matrix4f& transform_matrix) {
// Transform positions in-place
points.span().for_each([&](value::float3& pos) {
// Apply matrix transformation to position
value::float4 pos4{pos[0], pos[1], pos[2], 1.0f};
// Apply transform (simplified)
pos[0] = pos4[0]; pos[1] = pos4[1]; pos[2] = pos4[2];
});
// Transform normals (inverse transpose)
auto* normal_data = normals.storage.get_data<value::float3>();
if (normal_data) {
normal_data->span().for_each([&](value::float3& normal) {
// Apply inverse transpose transformation to normal
// (simplified - would need proper matrix operations)
});
}
}
};
///
/// Conversion utilities for TypedArray integration
///
class TypedArrayRenderConverter {
public:
// Convert existing RenderMesh to TypedRenderMesh with optimizations
static TypedRenderMesh convert_mesh(const RenderMesh& mesh, bool optimize_memory = true) {
TypedRenderMesh typed_mesh(mesh);
if (optimize_memory) {
typed_mesh.optimize_memory();
typed_mesh.reduce_precision();
}
return typed_mesh;
}
// Batch conversion of multiple meshes with shared optimization settings
static std::vector<TypedRenderMesh> convert_meshes(
const std::vector<RenderMesh>& meshes,
bool optimize_memory = true,
bool reduce_precision = false) {
std::vector<TypedRenderMesh> typed_meshes;
typed_meshes.reserve(meshes.size());
for (const auto& mesh : meshes) {
typed_meshes.emplace_back(mesh);
auto& typed_mesh = typed_meshes.back();
if (optimize_memory) {
typed_mesh.optimize_memory();
}
if (reduce_precision) {
typed_mesh.reduce_precision();
}
}
return typed_meshes;
}
// Estimate memory savings from conversion
static size_t estimate_memory_savings(const RenderMesh& original_mesh,
const TypedRenderMesh& typed_mesh) {
size_t original_size = original_mesh.estimate_memory_usage();
size_t typed_size = typed_mesh.estimate_memory_usage();
return (original_size > typed_size) ? (original_size - typed_size) : 0;
}
};
} // namespace typed_array_opt
} // namespace tydra
} // namespace tinyusdz

49
src/typed-array.cc Normal file
View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2024 - Present, Syoyo Fujita.
// Copyright 2024 - Present, Light Transport Entertainment Inc.
///
/// @file typed-array.cc
/// @brief TypedArray implementation file
///
#include "typed-array.hh"
// Implementation file for TypedArray - since it's header-only template class,
// most functionality is implemented in the header file.
// This file can be used for explicit template instantiations if needed.
namespace tinyusdz {
// Explicit template instantiations for commonly used types
// This can help reduce compilation time by pre-instantiating frequently used types
// TypedArray instantiations
template class TypedArray<uint8_t>;
template class TypedArray<uint16_t>;
template class TypedArray<uint32_t>;
template class TypedArray<int8_t>;
template class TypedArray<int16_t>;
template class TypedArray<int32_t>;
template class TypedArray<float>;
template class TypedArray<double>;
// TypedArrayView instantiations
template class TypedArrayView<uint8_t>;
template class TypedArrayView<const uint8_t>;
template class TypedArrayView<uint16_t>;
template class TypedArrayView<const uint16_t>;
template class TypedArrayView<uint32_t>;
template class TypedArrayView<const uint32_t>;
template class TypedArrayView<int8_t>;
template class TypedArrayView<const int8_t>;
template class TypedArrayView<int16_t>;
template class TypedArrayView<const int16_t>;
template class TypedArrayView<int32_t>;
template class TypedArrayView<const int32_t>;
template class TypedArrayView<float>;
template class TypedArrayView<const float>;
template class TypedArrayView<double>;
template class TypedArrayView<const double>;
} // namespace tinyusdz

795
src/typed-array.hh Normal file
View File

@@ -0,0 +1,795 @@
// SPDX-License-Identifier: Apache 2.0
// Copyright 2024 - Present, Syoyo Fujita.
// Copyright 2024 - Present, Light Transport Entertainment Inc.
///
/// TypedArray: A type-safe wrapper around std::vector<uint8_t> with nonstd::span views
/// Provides in-place type transformation capabilities for memory-efficient operations
///
#pragma once
#include <cassert>
#include <cstdint>
#include <cstring>
#include <type_traits>
#include <vector>
#include <functional>
#include <stdexcept>
#include "nonstd/span.hpp"
namespace tinyusdz {
template<typename T>
class TypedArray {
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = T*;
using const_iterator = const T*;
// Default constructor
TypedArray() = default;
// Constructor with size
explicit TypedArray(size_type count) {
resize(count);
}
// Constructor with size and default value
TypedArray(size_type count, const T& value) {
resize(count, value);
}
// Constructor from initializer list
TypedArray(std::initializer_list<T> init) {
reserve(init.size());
for (const auto& item : init) {
push_back(item);
}
}
// Constructor from existing data
TypedArray(const T* data, size_type count) {
if (data && count > 0) {
_storage.resize(count * sizeof(T));
std::memcpy(_storage.data(), data, count * sizeof(T));
}
}
// Copy constructor
TypedArray(const TypedArray& other) = default;
// Move constructor
TypedArray(TypedArray&& other) noexcept = default;
// Copy assignment
TypedArray& operator=(const TypedArray& other) = default;
// Move assignment
TypedArray& operator=(TypedArray&& other) noexcept = default;
// Destructor
~TypedArray() = default;
// Size operations
size_type size() const noexcept {
return _storage.size() / sizeof(T);
}
size_type capacity() const noexcept {
return _storage.capacity() / sizeof(T);
}
bool empty() const noexcept {
return _storage.empty();
}
size_type max_size() const noexcept {
return _storage.max_size() / sizeof(T);
}
// Data access
pointer data() noexcept {
return reinterpret_cast<pointer>(_storage.data());
}
const_pointer data() const noexcept {
return reinterpret_cast<const_pointer>(_storage.data());
}
// Element access
reference operator[](size_type index) {
return data()[index];
}
const_reference operator[](size_type index) const {
return data()[index];
}
reference at(size_type index) {
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
// Exceptions disabled - just return element (bounds checking in debug mode only)
assert(index < size() && "TypedArray::at: index out of range");
#else
if (index >= size()) {
throw std::out_of_range("TypedArray::at: index out of range");
}
#endif
return data()[index];
}
const_reference at(size_type index) const {
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
// Exceptions disabled - just return element (bounds checking in debug mode only)
assert(index < size() && "TypedArray::at: index out of range");
#else
if (index >= size()) {
throw std::out_of_range("TypedArray::at: index out of range");
}
#endif
return data()[index];
}
reference front() {
return data()[0];
}
const_reference front() const {
return data()[0];
}
reference back() {
return data()[size() - 1];
}
const_reference back() const {
return data()[size() - 1];
}
// Iterators
iterator begin() noexcept {
return data();
}
const_iterator begin() const noexcept {
return data();
}
const_iterator cbegin() const noexcept {
return data();
}
iterator end() noexcept {
return data() + size();
}
const_iterator end() const noexcept {
return data() + size();
}
const_iterator cend() const noexcept {
return data() + size();
}
// Span views
nonstd::span<T> span() noexcept {
return nonstd::span<T>(data(), size());
}
nonstd::span<const T> span() const noexcept {
return nonstd::span<const T>(data(), size());
}
nonstd::span<T> subspan(size_type offset, size_type count = static_cast<size_type>(-1)) {
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
assert(offset <= size() && "TypedArray::subspan: offset out of range");
#else
if (offset > size()) {
throw std::out_of_range("TypedArray::subspan: offset out of range");
}
#endif
size_type actual_count = (count == static_cast<size_type>(-1)) ? (size() - offset) : count;
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
assert(offset + actual_count <= size() && "TypedArray::subspan: count exceeds array bounds");
#else
if (offset + actual_count > size()) {
throw std::out_of_range("TypedArray::subspan: count exceeds array bounds");
}
#endif
return nonstd::span<T>(data() + offset, actual_count);
}
nonstd::span<const T> subspan(size_type offset, size_type count = static_cast<size_type>(-1)) const {
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
assert(offset <= size() && "TypedArray::subspan: offset out of range");
#else
if (offset > size()) {
throw std::out_of_range("TypedArray::subspan: offset out of range");
}
#endif
size_type actual_count = (count == static_cast<size_type>(-1)) ? (size() - offset) : count;
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
assert(offset + actual_count <= size() && "TypedArray::subspan: count exceeds array bounds");
#else
if (offset + actual_count > size()) {
throw std::out_of_range("TypedArray::subspan: count exceeds array bounds");
}
#endif
return nonstd::span<const T>(data() + offset, actual_count);
}
// Modifiers
void clear() noexcept {
_storage.clear();
}
void resize(size_type count) {
_storage.resize(count * sizeof(T));
}
void resize(size_type count, const T& value) {
size_type old_size = size();
_storage.resize(count * sizeof(T));
// Initialize new elements with the given value
for (size_type i = old_size; i < count; ++i) {
new (data() + i) T(value);
}
}
void reserve(size_type new_capacity) {
_storage.reserve(new_capacity * sizeof(T));
}
void shrink_to_fit() {
_storage.shrink_to_fit();
}
void push_back(const T& value) {
size_type old_size = size();
resize(old_size + 1);
data()[old_size] = value;
}
void push_back(T&& value) {
size_type old_size = size();
resize(old_size + 1);
data()[old_size] = std::move(value);
}
void pop_back() {
if (!empty()) {
resize(size() - 1);
}
}
// In-place transform function
// Transforms from current type T to new type N
// Requirement: sizeof(T) >= sizeof(N) for in-place operation
template<typename N, typename Func>
TypedArray<N> transform(Func func) const {
static_assert(sizeof(T) >= sizeof(N),
"transform: source type size must be >= destination type size for in-place operation");
static_assert(std::is_trivially_copyable<N>::value,
"transform: destination type must be trivially copyable");
TypedArray<N> result;
if (empty()) {
return result;
}
// Calculate how many elements of type N we can fit
size_type src_count = size();
size_type dst_count = (src_count * sizeof(T)) / sizeof(N);
result.resize(dst_count);
// Apply transformation
for (size_type i = 0; i < src_count; ++i) {
// Calculate how many N elements this T element can produce
size_type n_elements_per_t = sizeof(T) / sizeof(N);
for (size_type j = 0; j < n_elements_per_t && (i * n_elements_per_t + j) < dst_count; ++j) {
size_type dst_idx = i * n_elements_per_t + j;
func(data()[i], result.data()[dst_idx]);
}
}
return result;
}
// Specialized transform for 1:1 type conversion (e.g., int to float)
template<typename N, typename Func>
TypedArray<N> transform_1to1(Func func) const {
static_assert(sizeof(T) >= sizeof(N),
"transform_1to1: source type size must be >= destination type size");
static_assert(std::is_trivially_copyable<N>::value,
"transform_1to1: destination type must be trivially copyable");
TypedArray<N> result;
if (empty()) {
return result;
}
result.resize(size());
for (size_type i = 0; i < size(); ++i) {
func(data()[i], result.data()[i]);
}
return result;
}
// Enhanced transform supporting sizeof(srcTy) <= sizeof(dstTy) with controlled buffer growth
template<typename N, typename Func>
TypedArray<N> transform_expand(Func func) const {
static_assert(std::is_trivially_copyable<N>::value,
"transform_expand: destination type must be trivially copyable");
TypedArray<N> result;
if (empty()) {
return result;
}
size_type src_count = size();
// Use template meta-programming instead of if constexpr for C++14 compatibility
return transform_expand_impl<N>(func, src_count, result,
std::integral_constant<bool, (sizeof(T) >= sizeof(N))>{});
}
// In-place transform with expansion (modifies current array)
// Only works when sizeof(T) <= sizeof(N) and we have sufficient capacity
template<typename N, typename Func>
TypedArray<N> transform_inplace_expand(Func func) {
static_assert(std::is_trivially_copyable<N>::value,
"transform_inplace_expand: destination type must be trivially copyable");
if (empty()) {
TypedArray<N> result;
return result;
}
size_type src_count = size();
size_type required_bytes = src_count * sizeof(N);
size_type current_bytes = _storage.size();
// Check if we can expand in-place or need reallocation
if (required_bytes <= _storage.capacity()) {
// Can expand in-place - transform from end to beginning to avoid overwriting
_storage.resize(required_bytes);
// Cast storage to both source and destination types
T* src_data = reinterpret_cast<T*>(_storage.data());
N* dst_data = reinterpret_cast<N*>(_storage.data());
// Transform backwards to avoid overlap issues
for (size_type i = src_count; i > 0; --i) {
size_type src_idx = i - 1;
size_type dst_idx = src_idx;
func(src_data[src_idx], dst_data[dst_idx]);
}
// Return TypedArray<N> that shares the same storage
TypedArray<N> result;
result._storage = std::move(_storage);
_storage.clear(); // Current array is now empty
return result;
} else {
// Need reallocation - use regular transform_expand
return transform_expand<N>(func);
}
}
// Transform with controlled growth - specify maximum buffer growth factor
template<typename N, typename Func>
TypedArray<N> transform_with_limit(Func func, double max_growth_factor = 2.0) const {
static_assert(std::is_trivially_copyable<N>::value,
"transform_with_limit: destination type must be trivially copyable");
TypedArray<N> result;
if (empty()) {
return result;
}
size_type src_count = size();
size_type required_bytes = src_count * sizeof(N);
size_type current_bytes = _storage.size();
// Check if expansion exceeds the growth limit
double growth_ratio = static_cast<double>(required_bytes) / current_bytes;
if (growth_ratio > max_growth_factor) {
throw std::runtime_error("transform_with_limit: required buffer growth exceeds limit");
}
// Proceed with transformation
result.resize(src_count);
for (size_type i = 0; i < src_count; ++i) {
func(data()[i], result.data()[i]);
}
return result;
}
// Get raw storage access (advanced usage)
std::vector<uint8_t>& storage() noexcept {
return _storage;
}
const std::vector<uint8_t>& storage() const noexcept {
return _storage;
}
// Swap
void swap(TypedArray& other) noexcept {
_storage.swap(other._storage);
}
// Comparison operators
bool operator==(const TypedArray& other) const {
return size() == other.size() &&
std::memcmp(data(), other.data(), size() * sizeof(T)) == 0;
}
bool operator!=(const TypedArray& other) const {
return !(*this == other);
}
private:
std::vector<uint8_t> _storage;
// Helper method implementations for C++14 compatibility (instead of if constexpr)
template<typename N, typename Func>
TypedArray<N> transform_expand_impl(Func func, size_type src_count, TypedArray<N>& result,
std::true_type /* sizeof(T) >= sizeof(N) */) const {
// Shrinking case - can use in-place approach
size_type dst_count = (src_count * sizeof(T)) / sizeof(N);
result.resize(dst_count);
for (size_type i = 0; i < src_count; ++i) {
size_type n_elements_per_t = sizeof(T) / sizeof(N);
for (size_type j = 0; j < n_elements_per_t && (i * n_elements_per_t + j) < dst_count; ++j) {
size_type dst_idx = i * n_elements_per_t + j;
func(data()[i], result.data()[dst_idx]);
}
}
return result;
}
template<typename N, typename Func>
TypedArray<N> transform_expand_impl(Func func, size_type src_count, TypedArray<N>& result,
std::false_type /* sizeof(T) < sizeof(N) */) const {
// Expanding case - requires buffer growth
// Buffer grows exactly to needed size: num_items * sizeof(dstTy)
result.resize(src_count);
for (size_type i = 0; i < src_count; ++i) {
func(data()[i], result.data()[i]);
}
return result;
}
};
///
/// TypedArrayView: A lightweight view over typed data using nonstd::span
/// Provides zero-copy access to data stored in various containers
///
template<typename T>
class TypedArrayView {
public:
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = pointer;
using const_iterator = const_pointer;
// Default constructor - creates empty view
TypedArrayView() noexcept : _span() {}
// Constructor from raw pointer and size
TypedArrayView(pointer data, size_type count) noexcept
: _span(data, count) {}
// Constructor from raw pointer range
TypedArrayView(pointer first, pointer last) noexcept
: _span(first, last) {}
// Constructor from C-style array
template<std::size_t N>
TypedArrayView(T (&arr)[N]) noexcept
: _span(arr, N) {}
// Constructor from std::vector with type size validation
template<typename U>
TypedArrayView(const std::vector<U>& vec) noexcept {
static_assert(std::is_trivially_copyable<T>::value,
"TypedArrayView: T must be trivially copyable");
static_assert(std::is_trivially_copyable<U>::value,
"TypedArrayView: source type must be trivially copyable");
if (sizeof(T) > sizeof(U) || vec.size() * sizeof(U) < sizeof(T)) {
// Cannot safely view as T - create empty view
_span = nonstd::span<T>();
} else {
// Safe to view as T
size_type count = (vec.size() * sizeof(U)) / sizeof(T);
_span = nonstd::span<T>(reinterpret_cast<const T*>(vec.data()), count);
}
}
// Constructor from mutable std::vector with type size validation
template<typename U>
TypedArrayView(std::vector<U>& vec) noexcept {
static_assert(std::is_trivially_copyable<T>::value,
"TypedArrayView: T must be trivially copyable");
static_assert(std::is_trivially_copyable<U>::value,
"TypedArrayView: source type must be trivially copyable");
if (sizeof(T) > sizeof(U) || vec.size() * sizeof(U) < sizeof(T)) {
// Cannot safely view as T - create empty view
_span = nonstd::span<T>();
} else {
// Safe to view as T
size_type count = (vec.size() * sizeof(U)) / sizeof(T);
_span = nonstd::span<T>(reinterpret_cast<T*>(vec.data()), count);
}
}
// Constructor from TypedArray with type size validation
template<typename U>
TypedArrayView(const TypedArray<U>& typed_array) noexcept {
static_assert(std::is_trivially_copyable<T>::value,
"TypedArrayView: T must be trivially copyable");
static_assert(std::is_trivially_copyable<U>::value,
"TypedArrayView: source type must be trivially copyable");
if (sizeof(T) > sizeof(U) || typed_array.size() * sizeof(U) < sizeof(T)) {
// Cannot safely view as T - create empty view
_span = nonstd::span<T>();
} else {
// Safe to view as T
size_type count = (typed_array.size() * sizeof(U)) / sizeof(T);
_span = nonstd::span<T>(reinterpret_cast<const T*>(typed_array.data()), count);
}
}
// Constructor from mutable TypedArray with type size validation
template<typename U>
TypedArrayView(TypedArray<U>& typed_array) noexcept {
static_assert(std::is_trivially_copyable<T>::value,
"TypedArrayView: T must be trivially copyable");
static_assert(std::is_trivially_copyable<U>::value,
"TypedArrayView: source type must be trivially copyable");
if (sizeof(T) > sizeof(U) || typed_array.size() * sizeof(U) < sizeof(T)) {
// Cannot safely view as T - create empty view
_span = nonstd::span<T>();
} else {
// Safe to view as T
size_type count = (typed_array.size() * sizeof(U)) / sizeof(T);
_span = nonstd::span<T>(reinterpret_cast<T*>(typed_array.data()), count);
}
}
// Constructor from nonstd::span
explicit TypedArrayView(nonstd::span<T> sp) noexcept : _span(sp) {}
// Copy constructor
TypedArrayView(const TypedArrayView& other) noexcept = default;
// Assignment operator
TypedArrayView& operator=(const TypedArrayView& other) noexcept = default;
// Size and capacity
size_type size() const noexcept {
return _span.size();
}
size_type size_bytes() const noexcept {
return _span.size_bytes();
}
bool empty() const noexcept {
return _span.empty();
}
// Data access
pointer data() noexcept {
return _span.data();
}
const_pointer data() const noexcept {
return _span.data();
}
// Element access
reference operator[](size_type index) const {
return _span[index];
}
reference at(size_type index) const {
#if !defined(TINYUSDZ_CXX_EXCEPTIONS) || (TINYUSDZ_CXX_EXCEPTIONS == 0)
assert(index < size() && "TypedArrayView::at: index out of range");
#else
if (index >= size()) {
throw std::out_of_range("TypedArrayView::at: index out of range");
}
#endif
return _span[index];
}
reference front() const {
return _span.front();
}
reference back() const {
return _span.back();
}
// Iterators
iterator begin() const noexcept {
return _span.begin();
}
iterator end() const noexcept {
return _span.end();
}
const_iterator cbegin() const noexcept {
return _span.cbegin();
}
const_iterator cend() const noexcept {
return _span.cend();
}
// Subviews
TypedArrayView first(size_type count) const {
return TypedArrayView(_span.first(count));
}
TypedArrayView last(size_type count) const {
return TypedArrayView(_span.last(count));
}
TypedArrayView subspan(size_type offset, size_type count = static_cast<size_type>(-1)) const {
size_type actual_count = (count == static_cast<size_type>(-1)) ? (size() - offset) : count;
return TypedArrayView(_span.subspan(offset, actual_count));
}
// Get underlying span
nonstd::span<T> span() const noexcept {
return _span;
}
// Type reinterpret view (with validation)
template<typename N>
TypedArrayView<N> reinterpret_as() const noexcept {
static_assert(std::is_trivially_copyable<N>::value,
"reinterpret_as: destination type must be trivially copyable");
if (sizeof(N) > sizeof(T) || size() * sizeof(T) < sizeof(N)) {
// Cannot safely reinterpret as N - return empty view
return TypedArrayView<N>();
}
// Safe to reinterpret as N
size_type new_count = (size() * sizeof(T)) / sizeof(N);
return TypedArrayView<N>(reinterpret_cast<const N*>(data()), new_count);
}
// Mutable type reinterpret view (with validation)
template<typename N>
TypedArrayView<N> reinterpret_as_mutable() const noexcept {
static_assert(std::is_trivially_copyable<N>::value,
"reinterpret_as_mutable: destination type must be trivially copyable");
static_assert(!std::is_const<T>::value,
"reinterpret_as_mutable: source type must not be const");
if (sizeof(N) > sizeof(T) || size() * sizeof(T) < sizeof(N)) {
// Cannot safely reinterpret as N - return empty view
return TypedArrayView<N>();
}
// Safe to reinterpret as N
size_type new_count = (size() * sizeof(T)) / sizeof(N);
return TypedArrayView<N>(reinterpret_cast<N*>(const_cast<typename std::remove_const<T>::type*>(data())), new_count);
}
// Check if this view is valid (non-empty and properly aligned)
bool is_valid() const noexcept {
return !empty() && data() != nullptr &&
(reinterpret_cast<std::uintptr_t>(data()) % alignof(T)) == 0;
}
// Comparison operators
bool operator==(const TypedArrayView& other) const noexcept {
if (size() != other.size()) return false;
if (data() == other.data()) return true; // Same memory location
return std::memcmp(data(), other.data(), size() * sizeof(T)) == 0;
}
bool operator!=(const TypedArrayView& other) const noexcept {
return !(*this == other);
}
private:
nonstd::span<T> _span;
};
// Non-member swap
template<typename T>
void swap(TypedArray<T>& lhs, TypedArray<T>& rhs) noexcept {
lhs.swap(rhs);
}
// Convenience functions for creating TypedArrayView
template<typename T>
TypedArrayView<T> make_typed_array_view(T* data, std::size_t count) {
return TypedArrayView<T>(data, count);
}
template<typename T>
TypedArrayView<const T> make_typed_array_view(const T* data, std::size_t count) {
return TypedArrayView<const T>(data, count);
}
template<typename T, std::size_t N>
TypedArrayView<T> make_typed_array_view(T (&arr)[N]) {
return TypedArrayView<T>(arr);
}
template<typename T>
TypedArrayView<T> make_typed_array_view(std::vector<T>& vec) {
return TypedArrayView<T>(vec);
}
template<typename T>
TypedArrayView<const T> make_typed_array_view(const std::vector<T>& vec) {
return TypedArrayView<const T>(vec);
}
template<typename T>
TypedArrayView<T> make_typed_array_view(TypedArray<T>& arr) {
return TypedArrayView<T>(arr);
}
template<typename T>
TypedArrayView<const T> make_typed_array_view(const TypedArray<T>& arr) {
return TypedArrayView<const T>(arr);
}
template<typename T>
TypedArrayView<T> make_typed_array_view(nonstd::span<T> sp) {
return TypedArrayView<T>(sp);
}
// Type reinterpret convenience functions
template<typename T, typename U>
TypedArrayView<T> reinterpret_typed_array_view(const TypedArrayView<U>& view) {
return view.template reinterpret_as<T>();
}
template<typename T, typename U>
TypedArrayView<T> reinterpret_typed_array_view_mutable(const TypedArrayView<U>& view) {
return view.template reinterpret_as_mutable<T>();
}
// Convenience function to create TypedArray from span
template<typename T>
TypedArray<T> make_typed_array(nonstd::span<const T> sp) {
return TypedArray<T>(sp.data(), sp.size());
}
} // namespace tinyusdz

View File

@@ -57,6 +57,7 @@
#endif
#include "token-type.hh"
#include "typed-array.hh"
#include "common-macros.inc"
// forward decl
@@ -2074,6 +2075,78 @@ class Value {
return nullptr;
}
//
// Get TypedArrayView to the underlying data.
//
// Returns a view over array data if the value contains an array type that's compatible
// with the requested element type T. For non-array types, returns an empty view.
//
// The view provides zero-copy access to the underlying data with type safety validation.
//
// Template parameter T: the desired element type for the view
// Parameter strict_cast: if true, requires exact type match; if false, allows compatible type casting
//
// Returns: TypedArrayView<T> - may be empty if type conversion is not possible
//
template <class T>
TypedArrayView<const T> as_view(bool strict_cast = false) const {
// Check if this is an array type
if (!(v_.type_id() & value::TYPE_ID_1D_ARRAY_BIT)) {
// Not an array type - return empty view
return TypedArrayView<const T>();
}
// For arrays, we need to check if we can safely view the underlying data as T
uint32_t underlying_type_id = v_.underlying_type_id() & (~value::TYPE_ID_1D_ARRAY_BIT);
uint32_t target_type_id = TypeTraits<T>::underlying_type_id();
if (strict_cast) {
// Strict cast - requires exact underlying type match
if (underlying_type_id != target_type_id) {
return TypedArrayView<const T>();
}
} else {
// Non-strict cast - allow compatible types (same underlying type)
if (underlying_type_id != target_type_id) {
return TypedArrayView<const T>();
}
}
// Try to get the array data and create a view
return create_array_view_helper<T>(strict_cast);
}
//
// Non-const version of as_view() for mutable access
//
template <class T>
TypedArrayView<T> as_view(bool strict_cast = false) {
// Check if this is an array type
if (!(v_.type_id() & value::TYPE_ID_1D_ARRAY_BIT)) {
// Not an array type - return empty view
return TypedArrayView<T>();
}
// For arrays, we need to check if we can safely view the underlying data as T
uint32_t underlying_type_id = v_.underlying_type_id() & (~value::TYPE_ID_1D_ARRAY_BIT);
uint32_t target_type_id = TypeTraits<T>::underlying_type_id();
if (strict_cast) {
// Strict cast - requires exact underlying type match
if (underlying_type_id != target_type_id) {
return TypedArrayView<T>();
}
} else {
// Non-strict cast - allow compatible types (same underlying type)
if (underlying_type_id != target_type_id) {
return TypedArrayView<T>();
}
}
// Try to get the array data and create a view
return create_array_view_helper_mutable<T>(strict_cast);
}
#if 0
// Useful function to retrieve concrete value with type T.
@@ -2136,6 +2209,181 @@ class Value {
private:
// any_value v_;
linb::any v_{nullptr};
// Helper methods for as_view() implementation
template <class T>
TypedArrayView<const T> create_array_view_helper(bool strict_cast) const {
// Try common array types that could contain T elements
// Direct type match - try std::vector<T>
if (auto* vec = as<std::vector<T>>(strict_cast)) {
return TypedArrayView<const T>(*vec);
}
// Try related types based on underlying type compatibility
if (!strict_cast) {
// Handle role type conversions (e.g., float3 <-> vector3f <-> normal3f)
uint32_t target_underlying_id = TypeTraits<T>::underlying_type_id();
switch (target_underlying_id) {
case TYPE_ID_FLOAT: {
if (auto* vec = as<std::vector<float>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_DOUBLE: {
if (auto* vec = as<std::vector<double>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT3: {
// Try float3, vector3f, normal3f, color3f, point3f
if (auto* vec = as<std::vector<float3>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<vector3f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<normal3f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<color3f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<point3f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT2: {
// Try float2, texcoord2f
if (auto* vec = as<std::vector<float2>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<texcoord2f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT4: {
// Try float4, color4f
if (auto* vec = as<std::vector<float4>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<color4f>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_INT32: {
if (auto* vec = as<std::vector<int32_t>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_UINT32: {
if (auto* vec = as<std::vector<uint32_t>>(false)) {
return TypedArrayView<const T>(reinterpret_cast<const T*>(vec->data()), vec->size());
}
break;
}
default:
break;
}
}
// No compatible type found
return TypedArrayView<const T>();
}
template <class T>
TypedArrayView<T> create_array_view_helper_mutable(bool strict_cast) {
// Try common array types that could contain T elements
// Direct type match - try std::vector<T>
if (auto* vec = as<std::vector<T>>(strict_cast)) {
return TypedArrayView<T>(*vec);
}
// Try related types based on underlying type compatibility
if (!strict_cast) {
// Handle role type conversions (e.g., float3 <-> vector3f <-> normal3f)
uint32_t target_underlying_id = TypeTraits<T>::underlying_type_id();
switch (target_underlying_id) {
case TYPE_ID_FLOAT: {
if (auto* vec = as<std::vector<float>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_DOUBLE: {
if (auto* vec = as<std::vector<double>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT3: {
// Try float3, vector3f, normal3f, color3f, point3f
if (auto* vec = as<std::vector<float3>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<vector3f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<normal3f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<color3f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<point3f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT2: {
// Try float2, texcoord2f
if (auto* vec = as<std::vector<float2>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<texcoord2f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_FLOAT4: {
// Try float4, color4f
if (auto* vec = as<std::vector<float4>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
if (auto* vec = as<std::vector<color4f>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_INT32: {
if (auto* vec = as<std::vector<int32_t>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
case TYPE_ID_UINT32: {
if (auto* vec = as<std::vector<uint32_t>>(false)) {
return TypedArrayView<T>(reinterpret_cast<T*>(vec->data()), vec->size());
}
break;
}
default:
break;
}
}
// No compatible type found
return TypedArrayView<T>();
}
};
// TimeSample interpolation type.