Browse Source

vulkan: fix uniform vec3 array alignment

Sasha Szpakowski 1 year ago
parent
commit
e08004d338

+ 37 - 0
src/modules/graphics/Shader.cpp

@@ -1478,6 +1478,43 @@ void Shader::handleUnknownUniformName(const char */*name*/)
 	// TODO: do something here?
 }
 
+void Shader::copyToUniformBuffer(const UniformInfo *info, const void *src, void *dst, int count) const
+{
+	count = std::min(count, info->count);
+
+	size_t elementsize = info->components * 4;
+	if (info->baseType == UNIFORM_MATRIX)
+		elementsize = info->matrix.columns * info->matrix.rows * 4;
+
+	// Assuming std140 packing rules, the source data can only be direct-copied
+	// to the uniform buffer in certain cases because it's tightly packed whereas
+	// the buffer's data isn't.
+	if (elementsize * info->count == info->dataSize || (count == 1 && info->baseType != UNIFORM_MATRIX))
+	{
+		memcpy(dst, src, elementsize * count);
+	}
+	else
+	{
+		int veccount = count;
+		int comp = info->components;
+
+		if (info->baseType == UNIFORM_MATRIX)
+		{
+			veccount *= info->matrix.rows;
+			comp = info->matrix.columns;
+		}
+
+		const int *isrc = (const int *) src;
+		int *idst = (int *) dst;
+
+		for (int i = 0; i < veccount; i++)
+		{
+			for (int c = 0; c < comp; c++)
+				idst[i * 4 + c] = isrc[i * comp + c];
+		}
+	}
+}
+
 bool Shader::initialize()
 {
 	bool success = glslang::InitializeProcess();

+ 3 - 0
src/modules/graphics/Shader.h

@@ -307,6 +307,9 @@ protected:
 
 	void handleUnknownUniformName(const char *name);
 
+	// std140 uniform buffer alignment-aware copy.
+	void copyToUniformBuffer(const UniformInfo *info, const void *src, void *dst, int count) const;
+
 	static std::string canonicaliizeUniformName(const std::string &name);
 	static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection);
 	static DataBaseType getDataBaseType(PixelFormat format);

+ 2 - 32
src/modules/graphics/metal/Shader.mm

@@ -708,40 +708,10 @@ void Shader::updateUniform(const UniformInfo *info, int count)
 
 	count = std::min(count, info->count);
 
-	// TODO: store some of this in UniformInfo.
-	size_t elementsize = info->components * 4;
-	if (info->baseType == UNIFORM_MATRIX)
-		elementsize = info->matrix.columns * info->matrix.rows * 4;
-
 	size_t offset = (const uint8 *)info->data - localUniformStagingData;
+	uint8 *dst = localUniformBufferData + offset;
 
-	// Assuming std140 packing rules, the source data can only be direct-copied
-	// to the uniform buffer in certain cases because it's tightly packed whereas
-	// the buffer's data isn't.
-	if (elementsize * info->count == info->dataSize || (count == 1 && info->baseType != UNIFORM_MATRIX))
-	{
-		memcpy(localUniformBufferData + offset, info->data, elementsize * count);
-	}
-	else
-	{
-		int veccount = count;
-		int comp = info->components;
-
-		if (info->baseType == UNIFORM_MATRIX)
-		{
-			veccount *= info->matrix.rows;
-			comp = info->matrix.columns;
-		}
-
-		const int *src = info->ints;
-		int *dst = (int *) (localUniformBufferData + offset);
-
-		for (int i = 0; i < veccount; i++)
-		{
-			for (int c = 0; c < comp; c++)
-				dst[i * 4 + c] = src[i * comp + c];
-		}
-	}
+	copyToUniformBuffer(info, info->data, dst, count);
 }
 
 void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count)

+ 13 - 4
src/modules/graphics/vulkan/Shader.cpp

@@ -358,8 +358,14 @@ void Shader::updateUniform(const UniformInfo *info, int count)
 	if (current == this)
 		Graphics::flushBatchedDrawsGlobal();
 
-	if (usesLocalUniformData(info))
-		memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
+	count = std::min(count, info->count);
+
+	if (info->data != nullptr)
+	{
+		size_t offset = (const uint8*)info->data - localUniformStagingData.data();
+		uint8 *dst = localUniformData.data() + offset;
+		copyToUniformBuffer(info, info->data, dst, count);
+	}
 }
 
 void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, int count)
@@ -453,10 +459,15 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross::
 		{
 			const auto &values = valuesit->second;
 			if (!values.empty())
+			{
 				memcpy(
 					u.data,
 					values.data(),
 					std::min(u.dataSize, values.size() * sizeof(LocalUniformValue)));
+
+				uint8 *dst = localUniformData.data() + offset;
+				copyToUniformBuffer(&u, u.data, dst, u.count);
+			}
 		}
 
 		BuiltinUniform builtin = BUILTIN_MAX_ENUM;
@@ -588,8 +599,6 @@ void Shader::compileShaders()
 
 				std::string basename("");
 				buildLocalUniforms(comp, type, 0, basename);
-
-				memcpy(localUniformData.data(), localUniformStagingData.data(), localUniformStagingData.size());
 			}
 			else
 				throw love::Exception("unimplemented: non default uniform blocks.");