Browse Source

Feature: DirectX, OpenGL and Vulkan will now parse structs in uniform blocks/constant buffers

BearishSun 8 years ago
parent
commit
8f14edffe6

+ 3 - 2
Source/BansheeD3D11RenderAPI/BsD3D11HLSLParamParser.cpp

@@ -5,6 +5,7 @@
 #include "RenderAPI/BsGpuParamDesc.h"
 #include "RenderAPI/BsGpuParamDesc.h"
 #include "Error/BsException.h"
 #include "Error/BsException.h"
 #include "Debug/BsDebug.h"
 #include "Debug/BsDebug.h"
+#include "Math/BsMath.h"
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
@@ -308,7 +309,7 @@ namespace bs { namespace ct
 			// Find array element size (reported size is total size of array, minus unused register slots)
 			// Find array element size (reported size is total size of array, minus unused register slots)
 			int totalArraySize = (varDesc.Size / 4);
 			int totalArraySize = (varDesc.Size / 4);
 
 
-			int totalSlotsUsedByArray = (totalArraySize / 4 + 1) * 4;
+			int totalSlotsUsedByArray = Math::divideAndRoundUp(totalArraySize, 4) * 4;
 			int unusedSlotsInArray = totalSlotsUsedByArray - totalArraySize;
 			int unusedSlotsInArray = totalSlotsUsedByArray - totalArraySize;
 
 
 			memberDesc.arrayElementStride = totalSlotsUsedByArray / memberDesc.arraySize;
 			memberDesc.arrayElementStride = totalSlotsUsedByArray / memberDesc.arraySize;
@@ -454,4 +455,4 @@ namespace bs { namespace ct
 		
 		
 		return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx;
 		return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx;
 	}
 	}
-}}
+}}

+ 3 - 27
Source/BansheeGLRenderAPI/BsGLRenderAPI.cpp

@@ -28,6 +28,7 @@
 #include "BsGLCommandBuffer.h"
 #include "BsGLCommandBuffer.h"
 #include "BsGLCommandBufferManager.h"
 #include "BsGLCommandBufferManager.h"
 #include "BsGLTextureView.h"
 #include "BsGLTextureView.h"
+#include "GLSL/BsGLSLParamParser.h"
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
@@ -2782,35 +2783,10 @@ namespace bs { namespace ct
 
 
 		for (auto& param : params)
 		for (auto& param : params)
 		{
 		{
-			const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[param.type];
-			UINT32 size = typeInfo.size / 4;
-			UINT32 alignment = typeInfo.alignment / 4;
-
-			// Fix alignment if needed
-			UINT32 alignOffset = block.blockSize % alignment;
-			if (alignOffset != 0)
-			{
-				UINT32 padding = (alignment - alignOffset);
-				block.blockSize += padding;
-			}
+			UINT32 size = GLSLParamParser::calcInterfaceBlockElementSizeAndOffset(param.type, param.arraySize, block.blockSize);
 
 
 			if (param.arraySize > 1)
 			if (param.arraySize > 1)
 			{
 			{
-				// Array elements are always padded and aligned to vec4
-				alignOffset = size % typeInfo.baseTypeSize;
-				if (alignOffset != 0)
-				{
-					UINT32 padding = (typeInfo.baseTypeSize - alignOffset);
-					size += padding;
-				}
-
-				alignOffset = block.blockSize % typeInfo.baseTypeSize;
-				if (alignOffset != 0)
-				{
-					UINT32 padding = (typeInfo.baseTypeSize - alignOffset);
-					block.blockSize += padding;
-				}
-
 				param.elementSize = size;
 				param.elementSize = size;
 				param.arrayElementStride = size;
 				param.arrayElementStride = size;
 				param.cpuMemOffset = block.blockSize;
 				param.cpuMemOffset = block.blockSize;
@@ -2849,4 +2825,4 @@ namespace bs { namespace ct
 		}
 		}
 	}
 	}
 #endif
 #endif
-}}
+}}

+ 64 - 32
Source/BansheeGLRenderAPI/GLSL/BsGLSLParamParser.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "GLSL/BsGLSLParamParser.h"
 #include "GLSL/BsGLSLParamParser.h"
+#include "RenderAPI/BsGpuParams.h"
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
@@ -61,6 +62,43 @@ namespace bs { namespace ct
 		return elementList;
 		return elementList;
 	}
 	}
 
 
+	UINT32 GLSLParamParser::calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset)
+	{
+		const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[type];
+		UINT32 size = (typeInfo.baseTypeSize * typeInfo.numColumns * typeInfo.numRows) / 4;
+		UINT32 alignment = typeInfo.alignment / 4;
+
+		// Fix alignment if needed
+		UINT32 alignOffset = offset % alignment;
+		if (alignOffset != 0)
+		{
+			UINT32 padding = (alignment - alignOffset);
+			offset += padding;
+		}
+
+		if (arraySize > 1)
+		{
+			// Array elements are always padded and aligned to vec4
+			alignOffset = size % 4;
+			if (alignOffset != 0)
+			{
+				UINT32 padding = (4 - alignOffset);
+				size += padding;
+			}
+
+			alignOffset = offset % 4;
+			if (alignOffset != 0)
+			{
+				UINT32 padding = (4 - alignOffset);
+				offset += padding;
+			}
+
+			return size;
+		}
+		else
+			return size;
+	}
+
 	VertexElementType GLSLParamParser::glTypeToAttributeType(GLenum glType)
 	VertexElementType GLSLParamParser::glTypeToAttributeType(GLenum glType)
 	{
 	{
 		switch (glType)
 		switch (glType)
@@ -266,12 +304,14 @@ namespace bs { namespace ct
 					{
 					{
 						inStruct = true;
 						inStruct = true;
 						structName = nameElements[1];
 						structName = nameElements[1];
+						paramName = nameElements.back();
 					}
 					}
 				}
 				}
 				else
 				else
 				{
 				{
 					inStruct = true;
 					inStruct = true;
 					structName = nameElements[0];
 					structName = nameElements[0];
+					paramName = nameElements.back();
 				}
 				}
 			}
 			}
 
 
@@ -294,7 +334,7 @@ namespace bs { namespace ct
 					structName = structName.substr(0, arrayStart);
 					structName = structName.substr(0, arrayStart);
 				}
 				}
 			}
 			}
-			else
+
 			{
 			{
 				// If the uniform name has a "[" in it then its an array element uniform.
 				// If the uniform name has a "[" in it then its an array element uniform.
 				String::size_type arrayStart = cleanParamName.find("[");
 				String::size_type arrayStart = cleanParamName.find("[");
@@ -302,33 +342,24 @@ namespace bs { namespace ct
 				if (arrayStart != String::npos)
 				if (arrayStart != String::npos)
 				{
 				{
 					String strArrIdx = cleanParamName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1));
 					String strArrIdx = cleanParamName.substr(arrayStart + 1, arrayEnd - (arrayStart + 1));
-					arrayIdx = parseUINT32(strArrIdx, 0);
-					isInArray = true;
+
+					// If in struct, we don't care about individual element array indices
+					if(!inStruct)
+					{
+						arrayIdx = parseUINT32(strArrIdx, 0);
+						isInArray = true;
+					}
 
 
 					cleanParamName = cleanParamName.substr(0, arrayStart);
 					cleanParamName = cleanParamName.substr(0, arrayStart);
 				}
 				}
 			}
 			}
 
 
-			if (inStruct)
-			{
-				// OpenGL makes struct management really difficult, which is why I have given up on implementing this so far
-				// Some of the issues I encountered:
-				//  - Elements will be optimized out if they are not used. This makes it hard to determine proper structure size.
-				//     - If struct is within a Uniform buffer block, then it is possible because the element won't be optimized out of the buffer
-				//     - If the struct is within a global buffer, it is impossible to determine actual size, since the element will be optimized out of the buffer too
-				//     - Same issue happens with arrays, as OpenGL will optimize out array elements. With global buffers this makes it impossible to determine
-				//       actual array size (for example suppose OpenGL optimized out few last elements)
-				//        - Normal arrays work fine as OpenGL has utilities for reporting their actual size, but those do not work with structs
-
-				BS_EXCEPT(NotImplementedException, "Structs are not supported.")
-			}
-
 			// GLSL will optimize out unused array indexes, so there's no guarantee that 0 is the first,
 			// GLSL will optimize out unused array indexes, so there's no guarantee that 0 is the first,
 			// so we store the first one here
 			// so we store the first one here
 			int firstArrayIndex = 0;
 			int firstArrayIndex = 0;
 			if (isInArray)
 			if (isInArray)
 			{
 			{
-				String& nameToSearch = cleanParamName;
+				String nameToSearch = cleanParamName;
 				if (inStruct)
 				if (inStruct)
 					nameToSearch = structName;
 					nameToSearch = structName;
 
 
@@ -586,8 +617,9 @@ namespace bs { namespace ct
 					BS_CHECK_GL_ERROR();
 					BS_CHECK_GL_ERROR();
 				}
 				}
 
 
-				// If parameter is not a part of a struct we're done
-				if (!inStruct)
+				// If parameter is not a part of a struct we're done. Also done if parameter is part of a struct, but
+				// not part of a uniform block (in which case we treat struct members as separate parameters)
+				if (!inStruct || blockIndex == -1)
 				{
 				{
 					returnParamDesc.params.insert(std::make_pair(gpuParam.name, gpuParam));
 					returnParamDesc.params.insert(std::make_pair(gpuParam.name, gpuParam));
 					continue;
 					continue;
@@ -615,26 +647,26 @@ namespace bs { namespace ct
 				// Update struct with size of the new parameter
 				// Update struct with size of the new parameter
 				GpuParamDataDesc& structDesc = foundStructs[structName];
 				GpuParamDataDesc& structDesc = foundStructs[structName];
 
 
-				assert(gpuParam.cpuMemOffset >= structDesc.cpuMemOffset);
 				if (arrayIdx == (UINT32)firstArrayIndex) // Determine element size only using the first array element
 				if (arrayIdx == (UINT32)firstArrayIndex) // Determine element size only using the first array element
 				{
 				{
-					structDesc.elementSize = std::max(structDesc.elementSize, (gpuParam.cpuMemOffset - structDesc.cpuMemOffset) + gpuParam.arrayElementStride * gpuParam.arraySize);
-					structDesc.arrayElementStride = structDesc.elementSize;
-				}
+					structDesc.elementSize = std::max(structDesc.elementSize, gpuParam.cpuMemOffset + 
+						gpuParam.arrayElementStride * gpuParam.arraySize);
 
 
-				// New array element reached, determine arrayElementStride
-				if (arrayIdx != (UINT32)firstArrayIndex)
-				{
-					UINT32 numElements = arrayIdx - firstArrayIndex;
-					structDesc.arrayElementStride = (gpuParam.cpuMemOffset - structDesc.cpuMemOffset) / numElements;
+					structDesc.gpuMemOffset = std::min(structDesc.gpuMemOffset, gpuParam.gpuMemOffset);
+					structDesc.cpuMemOffset = std::min(structDesc.cpuMemOffset, gpuParam.cpuMemOffset);
 				}
 				}
 
 
 				structDesc.arraySize = std::max(structDesc.arraySize, arrayIdx + 1);
 				structDesc.arraySize = std::max(structDesc.arraySize, arrayIdx + 1);
 			}
 			}
 		}
 		}
 
 
-		for (auto iter = foundStructs.begin(); iter != foundStructs.end(); ++iter)
-			returnParamDesc.params.insert(std::make_pair(iter->first, iter->second));
+		for(auto& entry : foundStructs)
+		{
+			entry.second.elementSize = entry.second.elementSize - entry.second.cpuMemOffset;
+			entry.second.arrayElementStride = Math::divideAndRoundUp(entry.second.elementSize, 4U) * 4;
+
+			returnParamDesc.params.insert(std::make_pair(entry.first, entry.second));
+		}
 
 
 		// Param blocks always need to be a multiple of 4, so make it so
 		// Param blocks always need to be a multiple of 4, so make it so
 		for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter)
 		for (auto iter = returnParamDesc.paramBlocks.begin(); iter != returnParamDesc.paramBlocks.end(); ++iter)
@@ -786,4 +818,4 @@ namespace bs { namespace ct
 
 
 		return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx;
 		return progTypeIdx * (UINT32)ParamType::Count + paramTypeIdx;
 	}
 	}
-}}
+}}

