Browse Source

DX11 arrays work

Marko Pintera 13 years ago
parent
commit
01defbfd9d

+ 15 - 8
CamelotClient/CamelotClient.cpp

@@ -111,16 +111,18 @@ int CALLBACK WinMain(
 								float uvMultiplier;									\
 							};													\
 																				\
+							float test1; \
+							InputStruct input[2];								\
 							float4x4 matViewProjection;							\
-							InputStruct input;									\
+														float test2; \
 							void vs_main(										\
 							in float4 inPos : POSITION,							\
 							in float2 uv : TEXCOORD0,							\
 							out float4 oPosition : SV_Position,					\
 							out float2 oUv : TEXCOORD0)							\
 							{													\
-							oPosition = mul(matViewProjection * input.matMultiplier, inPos);	\
-							oUv = uv * input.uvMultiplier;						\
+							oPosition = mul(matViewProjection * input[1].matMultiplier, inPos);	\
+							oUv = uv * input[1].uvMultiplier;						\
 							}";
 
 	HighLevelGpuProgramHandle vertProgRef =  HighLevelGpuProgram::create(vertShaderCode, "vs_main", "hlsl", GPT_VERTEX_PROGRAM, GPP_VS_4_0);
@@ -186,7 +188,7 @@ int CALLBACK WinMain(
 	ShaderPtr testShader = Shader::create("TestShader");
 
 	testShader->addParameter("matViewProjection", "matViewProjection", GPDT_MATRIX_4X4);
-	testShader->addParameter("input", "input", GPDT_STRUCT, 1, 8);
+	testShader->addParameter("input", "input", GPDT_STRUCT, 2, 8);
 
 	testShader->addParameter("samp", "samp", GPOT_SAMPLER2D);
 	testShader->addParameter("tex", "tex", GPOT_TEXTURE2D);
@@ -215,11 +217,16 @@ int CALLBACK WinMain(
 
 	testMaterial->setMat4("matViewProjection", Matrix4::IDENTITY);
 
-	float dbgMultipliers[2];
-	dbgMultipliers[0] = 1.0f;
-	dbgMultipliers[1] = 1.0f;
+	float dbgMultipliers1[2];
+	dbgMultipliers1[0] = 0.0f;
+	dbgMultipliers1[1] = 0.0f;
 
-	testMaterial->setStructData("input", dbgMultipliers, sizeof(dbgMultipliers));
+	float dbgMultipliers2[2];
+	dbgMultipliers2[0] = 1.0f;
+	dbgMultipliers2[1] = 1.0f;
+
+	testMaterial->setStructData("input", dbgMultipliers1, sizeof(dbgMultipliers1), 0);
+	testMaterial->setStructData("input", dbgMultipliers2, sizeof(dbgMultipliers2), 1);
 
 	//testMaterialRef = gResources().load("C:\\testMaterial.mat");
 	//testMaterialRef.waitUntilLoaded();

+ 48 - 4
CamelotD3D11RenderSystem/Source/CmD3D11HLSLParamParser.cpp

@@ -200,7 +200,18 @@ namespace CamelotEngine
 			parseVariable(varTypeDesc, varDesc, desc, blockDesc);
 		}
 
-		blockDesc.blockSize = constantBufferDesc.Size / 4; 
+		// Buffer always needs to be a multiple of 4, so make it so
+		blockDesc.blockSize += (4 - (blockDesc.blockSize % 4));
+
+#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)
@@ -210,11 +221,44 @@ namespace CamelotEngine
 		memberDesc.paramBlockSlot = paramBlock.slot;
 		memberDesc.arraySize = varTypeDesc.Elements == 0 ? 1 : varTypeDesc.Elements;
 		memberDesc.elementSize = varDesc.Size / 4; // Stored in multiples of 4
+		memberDesc.arrayElementStride = memberDesc.elementSize;
 		memberDesc.gpuMemOffset = varDesc.StartOffset;
-		memberDesc.cpuMemOffset = paramBlock.blockSize;
+		
+		// Elements in array always start at 16 byte (4 float) boundaries so we handle them specially
+		if(memberDesc.arraySize == 1)
+		{
+			UINT32 freeSlotsInRegister = (((paramBlock.blockSize / 4) + 1) * 4) - paramBlock.blockSize;
 
-		paramBlock.blockSize += memberDesc.arraySize * memberDesc.elementSize;
-	
+			// Fits in current register
+			if(memberDesc.elementSize <= freeSlotsInRegister)
+			{
+				memberDesc.cpuMemOffset = paramBlock.blockSize;
+				paramBlock.blockSize += memberDesc.elementSize;
+			}
+			else
+			{
+				// Doesn't fit, so skip remaining slots in register and go to new one
+				paramBlock.blockSize += freeSlotsInRegister;
+				memberDesc.cpuMemOffset = paramBlock.blockSize;
+				paramBlock.blockSize +=  memberDesc.elementSize;
+			}
+		}
+		else
+		{
+			// Find array element size (reported size is total size of array, minus unused register slots)
+			int totalSlotsUsedByArray = (memberDesc.elementSize / 4 + 1) * 4;
+			int unusedSlotsInArray = totalSlotsUsedByArray - memberDesc.elementSize;
+
+			memberDesc.arrayElementStride = totalSlotsUsedByArray / memberDesc.arraySize; // TODO - Array element stride should be stored in ParamDataDesc and used for determining size
+			memberDesc.elementSize = memberDesc.arrayElementStride - unusedSlotsInArray;
+
+			int freeSlotsInRegister = (((paramBlock.blockSize / 4) + 1) * 4) - paramBlock.blockSize;
+
+			paramBlock.blockSize += freeSlotsInRegister;
+			memberDesc.cpuMemOffset = paramBlock.blockSize;
+			paramBlock.blockSize += memberDesc.arraySize * memberDesc.arrayElementStride - unusedSlotsInArray;
+		}
+			
 		switch(varTypeDesc.Class)
 		{
 		case D3D_SVC_SCALAR:

+ 19 - 1
CamelotD3D9Renderer/Include/CmD3D9HLSLParamParser.h

@@ -104,7 +104,7 @@ namespace CamelotEngine
 				populateParamMemberDesc(memberDesc, desc);
 				mParamDesc.params.insert(std::make_pair(name, memberDesc));
 
-				blockDesc.blockSize += memberDesc.elementSize * memberDesc.arraySize;
+				blockDesc.blockSize += memberDesc.arrayElementStride * memberDesc.arraySize;
 			}
 			else if(desc.Type == D3DXPT_SAMPLER1D || desc.Type == D3DXPT_SAMPLER2D || desc.Type == D3DXPT_SAMPLER3D || desc.Type == D3DXPT_SAMPLERCUBE)
 			{
@@ -159,18 +159,22 @@ namespace CamelotEngine
 			case 1:
 				memberDesc.type = GPDT_INT1;
 				memberDesc.elementSize = 4;
+				memberDesc.arrayElementStride = 4;
 				break;
 			case 2:
 				memberDesc.type = GPDT_INT2;
 				memberDesc.elementSize = 4;
+				memberDesc.arrayElementStride = 4;
 				break;
 			case 3:
 				memberDesc.type = GPDT_INT3;
 				memberDesc.elementSize = 4;
+				memberDesc.arrayElementStride = 4;
 				break;
 			case 4:
 				memberDesc.type = GPDT_INT4;
 				memberDesc.elementSize = 4;
+				memberDesc.arrayElementStride = 4;
 				break;
 			} // columns
 			break;
@@ -196,14 +200,17 @@ namespace CamelotEngine
 						case 2:
 							memberDesc.type = GPDT_MATRIX_2X2;
 							memberDesc.elementSize = 8; // HLSL always packs
+							memberDesc.arrayElementStride = 8;
 							break;
 						case 3:
 							memberDesc.type = GPDT_MATRIX_2X3;
 							memberDesc.elementSize = 8; // HLSL always packs
+							memberDesc.arrayElementStride = 8;
 							break;
 						case 4:
 							memberDesc.type = GPDT_MATRIX_2X4;
 							memberDesc.elementSize = 8; 
+							memberDesc.arrayElementStride = 8;
 							break;
 						} // columns
 						break;
