//----------------------------------------------------------------------------- // Copyright (c) 2015 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 _GFXD3D11SHADER_H_ #define _GFXD3D11SHADER_H_ #include #include "core/util/path.h" #include "core/util/tDictionary.h" #include "gfx/gfxShader.h" #include "gfx/gfxResource.h" #include "gfx/genericConstBuffer.h" #include "gfx/D3D11/gfxD3D11Device.h" class GFXD3D11Shader; enum CONST_CLASS { D3DPC_SCALAR, D3DPC_VECTOR, D3DPC_MATRIX_ROWS, D3DPC_MATRIX_COLUMNS, D3DPC_OBJECT, D3DPC_STRUCT }; enum CONST_TYPE { D3DPT_VOID, D3DPT_BOOL, D3DPT_INT, D3DPT_FLOAT, D3DPT_STRING, D3DPT_TEXTURE, D3DPT_TEXTURE1D, D3DPT_TEXTURE2D, D3DPT_TEXTURE3D, D3DPT_TEXTURECUBE, D3DPT_SAMPLER, D3DPT_SAMPLER1D, D3DPT_SAMPLER2D, D3DPT_SAMPLER3D, D3DPT_SAMPLERCUBE, D3DPT_PIXELSHADER, D3DPT_VERTEXSHADER, D3DPT_PIXELFRAGMENT, D3DPT_VERTEXFRAGMENT }; enum REGISTER_TYPE { D3DRS_BOOL, D3DRS_INT4, D3DRS_FLOAT4, D3DRS_SAMPLER }; struct ConstantDesc { String Name = String::EmptyString; S32 RegisterIndex = 0; S32 RegisterCount = 0; S32 Rows = 0; S32 Columns = 0; S32 Elements = 0; S32 StructMembers = 0; REGISTER_TYPE RegisterSet = D3DRS_FLOAT4; CONST_CLASS Class = D3DPC_SCALAR; CONST_TYPE Type = D3DPT_FLOAT; U32 Bytes = 0; }; class ConstantTable { public: bool Create(const void* data); U32 GetConstantCount() const { return m_constants.size(); } const String& GetCreator() const { return m_creator; } const ConstantDesc* GetConstantByIndex(U32 i) const { return &m_constants[i]; } const ConstantDesc* GetConstantByName(const String& name) const; void ClearConstants() { m_constants.clear(); } private: Vector m_constants; String m_creator; }; // Structs struct CTHeader { U32 Size; U32 Creator; U32 Version; U32 Constants; U32 ConstantInfo; U32 Flags; U32 Target; }; struct CTInfo { U32 Name; U16 RegisterSet; U16 RegisterIndex; U16 RegisterCount; U16 Reserved; U32 TypeInfo; U32 DefaultValue; }; struct CTType { U16 Class; U16 Type; U16 Rows; U16 Columns; U16 Elements; U16 StructMembers; U32 StructMemberInfo; }; // Shader instruction opcodes const U32 SIO_COMMENT = 0x0000FFFE; const U32 SIO_END = 0x0000FFFF; const U32 SI_OPCODE_MASK = 0x0000FFFF; const U32 SI_COMMENTSIZE_MASK = 0x7FFF0000; const U32 CTAB_CONSTANT = 0x42415443; // Member functions inline bool ConstantTable::Create(const void* data) { const U32* ptr = static_cast(data); while(*++ptr != SIO_END) { if((*ptr & SI_OPCODE_MASK) == SIO_COMMENT) { // Check for CTAB comment U32 comment_size = (*ptr & SI_COMMENTSIZE_MASK) >> 16; if(*(ptr+1) != CTAB_CONSTANT) { ptr += comment_size; continue; } // Read header const char* ctab = reinterpret_cast(ptr+2); size_t ctab_size = (comment_size-1)*4; const CTHeader* header = reinterpret_cast(ctab); if(ctab_size < sizeof(*header) || header->Size != sizeof(*header)) return false; m_creator = ctab + header->Creator; // Read constants m_constants.reserve(header->Constants); const CTInfo* info = reinterpret_cast(ctab + header->ConstantInfo); for(U32 i = 0; i < header->Constants; ++i) { const CTType* type = reinterpret_cast(ctab + info[i].TypeInfo); // Fill struct ConstantDesc desc; desc.Name = ctab + info[i].Name; desc.RegisterSet = static_cast(info[i].RegisterSet); desc.RegisterIndex = info[i].RegisterIndex; desc.RegisterCount = info[i].RegisterCount; desc.Rows = type->Rows; desc.Class = static_cast(type->Class); desc.Type = static_cast(type->Type); desc.Columns = type->Columns; desc.Elements = type->Elements; desc.StructMembers = type->StructMembers; desc.Bytes = 4 * desc.Elements * desc.Rows * desc.Columns; m_constants.push_back(desc); } return true; } } return false; } inline const ConstantDesc* ConstantTable::GetConstantByName(const String& name) const { Vector::const_iterator it; for(it = m_constants.begin(); it != m_constants.end(); ++it) { if(it->Name == name) return &(*it); } return NULL; } /////////////////// Constant Buffers ///////////////////////////// // Maximum number of CBuffers ($Globals & $Params) const U32 CBUFFER_MAX = 2; struct ConstSubBufferDesc { U32 start; U32 size; ConstSubBufferDesc() : start(0), size(0){} }; class GFXD3D11ConstBufferLayout : public GenericConstBufferLayout { public: GFXD3D11ConstBufferLayout(); /// Get our constant sub buffer data Vector &getSubBufferDesc(){ return mSubBuffers; } /// We need to manually set the size due to D3D11 alignment void setSize(U32 size){ mBufferSize = size;} /// Set a parameter, given a base pointer virtual bool set(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); 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 mSubBuffers; }; class GFXD3D11ShaderConstHandle : public GFXShaderConstHandle { public: // GFXShaderConstHandle const String& getName() const; GFXShaderConstType getType() const; U32 getArraySize() const; WeakRefPtr mShader; bool mVertexConstant; GenericConstBufferLayout::ParamDesc mVertexHandle; bool mPixelConstant; GenericConstBufferLayout::ParamDesc mPixelHandle; /// Is true if this constant is for hardware mesh instancing. /// /// Note: We currently store its settings in mPixelHandle. /// bool mInstancingConstant; void setValid( bool valid ) { mValid = valid; } S32 getSamplerRegister() const; // Returns true if this is a handle to a sampler register. bool isSampler() const { return ( mPixelConstant && mPixelHandle.constType >= GFXSCT_Sampler ) || ( mVertexConstant && mVertexHandle.constType >= GFXSCT_Sampler ); } /// Restore to uninitialized state. void clear() { mShader = NULL; mVertexConstant = false; mPixelConstant = false; mInstancingConstant = false; mVertexHandle.clear(); mPixelHandle.clear(); mValid = false; } GFXD3D11ShaderConstHandle(); }; /// The D3D11 implementation of a shader constant buffer. class GFXD3D11ShaderConstBuffer : public GFXShaderConstBuffer { friend class GFXD3D11Shader; // Cache device context ID3D11DeviceContext* mDeviceContext; public: GFXD3D11ShaderConstBuffer(GFXD3D11Shader* shader, GFXD3D11ConstBufferLayout* vertexLayout, GFXD3D11ConstBufferLayout* pixelLayout); virtual ~GFXD3D11ShaderConstBuffer(); /// Called by GFXD3D11Device to activate this buffer. /// @param mPrevShaderBuffer The previously active buffer void activate(GFXD3D11ShaderConstBuffer *prevShaderBuffer); /// Used internally by GXD3D11ShaderConstBuffer to determine if it's dirty. bool isDirty(); /// Called from GFXD3D11Shader when constants have changed and need /// to be the shader this buffer references is reloaded. void onShaderReload(GFXD3D11Shader *shader); // GFXShaderConstBuffer virtual GFXShader* getShader(); virtual void set(GFXShaderConstHandle* handle, const F32 fv); virtual void set(GFXShaderConstHandle* handle, const Point2F& fv); virtual void set(GFXShaderConstHandle* handle, const Point3F& fv); virtual void set(GFXShaderConstHandle* handle, const Point4F& fv); virtual void set(GFXShaderConstHandle* handle, const PlaneF& fv); virtual void set(GFXShaderConstHandle* handle, const LinearColorF& fv); virtual void set(GFXShaderConstHandle* handle, const S32 f); virtual void set(GFXShaderConstHandle* handle, const Point2I& fv); virtual void set(GFXShaderConstHandle* handle, const Point3I& fv); virtual void set(GFXShaderConstHandle* handle, const Point4I& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); virtual void set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType = GFXSCT_Float4x4); virtual void set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4); // GFXResource virtual const String describeSelf() const; virtual void zombify(); virtual void resurrect(); protected: void _createBuffers(); template inline void SET_CONSTANT(GFXShaderConstHandle* handle, const T& fv, GenericConstBuffer *vBuffer, GenericConstBuffer *pBuffer); // Constant buffers, VSSetConstantBuffers1 has issues on win 7. So unfortunately for now we have multiple constant buffers ID3D11Buffer* mConstantBuffersV[CBUFFER_MAX]; ID3D11Buffer* mConstantBuffersP[CBUFFER_MAX]; /// We keep a weak reference to the shader /// because it will often be deleted. WeakRefPtr mShader; //vertex GFXD3D11ConstBufferLayout* mVertexConstBufferLayout; GenericConstBuffer* mVertexConstBuffer; //pixel GFXD3D11ConstBufferLayout* mPixelConstBufferLayout; GenericConstBuffer* mPixelConstBuffer; }; class gfxD3D11Include; typedef StrongRefPtr gfxD3DIncludeRef; /////////////////// GFXShader implementation ///////////////////////////// class GFXD3D11Shader : public GFXShader { friend class GFXD3D11Device; friend class GFXD3D11ShaderConstBuffer; public: typedef Map HandleMap; GFXD3D11Shader(); virtual ~GFXD3D11Shader(); // GFXShader virtual GFXShaderConstBufferRef allocConstBuffer(); virtual const Vector& getShaderConstDesc() const; virtual GFXShaderConstHandle* getShaderConstHandle(const String& name); virtual GFXShaderConstHandle* findShaderConstHandle(const String& name); virtual U32 getAlignmentValue(const GFXShaderConstType constType) const; virtual bool getDisassembly( String &outStr ) const; // GFXResource virtual void zombify(); virtual void resurrect(); protected: virtual bool _init(); static const U32 smCompiledShaderTag; ConstantTable table; ID3D11VertexShader *mVertShader; ID3D11PixelShader *mPixShader; GFXD3D11ConstBufferLayout* mVertexConstBufferLayout; GFXD3D11ConstBufferLayout* mPixelConstBufferLayout; static gfxD3DIncludeRef smD3DInclude; HandleMap mHandles; /// The shader disassembly from DX when this shader is compiled. /// We only store this data in non-release builds. String mDissasembly; /// Vector of sampler type descriptions consolidated from _compileShader. Vector mSamplerDescriptions; /// Vector of descriptions (consolidated for the getShaderConstDesc call) Vector mShaderConsts; // These two functions are used when compiling shaders from hlsl virtual bool _compileShader( const Torque::Path &filePath, const String &target, const D3D_SHADER_MACRO *defines, GenericConstBufferLayout *bufferLayout, Vector &samplerDescriptions ); void _getShaderConstants( ID3D11ShaderReflection* refTable, GenericConstBufferLayout *bufferLayout, Vector &samplerDescriptions ); bool _convertShaderVariable(const D3D11_SHADER_TYPE_DESC &typeDesc, GFXShaderConstDesc &desc); bool _saveCompiledOutput( const Torque::Path &filePath, ID3DBlob *buffer, GenericConstBufferLayout *bufferLayout, Vector &samplerDescriptions ); // Loads precompiled shaders bool _loadCompiledOutput( const Torque::Path &filePath, const String &target, GenericConstBufferLayout *bufferLayoutF, Vector &samplerDescriptions ); // This is used in both cases virtual void _buildShaderConstantHandles(GenericConstBufferLayout *layout, bool vertexConst); virtual void _buildSamplerShaderConstantHandles( Vector &samplerDescriptions ); /// Used to build the instancing shader constants from /// the instancing vertex format. void _buildInstancingShaderConstantHandles(); }; inline bool GFXD3D11Shader::getDisassembly(String &outStr) const { outStr = mDissasembly; return (outStr.isNotEmpty()); } #endif