Browse Source

DirectX 11 struct shader parameters work

Marko Pintera 13 years ago
parent
commit
700d56beaa

+ 22 - 5
CamelotClient/CamelotClient.cpp

@@ -105,15 +105,22 @@ int CALLBACK WinMain(
 
 	HighLevelGpuProgramHandle fragProgRef =  HighLevelGpuProgram::create(fragShaderCode, "ps_main", "hlsl", GPT_FRAGMENT_PROGRAM, GPP_PS_4_0);
 
-	String vertShaderCode = "float4x4 matViewProjection;	\
+	String vertShaderCode = "struct InputStruct									\
+							{													\
+								float matMultiplier;							\
+								float uvMultiplier;									\
+							};													\
+																				\
+							float4x4 matViewProjection;							\
+							InputStruct input;									\
 							void vs_main(										\
 							in float4 inPos : POSITION,							\
-							in float2 uv : TEXCOORD0,								\
+							in float2 uv : TEXCOORD0,							\
 							out float4 oPosition : SV_Position,					\
 							out float2 oUv : TEXCOORD0)							\
-							{														\
-							oPosition = mul(matViewProjection, inPos);			\
-							oUv = uv;											\
+							{													\
+							oPosition = mul(matViewProjection * input.matMultiplier, inPos);	\
+							oUv = uv * input.uvMultiplier;						\
 							}";
 
 	HighLevelGpuProgramHandle vertProgRef =  HighLevelGpuProgram::create(vertShaderCode, "vs_main", "hlsl", GPT_VERTEX_PROGRAM, GPP_VS_4_0);
@@ -177,7 +184,10 @@ int CALLBACK WinMain(
 	fragProgRef = gResources().load("C:\\fragProgCg.vprog");
 
 	ShaderPtr testShader = Shader::create("TestShader");
+
 	testShader->addParameter("matViewProjection", "matViewProjection", GPDT_MATRIX_4X4);
+	testShader->addParameter("input", "input", GPDT_STRUCT, 1, 8);
+
 	testShader->addParameter("samp", "samp", GPOT_SAMPLER2D);
 	testShader->addParameter("tex", "tex", GPOT_TEXTURE2D);
 	TechniquePtr newTechniqueGL = testShader->addTechnique("GLRenderSystem", "ForwardRenderer");
@@ -204,6 +214,13 @@ int CALLBACK WinMain(
 	testMaterial->setShader(testShader);
 
 	testMaterial->setMat4("matViewProjection", Matrix4::IDENTITY);
+
+	float dbgMultipliers[2];
+	dbgMultipliers[0] = 1.0f;
+	dbgMultipliers[1] = 1.0f;
+
+	testMaterial->setStructData("input", dbgMultipliers, sizeof(dbgMultipliers));
+
 	//testMaterialRef = gResources().load("C:\\testMaterial.mat");
 	//testMaterialRef.waitUntilLoaded();
 

+ 1 - 7
CamelotD3D11RenderSystem/Source/CmD3D11HLSLParamParser.cpp

@@ -200,13 +200,7 @@ namespace CamelotEngine
 			parseVariable(varTypeDesc, varDesc, desc, blockDesc);
 		}
 
-#if CM_DEBUG_MODE
-		if(constantBufferDesc.Size != (blockDesc.blockSize * 4))
-		{
-			CM_EXCEPT(InternalErrorException, "Calculated param block size and size returned by DirectX don't match. Calculated size is: " + toString(constantBufferDesc.Size) +
-				" and DirectX size is: " + toString(blockDesc.blockSize * 4));
-		}
-#endif
+		blockDesc.blockSize = constantBufferDesc.Size / 4; 
 	}
 
 	void D3D11HLSLParamParser::parseVariable(D3D11_SHADER_TYPE_DESC& varTypeDesc, D3D11_SHADER_VARIABLE_DESC& varDesc, GpuParamDesc& desc, GpuParamBlockDesc& paramBlock)

+ 1 - 0
CamelotRenderer/Include/CmGpuParams.h

@@ -16,6 +16,7 @@ namespace CamelotEngine
 		void setParamBlock(const String& name, GpuParamBlockPtr paramBlock);
 
 		const GpuParamDesc& getParamDesc() const { return mParamDesc; }
+		UINT32 getDataParamSize(const String& name) const;
 
 		bool hasParam(const String& name) const;
 		bool hasTexture(const String& name) const;

+ 20 - 0
CamelotRenderer/Include/CmMaterial.h

@@ -36,6 +36,23 @@ namespace CamelotEngine
 	class CM_EXPORT Material : public Resource
 	{
 	public:
+		struct StructData
+		{
+			StructData()
+				:size(0), data(nullptr)
+			{ }
+
+			StructData(void* _data, UINT32 _size)
+				:size(_size)
+			{
+				data = std::shared_ptr<void>(new UINT8[_size]);
+				memcpy(data.get(), _data, size);
+			}
+
+			std::shared_ptr<void> data;
+			UINT32 size;
+		};
+
 		~Material();
 
 		/**
@@ -59,6 +76,7 @@ namespace CamelotEngine
 		void setVec4(const String& name, const Vector4& value, UINT32 arrayIdx = 0);
 		void setMat3(const String& name, const Matrix3& value, UINT32 arrayIdx = 0);
 		void setMat4(const String& name, const Matrix4& value, UINT32 arrayIdx = 0);
+		void setStructData(const String& name, void* value, UINT32 size, UINT32 arrayIdx = 0);
 
 		void setParamBlock(const String& name, GpuParamBlockPtr paramBlock);
 
@@ -88,6 +106,7 @@ namespace CamelotEngine
 		map<String, vector<Vector4>::type>::type mVec4Values;
 		map<String, vector<Matrix3>::type>::type mMat3Values;
 		map<String, vector<Matrix4>::type>::type mMat4Values;
+		map<String, vector<StructData>::type>::type mStructValues;
 		map<String, TextureHandle>::type mTextureValues;
 		map<String, SamplerStateHandle>::type mSamplerValues;
 
@@ -124,6 +143,7 @@ namespace CamelotEngine
 		Vector4 getVec4(const String& name, UINT32 arrayIdx = 0) const;
 		Matrix3 getMat3(const String& name, UINT32 arrayIdx = 0) const;
 		Matrix4 getMat4(const String& name, UINT32 arrayIdx = 0) const;
+		const StructData& getStructData(const String& name, UINT32 arrayIdx = 0) const;
 
 		void initBestTechnique();
 

+ 68 - 5
CamelotRenderer/Include/CmMaterialRTTI.h

@@ -93,6 +93,19 @@ namespace CamelotEngine
 		virtual RTTITypeBase* getRTTI() const;	
 	};
 
+	class CM_EXPORT MaterialStructParam : public IReflectable
+	{
+	public:
+		String name;
+		Material::StructData value;
+		UINT32 arrayIdx;
+		UINT32 elementSize;
+
+		friend class MaterialStructParamRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const;	
+	};
+
 	class CM_EXPORT MaterialTextureParam : public IReflectable
 	{
 	public:
@@ -124,6 +137,7 @@ namespace CamelotEngine
 		vector<MaterialVec4Param>::type vec4Params;
 		vector<MaterialMat3Param>::type mat3Params;
 		vector<MaterialMat4Param>::type mat4Params;
+		vector<MaterialStructParam>::type structParams;
 		vector<MaterialTextureParam>::type textureParams;
 		vector<MaterialSamplerStateParam>::type samplerStateParams;
 
@@ -310,6 +324,50 @@ namespace CamelotEngine
 		virtual std::shared_ptr<IReflectable> newRTTIObject() { return std::shared_ptr<IReflectable>(new MaterialMat4Param()); }
 	};
 
+	class CM_EXPORT MaterialStructParamRTTI : public RTTIType<MaterialStructParam, IReflectable, MaterialStructParamRTTI>
+	{
+	public:
+		String& getName(MaterialStructParam* obj) { return obj->name; }
+		void setName(MaterialStructParam* obj, String& name) { obj->name = name; }
+
+		ManagedDataBlock getValue(MaterialStructParam* obj) 
+		{ 
+			UINT8* data = new UINT8[obj->value.size];
+			memcpy(data, obj->value.data.get(), obj->value.size);
+
+			ManagedDataBlock returnValue(data, obj->value.size, true);
+			return returnValue; 
+		}
+
+		void setValue(MaterialStructParam* obj, ManagedDataBlock value) 
+		{ 
+			obj->value = Material::StructData(value.getData(), value.getSize()); 
+		}
+
+		UINT32& getArrayIdx(MaterialStructParam* obj) { return obj->arrayIdx; }
+		void setArrayIdx(MaterialStructParam* obj, UINT32& value) { obj->arrayIdx = value; }
+
+		UINT32& getElementSize(MaterialStructParam* obj) { return obj->elementSize; }
+		void setElementSize(MaterialStructParam* obj, UINT32& value) { obj->elementSize = value; }
+
+		MaterialStructParamRTTI()
+		{
+			addPlainField("name", 0, &MaterialStructParamRTTI::getName, &MaterialStructParamRTTI::setName);
+			addDataBlockField("value", 1, &MaterialStructParamRTTI::getValue, &MaterialStructParamRTTI::setValue);
+			addPlainField("arrayIdx", 2, &MaterialStructParamRTTI::getArrayIdx, &MaterialStructParamRTTI::setArrayIdx);
+			addPlainField("elementSize", 3, &MaterialStructParamRTTI::getElementSize, &MaterialStructParamRTTI::setElementSize);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "MaterialStructParam";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() { return TID_MaterialParamStruct; }
+		virtual std::shared_ptr<IReflectable> newRTTIObject() { return std::shared_ptr<IReflectable>(new MaterialStructParam()); }
+	};
+
 	class CM_EXPORT MaterialTextureParamRTTI : public RTTIType<MaterialTextureParam, IReflectable, MaterialTextureParamRTTI>
 	{
 	public:
@@ -393,6 +451,11 @@ namespace CamelotEngine
 		UINT32 getMat4ArraySize(MaterialParams* obj) { return (UINT32)obj->mat4Params.size(); }
 		void setMat4ArraySize(MaterialParams* obj, UINT32 size) { obj->mat4Params.resize(size); }
 
+		MaterialStructParam& getStructParam(MaterialParams* obj, UINT32 idx) { return obj->structParams[idx]; }
+		void setStructParam(MaterialParams* obj, UINT32 idx, MaterialStructParam& param) { obj->structParams[idx] = param; }
+		UINT32 getStructArraySize(MaterialParams* obj) { return (UINT32)obj->structParams.size(); }
+		void setStructArraySize(MaterialParams* obj, UINT32 size) { obj->structParams.resize(size); }
+
 		MaterialTextureParam& getTextureParam(MaterialParams* obj, UINT32 idx) { return obj->textureParams[idx]; }
 		void setTextureParam(MaterialParams* obj, UINT32 idx, MaterialTextureParam& param) { obj->textureParams[idx] = param; }
 		UINT32 getTextureArraySize(MaterialParams* obj) { return (UINT32)obj->textureParams.size(); }
@@ -423,10 +486,13 @@ namespace CamelotEngine
 			addReflectableArrayField("mat4Params", 5, &MaterialParamsRTTI::getMat4Param, 
 				&MaterialParamsRTTI::getMat4ArraySize, &MaterialParamsRTTI::setMat4Param, &MaterialParamsRTTI::setMat4ArraySize);
 
-			addReflectableArrayField("textureParams", 6, &MaterialParamsRTTI::getTextureParam, 
+			addReflectableArrayField("structParams", 6, &MaterialParamsRTTI::getStructParam, 
+				&MaterialParamsRTTI::getStructArraySize, &MaterialParamsRTTI::setStructParam, &MaterialParamsRTTI::setStructArraySize);
+
+			addReflectableArrayField("textureParams", 7, &MaterialParamsRTTI::getTextureParam, 
 				&MaterialParamsRTTI::getTextureArraySize, &MaterialParamsRTTI::setTextureParam, &MaterialParamsRTTI::setTextureArraySize);
 
-			addReflectableArrayField("samplerStateParams", 7, &MaterialParamsRTTI::getSamplerStateParam, 
+			addReflectableArrayField("samplerStateParams", 8, &MaterialParamsRTTI::getSamplerStateParam, 
 				&MaterialParamsRTTI::getSamplerStateArraySize, &MaterialParamsRTTI::setSamplerStateParam, &MaterialParamsRTTI::setSamplerStateArraySize);
 		}
 
@@ -440,9 +506,6 @@ namespace CamelotEngine
 		virtual std::shared_ptr<IReflectable> newRTTIObject() { return std::shared_ptr<IReflectable>(new MaterialParams()); }
 	};
 
-	// TODO - Add MaterialParamsRTTI
-	// - Add a way to fill and read from MaterialParams
-
 	class CM_EXPORT MaterialRTTI : public RTTIType<Material, Resource, MaterialRTTI>
 	{
 	private:

+ 2 - 1
CamelotRenderer/Include/CmPrerequisites.h

@@ -259,7 +259,8 @@ namespace CamelotEngine
 		TID_SHADER_OBJECT_PARAM_DESC = 1036,
 		TID_SHADER_PARAM_BLOCK_DESC = 1047,
 		TID_ImportOptions = 1048,
-		TID_GpuProgramImportOptions = 1049
+		TID_GpuProgramImportOptions = 1049,
+		TID_MaterialParamStruct = 1050
 	};
 
 	/**

+ 16 - 1
CamelotRenderer/Include/CmShader.h

@@ -13,6 +13,7 @@ namespace CamelotEngine
 		GpuParamDataType type;
 		UINT32 arraySize;
 		bool hidden;
+		UINT32 elementSize;
 	};
 
 	struct CM_EXPORT SHADER_OBJECT_PARAM_DESC
@@ -55,7 +56,21 @@ namespace CamelotEngine
 		 */
 		TechniquePtr getBestTechnique() const;
 
-		void addParameter(const String& name, const String& gpuVariableName, GpuParamDataType type, UINT32 arraySize = 1, bool hidden = false);
+		/**
+		 * @brief	Registers a new parameter you can use for easily setting GpuProgram constants via Material.
+		 * 			Only data types may be set using this method. Use the other overload of the method if you want
+		 * 			to add object parameters.
+		 *
+		 * @param	name		   	The name of the parameter.
+		 * @param	gpuVariableName	Name of the GPU variable in the GpuProgram that the parameter corresponds with.
+		 * @param	type		   	The type of the parameter, must be the same as the type in GpuProgram.
+		 * @param	arraySize	   	(optional) If the parameter is an array, the number of elements in the array. Size of 1 means its not an array.
+		 * @param	elementSize	   	(optional) Size of an individual element in the array, in bytes. You only need to set this if you are setting variable
+		 * 							length parameters, like structs.
+		 * @param	hidden		   	(optional) Property that is not directly used by the material system, but can be useful if you need to mark certain parameters
+		 * 							as hidden to some system. (e.g. hiding internal engine-managed parameters from the user in the Editor)
+		 */
+		void addParameter(const String& name, const String& gpuVariableName, GpuParamDataType type, UINT32 arraySize = 1, UINT32 elementSize = 0, bool hidden = false);
 		void addParameter(const String& name, const String& gpuVariableName, GpuParamObjectType type, bool hidden = false);
 		void removeParameter(const String& name);
 		void setParamBlockAttribs(const String& name, bool shared, GpuParamBlockUsage usage);

+ 3 - 1
CamelotRenderer/Include/CmShaderRTTI.h

@@ -23,6 +23,7 @@ namespace CamelotEngine
 			memory = rttiWriteElem(data.type, memory);
 			memory = rttiWriteElem(data.name, memory);
 			memory = rttiWriteElem(data.gpuVariableName, memory);
+			memory = rttiWriteElem(data.elementSize, memory);
 		}
 
 		static void fromMemory(SHADER_DATA_PARAM_DESC& data, char* memory)
@@ -36,12 +37,13 @@ namespace CamelotEngine
 			memory = rttiReadElem(data.type, memory);
 			memory = rttiReadElem(data.name, memory);
 			memory = rttiReadElem(data.gpuVariableName, memory);
+			memory = rttiReadElem(data.elementSize, memory);
 		}
 
 		static UINT32 getDynamicSize(SHADER_DATA_PARAM_DESC& data)	
 		{ 
 			UINT64 dataSize = rttiGetElemSize(data.arraySize) + rttiGetElemSize(data.hidden) + rttiGetElemSize(data.type) + 
-				rttiGetElemSize(data.name) + rttiGetElemSize(data.gpuVariableName) + sizeof(UINT32);
+				rttiGetElemSize(data.name) + rttiGetElemSize(data.gpuVariableName) + rttiGetElemSize(data.elementSize) + sizeof(UINT32);
 
 #if CM_DEBUG_MODE
 			if(dataSize > std::numeric_limits<UINT32>::max())

+ 9 - 0
CamelotRenderer/Source/CmGpuParams.cpp

@@ -86,6 +86,15 @@ namespace CamelotEngine
 		mParamBlocks[iterFind->second.slot] = paramBlock;
 	}
 
+	UINT32 GpuParams::getDataParamSize(const String& name) const
+	{
+		GpuParamDataDesc* desc = getParamDesc(name);
+		if(desc != nullptr)
+			return desc->elementSize * 4;
+
+		return 0;
+	}
+
 	bool GpuParams::hasParam(const String& name) const
 	{
 		return getParamDesc(name) != nullptr;

+ 55 - 0
CamelotRenderer/Source/CmMaterial.cpp

@@ -41,6 +41,7 @@ namespace CamelotEngine
 		mVec4Values.clear();
 		mMat3Values.clear();
 		mMat4Values.clear();
+		mStructValues.clear();
 		mTextureValues.clear();
 		mSamplerValues.clear();
 
@@ -198,6 +199,9 @@ namespace CamelotEngine
 				case GPDT_MATRIX_4X4:
 					mMat4Values[iter->first].resize(iter->second.arraySize);
 					break;
+				case GPDT_STRUCT:
+					mStructValues[iter->first].resize(iter->second.arraySize);
+					break;
 				default:
 					CM_EXCEPT(InternalErrorException, "Unsupported data type.");
 				}
@@ -690,6 +694,47 @@ namespace CamelotEngine
 		savedValue[arrayIdx] = value;
 	}
 
+	void Material::setStructData(const String& name, void* value, UINT32 size, UINT32 arrayIdx)
+	{
+		throwIfNotInitialized();
+
+		auto iterFind = mValidParams.find(name);
+		if(iterFind == mValidParams.end())
+		{
+			LOGWRN("Material doesn't have a parameter named " + name);
+			return;
+		}
+
+		String& gpuVarName = iterFind->second;
+		for(auto iter = mParametersPerPass.begin(); iter != mParametersPerPass.end(); ++iter)
+		{
+			PassParametersPtr params = *iter;
+
+			for(UINT32 i = 0; i < params->getNumParams(); i++)
+			{
+				GpuParamsPtr& paramPtr = params->getParamByIdx(i);
+				if(paramPtr)
+				{
+					if(paramPtr->hasParam(gpuVarName))
+					{
+						UINT32 structSize = paramPtr->getDataParamSize(gpuVarName);
+
+						if(structSize != size)
+						{
+							CM_EXCEPT(InternalErrorException, "Size provided doesn't match the shader struct size. Provided: " + 
+								toString(size) + ". Expected: " + toString(structSize));
+						}
+
+						paramPtr->setParam(gpuVarName, value, size, arrayIdx);
+					}
+				}
+			}
+		}
+
+		auto& savedValue = mStructValues[name];
+		savedValue[arrayIdx] = StructData(value, size);
+	}
+
 	void Material::setParamBlock(const String& name, GpuParamBlockPtr paramBlock)
 	{
 		auto iterFind = mValidShareableParamBlocks.find(name);
@@ -826,6 +871,16 @@ namespace CamelotEngine
 		return iterFind->second.at(arrayIdx);
 	}
 