+ 11 - 0
Source/BansheeGLRenderAPI/GLSL/BsGLSLParamParser.h

@@ -59,6 +59,17 @@ namespace bs { namespace ct
 		 */
 		 */
 		List<VertexElement> buildVertexDeclaration(GLuint glProgram);
 		List<VertexElement> buildVertexDeclaration(GLuint glProgram);
 
 
+		/**
+		 * Calculates the size and alignment of a single element within a shader interface block using the std140 layout.
+		 *
+		 * @param[in]		type		Type of the element. Structs are not supported.
+		 * @param[in]		arraySize	Number of array elements of the element (1 if it's not an array).
+		 * @param[in, out]	offset		Current location in some parent buffer at which the element should be placed at. If the
+		 *								location doesn't match the element's alignment, the value will be modified to a valid
+		 *								alignment. In multiples of 4 bytes.
+		 * @return						Size of the element, in multiples of 4 bytes.
+		 */
+		static UINT32 calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset);
 	private:
 	private:
 		/** Types of HLSL parameters. */
 		/** Types of HLSL parameters. */
 		enum class ParamType
 		enum class ParamType

+ 138 - 69
Source/BansheeVulkanRenderAPI/BsVulkanGpuProgram.cpp

@@ -14,6 +14,7 @@
 #include "FileSystem/BsDataStream.h"
 #include "FileSystem/BsDataStream.h"
 
 
 #define AMD_EXTENSIONS
 #define AMD_EXTENSIONS
+#define NV_EXTENSIONS
 #include "glslang/Public/ShaderLang.h"
 #include "glslang/Public/ShaderLang.h"
 #include "glslang/Include/Types.h"
 #include "glslang/Include/Types.h"
 #include "SPIRV/GlslangToSpv.h"
 #include "SPIRV/GlslangToSpv.h"
@@ -367,58 +368,47 @@ namespace bs { namespace ct
 		return true;
 		return true;
 	}
 	}
 
 