@@ -213,14 +220,17 @@ namespace CamelotEngine
 						case 2:
 							memberDesc.type = GPDT_MATRIX_3X2;
 							memberDesc.elementSize = 12; // HLSL always packs
+							memberDesc.arrayElementStride = 12;
 							break;
 						case 3:
 							memberDesc.type = GPDT_MATRIX_3X3;
 							memberDesc.elementSize = 12; // HLSL always packs
+							memberDesc.arrayElementStride = 12;
 							break;
 						case 4:
 							memberDesc.type = GPDT_MATRIX_3X4;
 							memberDesc.elementSize = 12; 
+							memberDesc.arrayElementStride = 12;
 							break;
 						} // columns
 						break;
@@ -230,14 +240,17 @@ namespace CamelotEngine
 						case 2:
 							memberDesc.type = GPDT_MATRIX_4X2;
 							memberDesc.elementSize = 16; // HLSL always packs
+							memberDesc.arrayElementStride = 16;
 							break;
 						case 3:
 							memberDesc.type = GPDT_MATRIX_4X3;
 							memberDesc.elementSize = 16; // HLSL always packs
+							memberDesc.arrayElementStride = 16;
 							break;
 						case 4:
 							memberDesc.type = GPDT_MATRIX_4X4;
 							memberDesc.elementSize = 16; 
+							memberDesc.arrayElementStride = 16;
 							break;
 						} // secondDim
 						break;
@@ -252,18 +265,22 @@ namespace CamelotEngine
 				case 1:
 					memberDesc.type = GPDT_FLOAT1;
 					memberDesc.elementSize = 4;