+	const Material::StructData& Material::getStructData(const String& name, UINT32 arrayIdx) const
+	{
+		auto iterFind = mStructValues.find(name);
+
+		if(iterFind == mStructValues.end())
+			CM_EXCEPT(InternalErrorException, "No float parameter with the name: " + name);
+
+		return iterFind->second.at(arrayIdx);
+	}
+
 	MaterialHandle Material::create()
 	{
 		MaterialPtr materialPtr = MaterialManager::instance().create();

+ 31 - 1
CamelotRenderer/Source/CmMaterialRTTI.cpp

@@ -22,6 +22,9 @@ namespace CamelotEngine
 	RTTITypeBase* MaterialMat4Param::getRTTIStatic() { return MaterialMat4ParamRTTI::instance(); }
 	RTTITypeBase* MaterialMat4Param::getRTTI() const { return MaterialMat4Param::getRTTIStatic(); }
 
+	RTTITypeBase* MaterialStructParam::getRTTIStatic() { return MaterialStructParamRTTI::instance(); }
+	RTTITypeBase* MaterialStructParam::getRTTI() const { return MaterialStructParam::getRTTIStatic(); }
+
 	RTTITypeBase* MaterialTextureParam::getRTTIStatic() { return MaterialTextureParamRTTI::instance(); }
 	RTTITypeBase* MaterialTextureParam::getRTTI() const { return MaterialTextureParam::getRTTIStatic(); }
 
@@ -129,6 +132,20 @@ namespace CamelotEngine
 							}
 						}
 						break;