-	bool parseUniforms(const glslang::TProgram* program, GpuParamDesc& desc, String& log)
+	void parseStruct(const glslang::TTypeList* typeList, UINT32& size)
 	{
 	{
-		// Parse uniform blocks
-		UnorderedMap<UINT32, String> uniformBlockMap;
-		int numBlocks = program->getNumLiveUniformBlocks();
-		for (int i = 0; i < numBlocks; i++)
+		for (auto iter = typeList->begin(); iter != typeList->end(); ++iter)
 		{
 		{
-			const glslang::TType* ttype = program->getUniformBlockTType(i);
-			const glslang::TQualifier& qualifier = ttype->getQualifier();
-			const char* name = program->getUniformBlockName(i);
+			const glslang::TType* ttype = iter->type;
 
 
-			if (!qualifier.hasBinding())
+			if (ttype->getBasicType() == glslang::EbtStruct)
 			{
 			{
-				log = "Uniform parsing error: Found a uniform block without a binding qualifier. Each uniform block must "
-					" have an explicitly defined binding number.";
-
-				return false;
+				const glslang::TTypeList* childTypeList = ttype->getStruct();
+				parseStruct(childTypeList, size);
 			}
 			}
-
-			if(qualifier.storage == glslang::EvqBuffer) // Shared storage buffer
-			{
-				GpuParamObjectDesc param;
-				param.name = name;
-				param.slot = qualifier.layoutBinding;
-				param.set = qualifier.layoutSet;
-
-				if (param.set == glslang::TQualifier::layoutSetEnd)
-					param.set = 0;
-
-				param.type = GPOT_RWSTRUCTURED_BUFFER;
-				desc.buffers[name] = param;
-			}
-			else // Uniform buffer
+			else
 			{
 			{
-				int size = program->getUniformBlockSize(i);
+				UINT32 arraySize = 1;
+				if (ttype->isArray())
+					arraySize = (UINT32)ttype->getCumulativeArraySize();
 
 
-				GpuParamBlockDesc param;
-				param.name = name;
-				param.blockSize = size / 4;
-				param.isShareable = true;
-				param.slot = qualifier.layoutBinding;
-				param.set = qualifier.layoutSet;
-
-				if (param.set == glslang::TQualifier::layoutSetEnd)
-					param.set = 0;
+				GpuParamDataType paramType = mapGLSLangToGpuParamDataType(*ttype);
+				if (paramType == GPDT_UNKNOWN)
+				{
+					LOGWRN("Cannot determine type for uniform inside a struct.");
+					continue;
+				}
 
 
-				desc.paramBlocks[name] = param;
-				uniformBlockMap[i] = name;
+				UINT32 elemSize = VulkanUtility::calcInterfaceBlockElementSizeAndOffset(paramType, arraySize, size);
+				size += elemSize;
 			}
 			}
 		}
 		}
+	}
 
 
+	bool parseUniforms(const glslang::TProgram* program, GpuParamDesc& desc, String& log)
+	{
 		// Parse individual uniforms
 		// Parse individual uniforms
+		struct UniformInfo
+		{
+			UINT32 bufferOffset;
+			UINT32 arraySize;
+		};
+
+		UnorderedMap<String, UniformInfo> uniforms;
+
 		int numUniforms = program->getNumLiveUniformVariables();
 		int numUniforms = program->getNumLiveUniformVariables();
 		for (int i = 0; i < numUniforms; i++)
 		for (int i = 0; i < numUniforms; i++)
 		{
 		{
@@ -445,6 +435,7 @@ namespace bs { namespace ct
 				param.name = name;
 				param.name = name;
 				param.slot = qualifier.layoutBinding;
 				param.slot = qualifier.layoutBinding;
 				param.set = qualifier.layoutSet;
 				param.set = qualifier.layoutSet;
+				param.type = GPOT_UNKNOWN;
 
 
 				if (param.set == glslang::TQualifier::layoutSetEnd)
 				if (param.set == glslang::TQualifier::layoutSetEnd)
 					param.set = 0;
 					param.set = 0;
@@ -515,17 +506,95 @@ namespace bs { namespace ct
 			}
 			}
 			else
 			else
 			{
 			{
-				// We don't parse individual members of shared storage buffers
-				if (qualifier.storage != glslang::EvqUniform)
-					continue;
-
-				if(ttype->getBasicType() == glslang::EbtStruct)
+				if(qualifier.storage == glslang::EvqUniform || qualifier.storage == glslang::EvqGlobal)
 				{
 				{
-					// Not handling structs at the moment
+					UniformInfo info;
+					info.arraySize = program->getUniformArraySize(i);
+					info.bufferOffset = program->getUniformBufferOffset(i);
+
+					uniforms[String(name)] = info;
 				}
 				}
-				else
+			}
+		}
+
+		// Parse uniform blocks
+		int numBlocks = program->getNumLiveUniformBlocks();
+		for (int i = 0; i < numBlocks; i++)
+		{
+			const glslang::TType* ttype = program->getUniformBlockTType(i);
+			const glslang::TQualifier& qualifier = ttype->getQualifier();
+			const char* name = program->getUniformBlockName(i);
+
+			if (!qualifier.hasBinding())
+			{
+				log = "Uniform parsing error: Found a uniform block without a binding qualifier. Each uniform block must "
+					" have an explicitly defined binding number.";
+
+				return false;
+			}
+
+			if(qualifier.storage == glslang::EvqBuffer) // Shared storage buffer
+			{
+				GpuParamObjectDesc param;
+				param.name = name;
+				param.slot = qualifier.layoutBinding;
+				param.set = qualifier.layoutSet;
+
+				if (param.set == glslang::TQualifier::layoutSetEnd)
+					param.set = 0;
+
+				param.type = GPOT_RWSTRUCTURED_BUFFER;
+				desc.buffers[name] = param;
+			}
+			else // Uniform buffer
+			{
+				int size = program->getUniformBlockSize(i);
+
+				GpuParamBlockDesc blockDesc;
+				blockDesc.name = name;
+				blockDesc.blockSize = size / 4;
+				blockDesc.isShareable = true;
+				blockDesc.slot = qualifier.layoutBinding;
+				blockDesc.set = qualifier.layoutSet;
+
+				if (blockDesc.set == glslang::TQualifier::layoutSetEnd)
+					blockDesc.set = 0;
+
+				desc.paramBlocks[name] = blockDesc;
+
+				// Parse members of the uniform buffer
+				const glslang::TTypeList* typeList = ttype->getStruct();
+				if(typeList == nullptr)
+					continue;
+
+				for (auto iter = typeList->begin(); iter != typeList->end(); ++iter)
 				{
 				{
-					GpuParamDataType paramType = mapGLSLangToGpuParamDataType(*ttype);
+					const glslang::TType* paramTType = iter->type;
+					String paramName = paramTType->getFieldName().c_str();
+
+					auto findIter = uniforms.find(paramName);
+					if(findIter == uniforms.end()) // Likely unused and was optimized out
+						continue;
+
+					const UniformInfo& uniformInfo = findIter->second;
+
+					GpuParamDataType paramType;
+					UINT32 elementSize = 0;
+					UINT32 arrayStride = 0;
+					if (paramTType->getBasicType() == glslang::EbtStruct)
+					{
+						paramType = GPDT_STRUCT;
+
+						const glslang::TTypeList* paramTypeList = paramTType->getStruct();
+						parseStruct(paramTypeList, elementSize);
+
+						// Struct alignment always a multiple of vec4
+						arrayStride = Math::divideAndRoundUp(elementSize, 4U) * 4;
+					}
+					else
+					{
+						paramType = mapGLSLangToGpuParamDataType(*paramTType);
+					}
 
 
 					if (paramType == GPDT_UNKNOWN)
 					if (paramType == GPDT_UNKNOWN)
 					{
 					{
@@ -533,27 +602,27 @@ namespace bs { namespace ct
 						continue;
 						continue;
 					}
 					}
 
 
-					int blockIdx = program->getUniformBlockIndex(i);
-					auto iterFind = uniformBlockMap.find(blockIdx);
-					if (iterFind == uniformBlockMap.end())
-						LOGERR("Uniform is referencing a uniform block that doesn't exist: " + String(name));
-
-					const GpuParamBlockDesc& paramBlockDesc = desc.paramBlocks[iterFind->second];
-					const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[paramType];
-					int bufferOffset = program->getUniformBufferOffset(i) / 4;
-
-					GpuParamDataDesc param;
-					param.name = name;
-					param.type = paramType;
-					param.paramBlockSet = paramBlockDesc.set;
-					param.paramBlockSlot = paramBlockDesc.slot;
-					param.elementSize = typeInfo.size / 4;
-					param.arrayElementStride = param.elementSize;
-					param.arraySize = program->getUniformArraySize(i);
-					param.cpuMemOffset = bufferOffset;
-					param.gpuMemOffset = bufferOffset;
-
-					desc.params[name] = param;
+					if (paramType != GPDT_STRUCT)
+					{
+						const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[paramType];
+						elementSize = typeInfo.size / 4;
+						arrayStride = elementSize;
+					}
+
+					int bufferOffset = uniformInfo.bufferOffset / 4;
+
+					GpuParamDataDesc paramDesc;
+					paramDesc.name = paramName;
+					paramDesc.type = paramType;
+					paramDesc.paramBlockSet = blockDesc.set;
+					paramDesc.paramBlockSlot = blockDesc.slot;
+					paramDesc.elementSize = elementSize;
+					paramDesc.arrayElementStride = arrayStride;
+					paramDesc.arraySize = paramTType->isArray() ? paramTType->getCumulativeArraySize() : 1;
+					paramDesc.cpuMemOffset = bufferOffset;
+					paramDesc.gpuMemOffset = bufferOffset;
+
+					desc.params[name] = paramDesc;
 				}
 				}
 			}
 			}
 		}
 		}

+ 2 - 26
Source/BansheeVulkanRenderAPI/BsVulkanRenderAPI.cpp

@@ -19,6 +19,7 @@
 #include "BsVulkanGpuParamBlockBuffer.h"
 #include "BsVulkanGpuParamBlockBuffer.h"
 
 
 #include <vulkan/vulkan.h>
 #include <vulkan/vulkan.h>
+#include "BsVulkanUtility.h"
 
 
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 	#include "Win32/BsWin32VideoModeInfo.h"
 	#include "Win32/BsWin32VideoModeInfo.h"
@@ -590,35 +591,10 @@ namespace bs { namespace ct
 
 
 		for (auto& param : params)
 		for (auto& param : params)
 		{
 		{
-			const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[param.type];
-			UINT32 size = typeInfo.size / 4;
-			UINT32 alignment = typeInfo.alignment / 4;
-
-			// Fix alignment if needed
-			UINT32 alignOffset = block.blockSize % alignment;
-			if (alignOffset != 0)
-			{
-				UINT32 padding = (alignment - alignOffset);
-				block.blockSize += padding;
-			}
+			UINT32 size = VulkanUtility::calcInterfaceBlockElementSizeAndOffset(param.type, param.arraySize, block.blockSize);
 
 
 			if (param.arraySize > 1)
 			if (param.arraySize > 1)
 			{
 			{
-				// Array elements are always padded and aligned to vec4
-				alignOffset = size % typeInfo.baseTypeSize;
-				if (alignOffset != 0)
-				{
-					UINT32 padding = (typeInfo.baseTypeSize - alignOffset);
-					size += padding;
-				}
-
-				alignOffset = block.blockSize % typeInfo.baseTypeSize;
-				if (alignOffset != 0)
-				{
-					UINT32 padding = (typeInfo.baseTypeSize - alignOffset);
-					block.blockSize += padding;
-				}
-
 				param.elementSize = size;
 				param.elementSize = size;
 				param.arrayElementStride = size;
 				param.arrayElementStride = size;
 				param.cpuMemOffset = block.blockSize;
 				param.cpuMemOffset = block.blockSize;

+ 39 - 1
Source/BansheeVulkanRenderAPI/BsVulkanUtility.cpp

@@ -4,6 +4,7 @@
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanDevice.h"
 #include "BsVulkanDevice.h"
 #include "Error/BsException.h"
 #include "Error/BsException.h"
+#include "RenderAPI/BsGpuParams.h"
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
@@ -750,4 +751,41 @@ namespace bs { namespace ct
 
 
 		return false;
 		return false;
 	}
 	}
-}}
+
+	UINT32 VulkanUtility::calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset)
+	{
+		const GpuParamDataTypeInfo& typeInfo = bs::GpuParams::PARAM_SIZES.lookup[type];
+		UINT32 size = (typeInfo.baseTypeSize * typeInfo.numColumns * typeInfo.numRows) / 4;
+		UINT32 alignment = typeInfo.alignment / 4;
+
+		// Fix alignment if needed
+		UINT32 alignOffset = offset % alignment;
+		if (alignOffset != 0)
+		{
+			UINT32 padding = (alignment - alignOffset);
+			offset += padding;
+		}
+
+		if (arraySize > 1)
+		{
+			// Array elements are always padded and aligned to vec4
+			alignOffset = size % 4;
+			if (alignOffset != 0)
+			{
+				UINT32 padding = (4 - alignOffset);
+				size += padding;
+			}
+
+			alignOffset = offset % 4;
+			if (alignOffset != 0)
+			{
+				UINT32 padding = (4 - alignOffset);
+				offset += padding;
+			}
+
+			return size;
+		}
+		else
+			return size;
+	}
+}}

+ 12 - 0
Source/BansheeVulkanRenderAPI/BsVulkanUtility.h

@@ -91,6 +91,18 @@ namespace bs { namespace ct
 
 
 		/** Checks if the two image subresource ranges have any overlapping subresources. */
 		/** Checks if the two image subresource ranges have any overlapping subresources. */
 		static bool rangeOverlaps(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b);
 		static bool rangeOverlaps(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b);
+
+		/**
+		 * Calculates the size and alignment of a single element within a shader interface block using the std140 layout.
+		 *
+		 * @param[in]		type		Type of the element. Structs are not supported.
+		 * @param[in]		arraySize	Number of array elements of the element (1 if it's not an array).
+		 * @param[in, out]	offset		Current location in some parent buffer at which the element should be placed at. If the
+		 *								location doesn't match the element's alignment, the value will be modified to a valid
+		 *								alignment. In multiples of 4 bytes.
+		 * @return						Size of the element, in multiples of 4 bytes.
+		 */
+		static UINT32 calcInterfaceBlockElementSizeAndOffset(GpuParamDataType type, UINT32 arraySize, UINT32& offset);
 	};
 	};
 
 
 	/** @} */
 	/** @} */