Skip to content

Commit

Permalink
Fix loading of 3D large textures with baked mipmaps
Browse files Browse the repository at this point in the history
3D textures with a depth = 128 and 7 mipmaps would exceed the maximum
allowed of 256 slices (128x7 = 896)

Increasing the hardcoded limit in QueuedImage would make each instance
too big, therefore changed the code to use bitset64 which handles the
limits dynamically via an additional indirection and allocation

Additionally, further enhanced cbitset64
To avoid hurting build times, OgreBitset.h now requires including
OgreBitset.inl in the cpp file, instead of the header
  • Loading branch information
darksylinc committed May 6, 2021
1 parent 86ffd8a commit a481059
Show file tree
Hide file tree
Showing 5 changed files with 501 additions and 130 deletions.
219 changes: 158 additions & 61 deletions OgreMain/include/OgreBitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,63 @@ namespace Ogre
{
/** @ingroup Core
@class cbitsetN
@remarks
IMPORTANT:
To use this class, you MUST include OgreBitset.inl in your cpp file
This split into *.h and *.inl was to avoid bloating build times
of headers that include OgreBitset.h
@param _N
Number of bits this bitset will hold
@param _bits
The exponent of the number of bits to hold per mValues.
E.g. 32 bits per mValues needs _bits = 5, because 2^5 = 32
Note we never tested other combinations where 2^_bits * 8 != sizeof( _internalDataType )
@param _mask
The maximum number of bits - 1 to avoid overflow
e.g. 32 bits per mValues needs a mask of 31 (0x1F) to wrap around
so when we call
@code
this->set( 32 );
@endcode
It actually performs:
@code
idx = 32 / 32; // 32 >> 5
mask = 32 % 32; // 32 & 0x1F
this->mValues[idx] |= mask;
@endcode
*/
template <size_t _N, typename _internalDataType, size_t _bits, size_t _mask>
class cbitsetN
{
protected:
_internalDataType mValues[( _N + ( (size_t)1u << _bits ) - 1u ) >> _bits];

inline size_t getInternalArraySize() const { return sizeof( mValues ) / sizeof( mValues[0] ); }

public:
cbitsetN() { clear(); }

/// Sets all bits to 0
void clear() { memset( mValues, 0, sizeof( mValues ) ); }

/// Returns true if all bits are unset
bool empty() const;

/// Sets all bits to 1
void setAll() { memset( mValues, 0xFF, sizeof( mValues ) ); }
void setAll();

/** Sets all bits in range [0; position)
It's the same as calling:
for( size_t i = 0u; i < position; ++i )
this->set( i );
Values in range [position; _N) are left untouched
@param position
*/
void setAllUntil( size_t position );

/// Return maximum number of bits this bitset can hold
size_t capacity() const { return _N; }
Expand All @@ -58,92 +101,146 @@ namespace Ogre
Value in range [0; _N)
@param bValue
*/
void setValue( size_t position, bool bValue )
{
OGRE_ASSERT_MEDIUM( position < _N );
const size_t idx = position >> _bits;
const _internalDataType mask = (_internalDataType)1u << ( position & _mask );
if( bValue )
mValues[idx] |= mask;
else
mValues[idx] &= ~mask;
}
void setValue( const size_t position, const bool bValue );

/** Sets bit at 'position' to 1
@param position
Value in range [0; _N)
*/
void set( size_t position )
{
OGRE_ASSERT_MEDIUM( position < _N );
const size_t idx = position >> _bits;
const _internalDataType mask = (_internalDataType)1u << ( position & _mask );
mValues[idx] |= mask;
}
void set( const size_t position );

/** Sets bit at 'position' to 0
@param position
Value in range [0; _N)
*/
void unset( size_t position )
{
OGRE_ASSERT_MEDIUM( position < _N );
const size_t idx = position >> _bits;
const _internalDataType mask = (_internalDataType)1u << ( position & _mask );
mValues[idx] &= ~mask;
}
void unset( const size_t position );

/** Returns true if bit at 'position' is 1
@param position
Value in range [0; _N)
*/
bool test( size_t position ) const
{
OGRE_ASSERT_MEDIUM( position < _N );
const size_t idx = position >> _bits;
const _internalDataType mask = (_internalDataType)1u << ( position & _mask );
return ( mValues[idx] & mask ) != 0u;
}
bool test( const size_t position ) const;

/// Returns the number of bits that are set between range [0; positionEnd).
size_t numBitsSet( size_t positionEnd ) const
{
OGRE_ASSERT_MEDIUM( positionEnd < _N );
size_t retVal = 0u;
for( size_t i = 0u; i < positionEnd; )
{
if( ( positionEnd - i ) >= _mask )
{
const _internalDataType value = mValues[i >> _bits];
if( value == (_internalDataType)-1 )
{
retVal += _mask + 1u;
}
else if( value != 0u )
{
for( size_t j = 0u; j <= _mask; ++j )
retVal += ( value & ( (_internalDataType)1u << j ) ) != 0u ? 1u : 0u;
}

i += _mask + 1u;
}
else
{
retVal += test( i ) ? 1u : 0u;
++i;
}
}
return retVal;
}
size_t numBitsSet( const size_t positionEnd ) const;
};

/** @ingroup Core
@class cbitset32
This is similar to std::bitset, except waaay less bloat.
cbitset32 stands for constant/compile-time bitset with an internal representation of 32-bits
2^5 = 32
0x1F = 32 - 1 = 5 bits set
@remarks
IMPORTANT:
To use this class, you MUST include OgreBitset.inl in your cpp file
This split into *.h and *.inl was to avoid bloating build times
of headers that include OgreBitset.h
@param _N
Number of bits this bitset will hold
*/
template <size_t _N>
class cbitset32 : public cbitsetN<_N, uint32, 5u, 0x1Fu>
{
};

/** @ingroup Core
@class cbitset64
This is similar to std::bitset, except waaay less bloat.
cbitset64 stands for constant/compile-time bitset with an internal representation of 64-bits
2^6 = 64
0x3F = 64 - 1 = 6 bits set
@remarks
IMPORTANT:
To use this class, you MUST include OgreBitset.inl in your cpp file
This split into *.h and *.inl was to avoid bloating build times
of headers that include OgreBitset.h
@param _N
Number of bits this bitset will hold
*/
template <size_t _N>
class cbitset64 : public cbitsetN<_N, uint64, 6u, 0x3Fu>
{
public:
/** Finds the first bit set.
@return
The index to the first unset bit
returns this->capacity() if all bits are unset (i.e. all 0s)
*/
size_t findFirstBitSet() const;

/** Finds the first bit unset after the last bit set.
@return
If all bits are unset (i.e. all 0s):
Returns 0
If at least one bit is set:
The index to the last set bit, plus one.
Return range is [1; capacity]
*/
size_t findLastBitSetPlusOne() const;
};

/** @ingroup Core
@class bitset64
See cbitset64
This is the same, but the maximum number of bits is not known
at build time
*/
class bitset64
{
protected:
FastArray<uint64> mValues;
size_t mBitsCapacity;

public:
bitset64() : mBitsCapacity( 0u ) {}

void reset( size_t bitsCapacity )
{
mBitsCapacity = bitsCapacity;
mValues.clear();
mValues.resizePOD( ( bitsCapacity + 63u ) >> 6u, 0 );
}

/// @copydoc cbitset64::clear
void clear() { memset( mValues.begin(), 0, mValues.size() * sizeof( uint64 ) ); }

/// @copydoc cbitset64::empty
inline bool empty() const;

/// @copydoc cbitset64::setAll
inline void setAll();

/// @copydoc cbitset64::setAllUntil
inline void setAllUntil( size_t position );

/// @copydoc cbitset64::capacity
inline size_t capacity() const { return mBitsCapacity; }

/// @copydoc cbitset64::setValue
inline void setValue( const size_t position, const bool bValue );

/// @copydoc cbitset64::set
inline void set( const size_t position );

/// @copydoc cbitset64::unset
inline void unset( const size_t position );

/// @copydoc cbitset64::test
inline bool test( const size_t position ) const;

/// @copydoc cbitset64::numBitsSet
inline size_t numBitsSet( const size_t positionEnd ) const;

/// @copydoc cbitset64::findFirstBitSet
inline size_t findFirstBitSet() const;

/// @copydoc cbitset64::findLastBitSetPlusOne
inline size_t findLastBitSetPlusOne() const;
};
} // namespace Ogre

#endif

0 comments on commit a481059

Please sign in to comment.