+					case GPDT_STRUCT:
+						{
+							for(UINT32 i = 0; i < paramDesc.arraySize; i++)
+							{
+								MaterialStructParam param;
+								param.name = iter->first;
+								param.value = material->getStructData(iter->first, i);
+								param.arrayIdx = i;
+								param.elementSize = paramDesc.elementSize;
+
+								params->structParams.push_back(param);
+							}
+						}
+						break;
 					default:
 						CM_EXCEPT(InternalErrorException, "Cannot serialize this paramater type: " + toString(paramDesc.type));
 					}
@@ -143,7 +160,7 @@ namespace CamelotEngine
 						param.name = iter->first;
 						param.value = material->getSamplerState(iter->first);
 
-						//params->samplerStateParams.push_back(param);
+						params->samplerStateParams.push_back(param);
 					}
 					else if(Shader::isTexture(paramDesc.type))
 					{
@@ -274,6 +291,19 @@ namespace CamelotEngine
 				material->setMat4(iter->name, iter->value, iter->arrayIdx);
 			}
 
+			for(auto iter = params->structParams.begin(); iter != params->structParams.end(); ++iter)
+			{
+				if(!shader->hasDataParam(iter->name))
+					continue;
+
+				const SHADER_DATA_PARAM_DESC& paramDesc = shader->getDataParamDesc(iter->name);
+
+				if(paramDesc.type != GPDT_STRUCT || iter->arrayIdx < 0 || iter->arrayIdx >= paramDesc.arraySize || iter->elementSize != paramDesc.elementSize)
+					continue;
+
+				material->setStructData(iter->name, iter->value.data.get(), iter->value.size, iter->arrayIdx);
+			}
+
 			for(auto iter = params->samplerStateParams.begin(); iter != params->samplerStateParams.end(); ++iter)
 			{
 				if(!shader->hasObjectParam(iter->name))

+ 5 - 1
CamelotRenderer/Source/CmShader.cpp

@@ -61,14 +61,18 @@ namespace CamelotEngine
 		// TODO - Low priority. Instead of throwing an exception use an extremely simple technique that will be supported almost everywhere as a fallback.
 	}
 
-	void Shader::addParameter(const String& name, const String& gpuVariableName, GpuParamDataType type, UINT32 arraySize, bool hidden)
+	void Shader::addParameter(const String& name, const String& gpuVariableName, GpuParamDataType type, UINT32 arraySize, UINT32 elementSize, bool hidden)
 	{
+		if(type == GPDT_STRUCT && elementSize <= 0)
+			CM_EXCEPT(InvalidParametersException, "You need to provide a non-zero element size for a struct parameter.")
+
 		SHADER_DATA_PARAM_DESC desc;
 		desc.name = name;
 		desc.gpuVariableName = gpuVariableName;
 		desc.type = type;
 		desc.arraySize = arraySize;
 		desc.hidden = hidden;
+		desc.elementSize = elementSize;
 
 		mDataParams[name] = desc;
 		mObjectParams.erase(name);