mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
typedarrayview.
This commit is contained in:
@@ -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
249
src/nonstd/span.hpp
Normal 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
|
||||
@@ -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; }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
491
src/tydra/typed-array-render-data.hh
Normal file
491
src/tydra/typed-array-render-data.hh
Normal 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
49
src/typed-array.cc
Normal 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
795
src/typed-array.hh
Normal 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
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user