yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie Hermaszewskaformatf65d756bf

master
9.6 KiB337 linesraw
1#ifndef SLANG_CORE_UINT_SET_H
2#define SLANG_CORE_UINT_SET_H
3
4#include "slang-common.h"
5#include "slang-hash.h"
6#include "slang-list.h"
7#include "slang-math.h"
8
9#if defined(_MSC_VER)
10#include <intrin.h>
11#endif
12#include <memory.h>
13
14namespace Slang
15{
16
17constexpr Index intLog2(unsigned x)
18{
19    return x == 1 ? 0 : 1 + intLog2(x >> 1);
20}
21
22// if `in` is 0, result is undefined behavior
23static inline Index bitscanForward(uint64_t in)
24{
25    SLANG_ASSERT(in != 0);
26#if defined(_MSC_VER)
27
28#ifdef _WIN64
29    uint64_t out = 0;
30    _BitScanForward64((unsigned long*)&out, in);
31    return Index(out);
32#else
33    uint32_t out;
34    // check for 0s in 0bit->31bit. If all 0's, check for 0s in 32bit->63bit
35    if (_BitScanForward((unsigned long*)&out, *(((uint32_t*)&in))))
36        return Index(out);
37    _BitScanForward((unsigned long*)&out, *(((uint32_t*)&in) + 1));
38    return Index(out) + 32;
39#endif // #ifdef _WIN64
40
41#else
42    return Index(__builtin_ctzll(in));
43#endif // #if defined(_MSC_VER)
44}
45
46/* Hold a set of UInt values. Implementation works by storing as a bit per value */
47/// UIntSet is essentially a Element[], where each Element is `b` bits big.
48/// Each index has `b` number of integers. If the bit is 1, we have an element there.
49/// Value of each element is equal to the binary offset from Element[0], bit 0.
50class UIntSet
51{
52public:
53    typedef UIntSet ThisType;
54    typedef uint64_t Element; ///< Type that holds the bits to say if value is present
55
56    constexpr static Index kElementSize =
57        sizeof(Element) * 8; ///< The number of bits in an element. This also determines how many
58                             ///< values a element can hold.
59    constexpr static Index kElementMask = kElementSize - 1; ///< Mask to get shift from an index
60    constexpr static Index kElementShift = intLog2(
61        sizeof(Element) * 8); ///< How many bits to shift to get Element index from an index. 5 for
62                              ///< 2^5=32 elements in a uint32_t. 6 for 2^6=64 in a uint64_t.
63
64    UIntSet() {}
65    UIntSet(const UIntSet& other) { m_buffer = other.m_buffer; }
66    UIntSet(UIntSet&& other) { *this = (_Move(other)); }
67    UIntSet(UInt maxVal) { resizeAndClear(maxVal); }
68
69    UIntSet& operator=(UIntSet&& other);
70    UIntSet& operator=(const UIntSet& other);
71
72    HashCode getHashCode() const;
73
74    /// Return the count of all bits directly represented
75    Int getCount() const { return Int(m_buffer.getCount()) * kElementSize; }
76
77    const List<Element>& getBuffer() const { return m_buffer; }
78
79    /// Resize such that val can be stored and clear contents
80    void resizeAndClear(UInt val);
81    /// Set all of the values up to count, as set
82    void setAll();
83    /// Resize (but maintain contents) up to bit size.
84    /// NOTE! That since storage is in Element blocks, it may mean some values after size are set
85    /// (up to the Element boundary)
86    void resize(UInt size);
87    void resizeBackingBufferDirectly(Index size);
88
89    /// Clear all of the contents (by clearing the bits)
90    void clear();
91
92    /// Clear all the contents and free memory
93    void clearAndDeallocate();
94
95    /// Add a value
96    inline void add(UInt val);
97    inline void add(const UIntSet& val);
98    inline void addRange(const List<UInt>& other);
99
100    inline void addRawElement(Element val, Index bitOffset);
101
102    /// Remove a value
103    inline void remove(UInt val);
104    /// Returns true if the value is present
105    inline bool contains(UInt val) const;
106
107    inline bool contains(const UIntSet& set) const;
108
109    /// ==
110    bool operator==(const UIntSet& set) const;
111    /// !=
112    bool operator!=(const UIntSet& set) const { return !(*this == set); }
113
114    /// Store the union between this and set
115    void unionWith(const UIntSet& set);
116    /// Store the intersection between this and set
117    void intersectWith(const UIntSet& set);
118    /// Store the subtraction between this and set
119    void subtractWith(const UIntSet& set);
120
121    ///
122    bool isEmpty() const;
123
124    /// Swap this with rhs
125    void swapWith(ThisType& rhs) { m_buffer.swapWith(rhs.m_buffer); }
126
127    template<typename T>
128    List<T> getElements() const;
129    Index countElements() const;
130
131    /// Store the union of set1 and set2 in outRs
132    static void calcUnion(UIntSet& outRs, const UIntSet& set1, const UIntSet& set2);
133    /// Store the intersection of set1 and set2 in outRs
134    static void calcIntersection(UIntSet& outRs, const UIntSet& set1, const UIntSet& set2);
135    /// Store the subtraction of set2 from set1 in outRs
136    static void calcSubtract(UIntSet& outRs, const UIntSet& set1, const UIntSet& set2);
137
138    /// Returns true if set1 and set2 have a same value set (ie there is an intersection)
139    static bool hasIntersection(const UIntSet& set1, const UIntSet& set2);
140
141    /// Get LSB Zero of UIntSet. LSB Zero is the smallest value missing from this UIntSet.
142    Index getLSBZero();
143
144    struct Iterator
145    {
146        friend class UIntSet;
147
148    private:
149        const List<Element>* m_context;
150        Index m_block = 0;
151        Element m_processedElement = 0;
152        uint64_t m_LSB = 0;
153
154        void clearLSB()
155        {
156            m_LSB = bitscanForward(m_processedElement);
157            m_processedElement &= m_processedElement - 1;
158        }
159
160        Iterator(const List<Element>* context) { m_context = context; }
161
162    public:
163        Element operator*() { return Element(m_LSB + (kElementSize * m_block)); }
164
165        Iterator& operator++()
166        {
167            while (m_processedElement == 0)
168            {
169                m_block++;
170                if (m_block >= m_context->getCount())
171                {
172                    return *this;
173                }
174                m_processedElement = (*m_context)[m_block];
175            }
176            clearLSB();
177            return *this;
178        }
179        Iterator& operator++(int) { return ++(*this); }
180        bool operator==(const Iterator& other) const
181        {
182            return other.m_block == this->m_block &&
183                   other.m_processedElement == this->m_processedElement;
184        }
185        bool operator!=(const Iterator& other) const { return !(other == *this); }
186    };
187    Iterator begin() const
188    {
189        Iterator tmp(&m_buffer);
190        if (m_buffer.getCount() == 0)
191            return tmp;
192
193        tmp.m_processedElement = m_buffer[0];
194        if (tmp.m_processedElement == 0)
195        {
196            tmp++;
197            return tmp;
198        }
199
200        tmp.clearLSB();
201        return tmp;
202    }
203    Iterator end() const
204    {
205        Iterator tmp(&m_buffer);
206        tmp.m_block = m_buffer.getCount();
207        tmp.m_processedElement = 0;
208        return tmp;
209    }
210
211    bool areAllZero() { return _areAllZero(m_buffer.getBuffer(), m_buffer.getCount()); }
212
213protected:
214    static bool _areAllZero(const UIntSet::Element* elems, Index count)
215    {
216        for (Index i = 0; i < count; ++i)
217        {
218            if (elems[i])
219            {
220                return false;
221            }
222        }
223        return true;
224    }
225
226    List<Element> m_buffer;
227};
228
229// --------------------------------------------------------------------------
230inline void UIntSet::remove(UInt val)
231{
232    const Index idx = Index(val >> kElementShift);
233    if (idx < m_buffer.getCount())
234    {
235        m_buffer[idx] &= ~(Element(1) << (val & kElementMask));
236    }
237}
238
239// --------------------------------------------------------------------------
240inline bool UIntSet::contains(UInt val) const
241{
242    const Index idx = Index(val >> kElementShift);
243    return idx < m_buffer.getCount() &&
244           ((m_buffer[idx] & (Element(1) << (val & kElementMask))) != 0);
245}
246
247// --------------------------------------------------------------------------
248inline bool UIntSet::contains(const UIntSet& set) const
249{
250    for (Index i = 0; i < set.m_buffer.getCount(); i++)
251    {
252        if (i >= m_buffer.getCount())
253        {
254            if (set.m_buffer[i])
255                return false;
256        }
257        else
258        {
259            if ((m_buffer[i] & set.m_buffer[i]) != set.m_buffer[i])
260                return false;
261        }
262    }
263    return true;
264}
265
266// --------------------------------------------------------------------------
267
268inline void UIntSet::resizeBackingBufferDirectly(Index newCount)
269{
270    const Index oldCount = m_buffer.getCount();
271    m_buffer.setCount(newCount);
272
273    if (newCount > oldCount)
274    {
275        ::memset(m_buffer.getBuffer() + oldCount, 0, (newCount - oldCount) * sizeof(Element));
276    }
277}
278
279inline void UIntSet::add(UInt val)
280{
281    const Index idx = Index(val >> kElementShift);
282    if (idx >= m_buffer.getCount())
283    {
284        resize(val + 1);
285    }
286    m_buffer[idx] |= Element(1) << (val & kElementMask);
287}
288
289inline void UIntSet::add(const UIntSet& other)
290{
291    auto otherCount = other.m_buffer.getCount();
292    if (this->m_buffer.getCount() < otherCount)
293        resizeBackingBufferDirectly(otherCount);
294
295    for (auto i = 0; i < otherCount; i++)
296        m_buffer[i] |= other.m_buffer[i];
297}
298
299inline void UIntSet::addRange(const List<UInt>& other)
300{
301    for (auto i : other)
302        add(i);
303}
304
305inline void UIntSet::addRawElement(Element other, Index elementIndex)
306{
307    if (this->m_buffer.getCount() <= elementIndex)
308        resizeBackingBufferDirectly(elementIndex + 1);
309    m_buffer[elementIndex] |= other;
310}
311
312template<typename T>
313List<T> UIntSet::getElements() const
314{
315    auto count = m_buffer.getCount();
316    if (count == 0)
317        return {};
318
319    // Specific path for uint64_t. If using SIMD we should not use this path due to larger data
320    // types.
321
322    List<T> elements;
323    elements.reserve(count);
324    for (Index block = 0; block < count; block++)
325    {
326        Element n = m_buffer[block];
327        while (n != 0)
328        {
329            elements.add(T(bitscanForward((uint64_t)n) + (kElementSize * block)));
330            n &= n - 1;
331        }
332    }
333    return elements;
334}
335
336} // namespace Slang
337#endif