1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file points/AttributeArray.cc
5 
6 #include "AttributeArray.h"
7 #include <map>
8 
9 namespace openvdb {
10 OPENVDB_USE_VERSION_NAMESPACE
11 namespace OPENVDB_VERSION_NAME {
12 namespace points {
13 
14 
15 ////////////////////////////////////////
16 
17 
18 namespace {
19 
20 using AttributeFactoryMap = std::map<NamePair, AttributeArray::FactoryMethod>;
21 
22 struct LockedAttributeRegistry
23 {
24     tbb::spin_mutex     mMutex;
25     AttributeFactoryMap mMap;
26 };
27 
28 // Global function for accessing the registry
29 LockedAttributeRegistry*
getAttributeRegistry()30 getAttributeRegistry()
31 {
32     static LockedAttributeRegistry registry;
33     return &registry;
34 }
35 
36 } // unnamed namespace
37 
38 
39 ////////////////////////////////////////
40 
41 // AttributeArray::ScopedRegistryLock implementation
42 
ScopedRegistryLock()43 AttributeArray::ScopedRegistryLock::ScopedRegistryLock()
44     : lock(getAttributeRegistry()->mMutex)
45 {
46 }
47 
48 
49 ////////////////////////////////////////
50 
51 // AttributeArray implementation
52 
53 
54 #if OPENVDB_ABI_VERSION_NUMBER >= 7
AttributeArray(const AttributeArray & rhs)55 AttributeArray::AttributeArray(const AttributeArray& rhs)
56     : AttributeArray(rhs, tbb::spin_mutex::scoped_lock(rhs.mMutex))
57 {
58 }
59 
60 
AttributeArray(const AttributeArray & rhs,const tbb::spin_mutex::scoped_lock &)61 AttributeArray::AttributeArray(const AttributeArray& rhs, const tbb::spin_mutex::scoped_lock&)
62 #else
63 AttributeArray::AttributeArray(const AttributeArray& rhs)
64 #endif
65     : mIsUniform(rhs.mIsUniform)
66     , mFlags(rhs.mFlags)
67     , mUsePagedRead(rhs.mUsePagedRead)
68     , mOutOfCore(rhs.mOutOfCore.load())
69     , mPageHandle()
70 {
71     if (mFlags & PARTIALREAD)       mCompressedBytes = rhs.mCompressedBytes;
72     else if (rhs.mPageHandle)       mPageHandle = rhs.mPageHandle->copy();
73 }
74 
75 
76 AttributeArray&
operator =(const AttributeArray & rhs)77 AttributeArray::operator=(const AttributeArray& rhs)
78 {
79     // if this AttributeArray has been partially read, zero the compressed bytes,
80     // so the page handle won't attempt to clean up invalid memory
81     if (mFlags & PARTIALREAD)       mCompressedBytes = 0;
82     mIsUniform = rhs.mIsUniform;
83     mFlags = rhs.mFlags;
84     mUsePagedRead = rhs.mUsePagedRead;
85     mOutOfCore.store(rhs.mOutOfCore);
86     if (mFlags & PARTIALREAD)       mCompressedBytes = rhs.mCompressedBytes;
87     else if (rhs.mPageHandle)       mPageHandle = rhs.mPageHandle->copy();
88     else                            mPageHandle.reset();
89     return *this;
90 }
91 
92 
93 AttributeArray::Ptr
create(const NamePair & type,Index length,Index stride,bool constantStride,const Metadata * metadata,const ScopedRegistryLock * lock)94 AttributeArray::create(const NamePair& type, Index length, Index stride,
95     bool constantStride, const Metadata* metadata, const ScopedRegistryLock* lock)
96 {
97     auto* registry = getAttributeRegistry();
98     tbb::spin_mutex::scoped_lock _lock;
99     if (!lock)  _lock.acquire(registry->mMutex);
100 
101     auto iter = registry->mMap.find(type);
102     if (iter == registry->mMap.end()) {
103         OPENVDB_THROW(LookupError,
104             "Cannot create attribute of unregistered type " << type.first << "_" << type.second);
105     }
106     return (iter->second)(length, stride, constantStride, metadata);
107 }
108 
109 
110 bool
isRegistered(const NamePair & type,const ScopedRegistryLock * lock)111 AttributeArray::isRegistered(const NamePair& type, const ScopedRegistryLock* lock)
112 {
113     LockedAttributeRegistry* registry = getAttributeRegistry();
114     tbb::spin_mutex::scoped_lock _lock;
115     if (!lock)  _lock.acquire(registry->mMutex);
116     return (registry->mMap.find(type) != registry->mMap.end());
117 }
118 
119 
120 void
clearRegistry(const ScopedRegistryLock * lock)121 AttributeArray::clearRegistry(const ScopedRegistryLock* lock)
122 {
123     LockedAttributeRegistry* registry = getAttributeRegistry();
124     tbb::spin_mutex::scoped_lock _lock;
125     if (!lock)  _lock.acquire(registry->mMutex);
126     registry->mMap.clear();
127 }
128 
129 
130 void
registerType(const NamePair & type,FactoryMethod factory,const ScopedRegistryLock * lock)131 AttributeArray::registerType(const NamePair& type, FactoryMethod factory, const ScopedRegistryLock* lock)
132 {
133     { // check the type of the AttributeArray generated by the factory method
134         auto array = (*factory)(/*length=*/0, /*stride=*/0, /*constantStride=*/false, /*metadata=*/nullptr);
135         const NamePair& factoryType = array->type();
136         if (factoryType != type) {
137             OPENVDB_THROW(KeyError, "Attribute type " << type.first << "_" << type.second
138                 << " does not match the type created by the factory method "
139                 << factoryType.first << "_" << factoryType.second << ".");
140         }
141     }
142 
143     LockedAttributeRegistry* registry = getAttributeRegistry();
144     tbb::spin_mutex::scoped_lock _lock;
145     if (!lock)  _lock.acquire(registry->mMutex);
146 
147     registry->mMap[type] = factory;
148 }
149 
150 
151 void
unregisterType(const NamePair & type,const ScopedRegistryLock * lock)152 AttributeArray::unregisterType(const NamePair& type, const ScopedRegistryLock* lock)
153 {
154     LockedAttributeRegistry* registry = getAttributeRegistry();
155     tbb::spin_mutex::scoped_lock _lock;
156     if (!lock)  _lock.acquire(registry->mMutex);
157 
158     registry->mMap.erase(type);
159 }
160 
161 
162 void
setTransient(bool state)163 AttributeArray::setTransient(bool state)
164 {
165     if (state) mFlags = static_cast<uint8_t>(mFlags | Int16(TRANSIENT));
166     else       mFlags = static_cast<uint8_t>(mFlags & ~Int16(TRANSIENT));
167 }
168 
169 
170 void
setHidden(bool state)171 AttributeArray::setHidden(bool state)
172 {
173     if (state) mFlags = static_cast<uint8_t>(mFlags | Int16(HIDDEN));
174     else       mFlags = static_cast<uint8_t>(mFlags & ~Int16(HIDDEN));
175 }
176 
177 
178 void
setStreaming(bool state)179 AttributeArray::setStreaming(bool state)
180 {
181     if (state) mFlags = static_cast<uint8_t>(mFlags | Int16(STREAMING));
182     else       mFlags = static_cast<uint8_t>(mFlags & ~Int16(STREAMING));
183 }
184 
185 
186 void
setConstantStride(bool state)187 AttributeArray::setConstantStride(bool state)
188 {
189     if (state) mFlags = static_cast<uint8_t>(mFlags | Int16(CONSTANTSTRIDE));
190     else       mFlags = static_cast<uint8_t>(mFlags & ~Int16(CONSTANTSTRIDE));
191 }
192 
193 
194 bool
operator ==(const AttributeArray & other) const195 AttributeArray::operator==(const AttributeArray& other) const
196 {
197     this->loadData();
198     other.loadData();
199 
200     if (this->mUsePagedRead != other.mUsePagedRead ||
201         this->mFlags != other.mFlags) return false;
202     return this->isEqual(other);
203 }
204 
205 } // namespace points
206 } // namespace OPENVDB_VERSION_NAME
207 } // namespace openvdb
208