From e08004d33835895f71d01be024964d194b6a9154 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Mon, 1 Apr 2024 23:06:19 -0300 Subject: [PATCH] vulkan: fix uniform vec3 array alignment --- src/modules/graphics/Shader.cpp | 37 ++++++++++++++++++++++++++ src/modules/graphics/Shader.h | 3 +++ src/modules/graphics/metal/Shader.mm | 34 ++--------------------- src/modules/graphics/vulkan/Shader.cpp | 17 +++++++++--- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index c3839a421..6f4bee6fe 100644 --- a/src/modules/graphics/Shader.cpp +++ b/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(); diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index abe57be38..be4e577b5 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -307,6 +307,9 @@ class Shader : public Object, public Resource 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 stages[], std::string& err, Reflection &reflection); static DataBaseType getDataBaseType(PixelFormat format); diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 3b6dedb48..b188c336e 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -708,40 +708,10 @@ static EShLanguage getGLSLangStage(ShaderStageType stage) 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) diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 276010ecd..ee5346771 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/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.");