//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _GENERICCONSTBUFFER_H_ #define _GENERICCONSTBUFFER_H_ #ifndef _TORQUE_STRING_H_ #include "core/util/str.h" #endif #ifndef _TDICTIONARY_H_ #include "core/util/tDictionary.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif #ifndef _ALIGNEDARRAY_H_ #include "core/util/tAlignedArray.h" #endif #ifndef _COLOR_H_ #include "core/color.h" #endif #ifndef _MMATRIX_H_ #include "math/mMatrix.h" #endif #ifndef _MPOINT2_H_ #include "math/mPoint2.h" #endif #ifndef _GFXENUMS_H_ #include "gfx/gfxEnums.h" #endif class Stream; /// This class defines the memory layout for a GenericConstBuffer. class GenericConstBufferLayout { public: /// Describes the parameters we contain struct ParamDesc { ParamDesc() : name(), offset( 0 ), size( 0 ), constType( GFXSCT_Float ), arraySize( 0 ), alignValue( 0 ), index( 0 ) { } void clear() { name = String::EmptyString; offset = 0; size = 0; constType = GFXSCT_Float; arraySize = 0; alignValue = 0; index = 0; } /// Parameter name String name; /// Offset into the memory block U32 offset; /// Size of the block U32 size; /// Type of data GFXShaderConstType constType; // For arrays, how many elements U32 arraySize; // Array element alignment value U32 alignValue; /// 0 based index of this param, in order of addParameter calls. U32 index; }; GenericConstBufferLayout(); virtual ~GenericConstBufferLayout() {} /// Add a parameter to the buffer virtual void addParameter(const String& name, const GFXShaderConstType constType, const U32 offset, const U32 size, const U32 arraySize, const U32 alignValue); /// Get the size of the buffer inline U32 getBufferSize() const { return mBufferSize; } /// Get the number of parameters inline U32 getParameterCount() const { return mParams.size(); } /// Returns the ParamDesc of a parameter bool getDesc(const String& name, ParamDesc& param) const; /// Returns the ParamDesc of a parameter bool getDesc(const U32 index, ParamDesc& param) const; /// Set a parameter, given a base pointer virtual bool set(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); /// Save this layout to a stream bool write(Stream* s); /// Load this layout from a stream bool read(Stream* s); /// Restore to initial state. void clear(); protected: /// Set a matrix, given a base pointer. virtual bool setMatrix(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); /// Vector of parameter descriptions. typedef Vector Params; /// Vector of parameter descriptions. Params mParams; U32 mBufferSize; U32 mCurrentIndex; // This if for debugging shader reloading and can be removed later. U32 mTimesCleared; }; /// This class manages shader constant data in a system memory buffer. It is /// used by device specific classes for batching together many constant changes /// which are then copied to the device thru a single API call. /// /// @see GenericConstBufferLayout /// class GenericConstBuffer { public: GenericConstBuffer(GenericConstBufferLayout* layout); ~GenericConstBuffer(); /// @name Set shader constant values /// @{ /// Actually set shader constant values /// @param name Name of the constant, this should be a name contained in the array returned in getShaderConstDesc, /// if an invalid name is used, its ignored, but it's not an error. inline void set(const GenericConstBufferLayout::ParamDesc& pd, const F32 f) { internalSet(pd, GFXSCT_Float, sizeof(F32), &f); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point2F& fv) { internalSet(pd, GFXSCT_Float2, sizeof(Point2F), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point3F& fv) { internalSet(pd, GFXSCT_Float3, sizeof(Point3F), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point4F& fv) { internalSet(pd, GFXSCT_Float4, sizeof(Point4F), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const PlaneF& fv) { internalSet(pd, GFXSCT_Float4, sizeof(PlaneF), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const LinearColorF& fv) { internalSet(pd, GFXSCT_Float4, sizeof(Point4F), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const S32 f) { internalSet(pd, GFXSCT_Int, sizeof(S32), &f); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point2I& fv) { internalSet(pd, GFXSCT_Int2, sizeof(Point2I), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point3I& fv) { internalSet(pd, GFXSCT_Int3, sizeof(Point3I), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point4I& fv) { internalSet(pd, GFXSCT_Int4, sizeof(Point4I), &fv); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float2, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float3, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float4, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int2, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int3, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int4, fv.getElementSize() * fv.size(), fv.getBuffer()); } inline void set( const GenericConstBufferLayout::ParamDesc& pd, const MatrixF& mat, const GFXShaderConstType matrixType ) { AssertFatal( matrixType == GFXSCT_Float2x2 || matrixType == GFXSCT_Float3x3 || matrixType == GFXSCT_Float3x4 || matrixType == GFXSCT_Float4x3 || matrixType == GFXSCT_Float4x4, "GenericConstBuffer::set() - Invalid matrix type!" ); internalSet( pd, matrixType, sizeof(MatrixF), &mat ); } inline void set( const GenericConstBufferLayout::ParamDesc& pd, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType ) { AssertFatal( matrixType == GFXSCT_Float2x2 || matrixType == GFXSCT_Float3x3 || matrixType == GFXSCT_Float3x4 || matrixType == GFXSCT_Float4x3 || matrixType == GFXSCT_Float4x4, "GenericConstBuffer::set() - Invalid matrix type!" ); internalSet( pd, matrixType, sizeof(MatrixF)*arraySize, mat ); } /// Gets the dirty buffer range and clears the dirty /// state at the same time. inline const U8* getDirtyBuffer( U32 *start, U32 *size ); /// Gets the entire buffer ignoring dirty range inline const U8* getEntireBuffer(); /// Sets the entire buffer as dirty or clears the dirty state. inline void setDirty( bool dirty ); /// Returns true if the buffer has been modified since the /// last call to getDirtyBuffer or setDirty. The buffer is /// not dirty on initial creation. /// /// @see getDirtyBuffer /// @see setDirty inline bool isDirty() const { return mDirtyEnd != 0; } /// Returns true if have the same layout and hold the same /// data as the input buffer. inline bool isEqual( const GenericConstBuffer *buffer ) const; /// Returns our layout object. inline GenericConstBufferLayout* getLayout() const { return mLayout; } #ifdef TORQUE_DEBUG /// Helper function used to assert on unset constants. void assertUnassignedConstants( const char *shaderName ); #endif protected: /// Returns a pointer to the raw buffer inline const U8* getBuffer() const { return mBuffer; } /// Called by the inlined set functions above to do the /// real dirty work of copying the data to the right location /// within the buffer. inline void internalSet( const GenericConstBufferLayout::ParamDesc &pd, const GFXShaderConstType constType, const U32 size, const void *data ); /// The buffer layout. GenericConstBufferLayout *mLayout; /// The pointer to the contant store or /// NULL if the layout is empty. U8 *mBuffer; /// The byte offset to the start of the dirty /// range within the buffer or U32_MAX if the /// buffer is not dirty. U32 mDirtyStart; /// The btye offset to the end of the dirty /// range within the buffer or 0 if the buffer /// is not dirty. U32 mDirtyEnd; #ifdef TORQUE_DEBUG /// A vector used to keep track if a constant /// has beed assigned a value or not. /// /// @see assertUnassignedConstants Vector mWasAssigned; #endif }; // NOTE: These inlines below are here to get the very best possible // performance when setting the device shader constants and can be // called 4000-8000 times per frame or more. // // You need a very good reason to consider changing them. inline void GenericConstBuffer::internalSet( const GenericConstBufferLayout::ParamDesc &pd, const GFXShaderConstType constType, const U32 size, const void *data ) { // NOTE: We should have never gotten here if the buffer // was null as no valid shader constant could have been // assigned. // // If this happens its a bug in another part of the code. // AssertFatal( mBuffer, "GenericConstBuffer::internalSet - The buffer is NULL!" ); if ( mLayout->set( pd, constType, size, data, mBuffer ) ) { #ifdef TORQUE_DEBUG // Update the debug assignment tracking. mWasAssigned[ pd.index ] = true; #endif // Keep track of the dirty range so it can be queried // later in GenericConstBuffer::getDirtyBuffer. mDirtyStart = getMin( pd.offset, mDirtyStart ); mDirtyEnd = getMax( pd.offset + pd.size, mDirtyEnd ); } } inline void GenericConstBuffer::setDirty( bool dirty ) { if ( !mBuffer ) return; if ( dirty ) { mDirtyStart = 0; mDirtyEnd = mLayout->getBufferSize(); } else if ( !dirty ) { mDirtyStart = U32_MAX; mDirtyEnd = 0; } } inline const U8* GenericConstBuffer::getDirtyBuffer( U32 *start, U32 *size ) { AssertFatal( isDirty(), "GenericConstBuffer::getDirtyBuffer() - Buffer is not dirty!" ); AssertFatal( mDirtyEnd > mDirtyStart, "GenericConstBuffer::getDirtyBuffer() - Dirty range is invalid!" ); AssertFatal( mBuffer, "GenericConstBuffer::getDirtyBuffer() - Buffer is empty!" ); // Use the area we calculated during internalSet. *size = mDirtyEnd - mDirtyStart; *start = mDirtyStart; const U8 *buffer = mBuffer + mDirtyStart; // Clear the dirty state while we're here. mDirtyStart = U32_MAX; mDirtyEnd = 0; return buffer; } inline const U8* GenericConstBuffer::getEntireBuffer() { AssertFatal(mBuffer, "GenericConstBuffer::getDirtyBuffer() - Buffer is empty!"); return mBuffer; } inline bool GenericConstBuffer::isEqual( const GenericConstBuffer *buffer ) const { U32 bsize = mLayout->getBufferSize(); if ( bsize != buffer->mLayout->getBufferSize() ) return false; return dMemcmp( mBuffer, buffer->getBuffer(), bsize ) == 0; } #endif // _GENERICCONSTBUFFER_H_