+					memberDesc.arrayElementStride = 4;
 					break;
 				case 2:
 					memberDesc.type = GPDT_FLOAT2;
 					memberDesc.elementSize = 4;
+					memberDesc.arrayElementStride = 4;
 					break;
 				case 3:
 					memberDesc.type = GPDT_FLOAT3;
 					memberDesc.elementSize = 4;
+					memberDesc.arrayElementStride = 4;
 					break;
 				case 4:
 					memberDesc.type = GPDT_FLOAT4;
 					memberDesc.elementSize = 4;
+					memberDesc.arrayElementStride = 4;
 					break;
 				} // columns
 				break;
@@ -272,6 +289,7 @@ namespace CamelotEngine
 		case D3DXPT_BOOL:
 			memberDesc.type = GPDT_BOOL;
 			memberDesc.elementSize = 1;
+			memberDesc.arrayElementStride = 1;
 			break;
 		default:
 			break;

+ 3 - 0
CamelotGLRenderer/Source/GLSL/include/CmGLSLParamParser.h

@@ -273,6 +273,7 @@ namespace CamelotEngine
 						assert (arrayStride % 4 == 0);
 
 						gpuParam.elementSize = arrayStride / 4;
+						gpuParam.arrayElementStride = gpuParam.elementSize;
 					}
 
 					gpuParam.paramBlockSlot = blockIndex + 1; // 0 is reserved for globals
@@ -403,5 +404,7 @@ namespace CamelotEngine
 		default:
 			CM_EXCEPT(InternalErrorException, "Invalid shader parameter type: " + toString(uniformType) + " for parameter " + paramName);
 		}
+
+		desc.arrayElementStride = desc.elementSize;
 	}
 }

+ 1 - 0
CamelotRenderer/Include/CmGpuParamDesc.h

@@ -11,6 +11,7 @@ namespace CamelotEngine
 		String name;
 		UINT32 elementSize; // Multiple of 4 bytes
 		UINT32 arraySize;
+		UINT32 arrayElementStride; // Multiple of 4 bytes
 		GpuParamDataType type;
 
 		UINT32 paramBlockSlot;

+ 2 - 2
CamelotRenderer/Source/CmGpuParams.cpp

@@ -219,13 +219,13 @@ namespace CamelotEngine
 			return;
 		}
 
-		paramBlock->write((desc->cpuMemOffset + arrayIndex * desc->elementSize) * sizeof(UINT32), value, sizeBytes);
+		paramBlock->write((desc->cpuMemOffset + arrayIndex * desc->arrayElementStride) * sizeof(UINT32), value, sizeBytes);
 
 		// Set unused bytes to 0
 		if(sizeBytes < elementSizeBytes)
 		{
 			UINT32 diffSize = elementSizeBytes - sizeBytes;
-			paramBlock->zeroOut((desc->cpuMemOffset + arrayIndex * desc->elementSize + sizeBytes)  * sizeof(UINT32), diffSize);
+			paramBlock->zeroOut((desc->cpuMemOffset + arrayIndex * desc->arrayElementStride + sizeBytes)  * sizeof(UINT32), diffSize);
 		}
 	}
 

+ 2 - 11
CamelotRenderer/Source/CmMaterial.cpp

@@ -487,7 +487,8 @@ namespace CamelotEngine
 
 	bool Material::areParamsEqual(const GpuParamDataDesc& paramA, const GpuParamDataDesc& paramB, bool ignoreBufferOffsets) const
 	{
-		bool equal = paramA.arraySize == paramB.arraySize && paramA.elementSize == paramB.elementSize && paramA.type == paramB.type;
+		bool equal = paramA.arraySize == paramB.arraySize && paramA.elementSize == paramB.elementSize 
+			&& paramA.type == paramB.type && paramA.arrayElementStride == paramB.arrayElementStride;
 
 		if(!ignoreBufferOffsets)
 			equal &= paramA.cpuMemOffset == paramB.cpuMemOffset && paramA.gpuMemOffset == paramB.gpuMemOffset;
@@ -716,17 +717,7 @@ namespace CamelotEngine
 				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);
-					}
 				}
 			}
 		}

+ 9 - 1
CamelotRenderer/TODO.txt

@@ -12,12 +12,20 @@
 >>>>>>>>>>FINAL SPRINT BEFORE EDITOR WORK
 Pass
  - A way to bind buffers to a Pass, while specifying buffer range
- - GpuParams support for bools, buffers, structs
+ - GpuParams support for buffers, structs
 
 Shader import:
  - Make sure none of the current shaders in CamelotClient are defined in code anymore
  - Add include files to ImportOptions
 
+  arrays in GLSL should work fine if I increase locations sequentially based on the first element (so I don't need to introduce names like name[0])
+
+ Add a better way to test multiple configurations (probably just re-factor CamelotClient with a few includes)
+  - Need to comment out code for setting structs when working with DX9
+
+Test shader arrays
+Implement structs in GLSL
+
 Seems there is a possible deadlock when starting the render thread, while waiting for the thread to be started
 
 Deserialization issues: