From 2079f657e7deaa1f2ba7924af798482cc7a3b0d9 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Thu, 7 Mar 2024 21:24:11 -0400 Subject: [PATCH] graphics: most uniform reflection is handled at a higher level instead of backend code. Fixes boolean uniforms when Metal is used. --- src/modules/graphics/Shader.cpp | 307 ++++++++--- src/modules/graphics/Shader.h | 60 +-- src/modules/graphics/ShaderStage.h | 8 + src/modules/graphics/metal/Shader.h | 5 +- src/modules/graphics/metal/Shader.mm | 264 ++++------ src/modules/graphics/opengl/Shader.cpp | 688 ++++--------------------- src/modules/graphics/opengl/Shader.h | 9 - src/modules/graphics/vulkan/Shader.cpp | 283 ++++------ src/modules/graphics/vulkan/Shader.h | 4 - src/modules/graphics/wrap_Shader.cpp | 11 +- 10 files changed, 560 insertions(+), 1079 deletions(-) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index cfe9e1a6c..636750d90 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -691,9 +691,48 @@ Shader::Shader(StrongRef _stages[], const CompileOptions &options) , debugName(options.debugName) { std::string err; - if (!validateInternal(_stages, err, validationReflection)) + if (!validateInternal(_stages, err, reflection)) throw love::Exception("%s", err.c_str()); + activeTextures.resize(reflection.textureCount); + activeBuffers.resize(reflection.bufferCount); + + auto gfx = Module::getInstance(Module::M_GRAPHICS); + + // Default bindings for read-only resources. + for (const auto &kvp : reflection.allUniforms) + { + const auto &u = *kvp.second; + + if (u.resourceIndex < 0) + continue; + + if ((u.access & ACCESS_WRITE) != 0) + continue; + + if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE) + { + auto tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType); + for (int i = 0; i < u.count; i++) + { + tex->retain(); + activeTextures[u.resourceIndex + i] = tex; + } + } + else if (u.baseType == UNIFORM_TEXELBUFFER || u.baseType == UNIFORM_STORAGEBUFFER) + { + auto buffer = u.baseType == UNIFORM_TEXELBUFFER + ? gfx->getDefaultTexelBuffer(u.dataBaseType) + : gfx->getDefaultStorageBuffer(); + + for (int i = 0; i < u.count; i++) + { + buffer->retain(); + activeBuffers[u.resourceIndex + i] = buffer; + } + } + } + for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) stages[i] = _stages[i]; } @@ -708,6 +747,18 @@ Shader::~Shader() if (current == this) attachDefault(STANDARD_DEFAULT); + + for (Texture *tex : activeTextures) + { + if (tex) + tex->release(); + } + + for (Buffer *buffer : activeBuffers) + { + if (buffer) + buffer->release(); + } } bool Shader::hasStage(ShaderStageType stage) @@ -740,6 +791,18 @@ bool Shader::isDefaultActive() return false; } +const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const +{ + const auto it = reflection.allUniforms.find(name); + return it != reflection.allUniforms.end() ? it->second : nullptr; +} + +bool Shader::hasUniform(const std::string &name) const +{ + const auto it = reflection.allUniforms.find(name); + return it != reflection.allUniforms.end() && it->second->active; +} + const Shader::UniformInfo *Shader::getMainTextureInfo() const { return getUniformInfo(BUILTIN_TEXTURE_MAIN); @@ -781,9 +844,9 @@ bool Shader::isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b) void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const { - if ((primtype == PRIMITIVE_POINTS) != validationReflection.usesPointSize) + if ((primtype == PRIMITIVE_POINTS) != reflection.usesPointSize) { - if (validationReflection.usesPointSize) + if (reflection.usesPointSize) throw love::Exception("The active shader can only be used to draw points."); else throw love::Exception("The gl_PointSize variable must be set in a vertex shader when drawing points."); @@ -825,17 +888,29 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const void Shader::getLocalThreadgroupSize(int *x, int *y, int *z) { - *x = validationReflection.localThreadgroupSize[0]; - *y = validationReflection.localThreadgroupSize[1]; - *z = validationReflection.localThreadgroupSize[2]; + *x = reflection.localThreadgroupSize[0]; + *y = reflection.localThreadgroupSize[1]; + *z = reflection.localThreadgroupSize[2]; } bool Shader::validate(StrongRef stages[], std::string& err) { - ValidationReflection reflection; + Reflection reflection; return validateInternal(stages, err, reflection); } +static DataBaseType getBaseType(glslang::TBasicType basictype) +{ + switch (basictype) + { + case glslang::EbtInt: return DATA_BASETYPE_INT; + case glslang::EbtUint: return DATA_BASETYPE_UINT; + case glslang::EbtFloat: return DATA_BASETYPE_FLOAT; + case glslang::EbtBool: return DATA_BASETYPE_BOOL; + default: return DATA_BASETYPE_FLOAT; + } +} + static PixelFormat getPixelFormat(glslang::TLayoutFormat format) { using namespace glslang; @@ -885,6 +960,30 @@ static PixelFormat getPixelFormat(glslang::TLayoutFormat format) } } +static TextureType getTextureType(const glslang::TSampler &sampler) +{ + if (sampler.is2D()) + return sampler.isArrayed() ? TEXTURE_2D_ARRAY : TEXTURE_2D; + else if (sampler.dim == glslang::EsdCube) + return sampler.isArrayed() ? TEXTURE_MAX_ENUM : TEXTURE_CUBE; + else if (sampler.dim == glslang::Esd3D) + return TEXTURE_VOLUME; + else + return TEXTURE_MAX_ENUM; +} + +static uint32 getStageMask(EShLanguageMask mask) +{ + uint32 m = 0; + if (mask & EShLangVertexMask) + m |= SHADERSTAGEMASK_VERTEX; + if (mask & EShLangFragmentMask) + m |= SHADERSTAGEMASK_PIXEL; + if (mask & EShLangComputeMask) + m |= SHADERSTAGEMASK_COMPUTE; + return m; +} + template static T convertData(const glslang::TConstUnion &data) { @@ -903,7 +1002,7 @@ static T convertData(const glslang::TConstUnion &data) } } -bool Shader::validateInternal(StrongRef stages[], std::string &err, ValidationReflection &reflection) +bool Shader::validateInternal(StrongRef stages[], std::string &err, Reflection &reflection) { glslang::TProgram program; @@ -946,6 +1045,9 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, } } + reflection.textureCount = 0; + reflection.bufferCount = 0; + for (int i = 0; i < program.getNumUniformVariables(); i++) { const glslang::TObjectReflection &info = program.getUniform(i); @@ -955,9 +1057,40 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, const glslang::TQualifier &qualifiers = type->getQualifier(); - if (type->isImage()) + UniformInfo u = {}; + + u.name = canonicaliizeUniformName(info.name); + u.location = -1; + u.access = ACCESS_READ; + u.stageMask = getStageMask(info.stages); + u.components = 1; + u.resourceIndex = -1; + + if (type->isSizedArray()) + u.count = type->getArraySizes()->getCumulativeSize(); + else + u.count = 1; + + const auto &sampler = type->getSampler(); + + if (type->isTexture() && type->getSampler().isCombined()) { - if ((info.stages & EShLangComputeMask) == 0) + u.baseType = UNIFORM_SAMPLER; + u.dataBaseType = getBaseType(sampler.getBasicType()); + u.isDepthSampler = sampler.isShadow(); + u.textureType = getTextureType(sampler); + + if (u.textureType == TEXTURE_MAX_ENUM) + continue; + + u.resourceIndex = reflection.textureCount; + reflection.textureCount += u.count; + + reflection.sampledTextures[u.name] = u; + } + else if (type->isImage()) + { + if ((info.stages & (~EShLangComputeMask)) != 0) { err = "Shader validation error:\nStorage Texture uniform variables (image2D, etc) are only allowed in compute shaders."; return false; @@ -965,36 +1098,63 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, if (!qualifiers.hasFormat()) { - err = "Shader validation error:\nStorage Texture '" + info.name + "' must have an explicit format set in its layout declaration."; + err = "Shader validation error:\nStorage Texture '" + u.name + "' must have an explicit format set in its layout declaration."; return false; } - StorageTextureReflection texreflection = {}; + u.baseType = UNIFORM_STORAGETEXTURE; + u.storageTextureFormat = getPixelFormat(qualifiers.getFormat()); + u.dataBaseType = getDataBaseType(u.storageTextureFormat); + u.textureType = getTextureType(sampler); - texreflection.format = getPixelFormat(qualifiers.getFormat()); + if (u.textureType == TEXTURE_MAX_ENUM) + continue; + + u.resourceIndex = reflection.textureCount; + reflection.textureCount += u.count; if (qualifiers.isReadOnly()) - texreflection.access = ACCESS_READ; + u.access = ACCESS_READ; else if (qualifiers.isWriteOnly()) - texreflection.access = ACCESS_WRITE; + u.access = ACCESS_WRITE; else - texreflection.access = (Access)(ACCESS_READ | ACCESS_WRITE); + u.access = (Access)(ACCESS_READ | ACCESS_WRITE); - reflection.storageTextures[info.name] = texreflection; + reflection.storageTextures[u.name] = u; + } + else if (type->getBasicType() == glslang::EbtSampler && type->getSampler().isBuffer()) + { + u.baseType = UNIFORM_TEXELBUFFER; + u.dataBaseType = getBaseType(sampler.getBasicType()); + + u.resourceIndex = reflection.bufferCount; + reflection.bufferCount += u.count; + + reflection.texelBuffers[u.name] = u; } else if (!type->isOpaque()) { - LocalUniform u = {}; - auto &values = u.initializerValues; + std::vector values; const glslang::TConstUnionArray *constarray = info.getConstArray(); + if (type->isMatrix()) + { + u.matrix.rows = type->getMatrixRows(); + u.matrix.columns = type->getMatrixCols(); + } + else + { + u.components = type->getVectorSize(); + } + // Store initializer values for local uniforms. Some love graphics // backends strip these out of the shader so we need to be able to // access them (to re-send them) by getting them here. switch (type->getBasicType()) { case glslang::EbtFloat: - u.dataType = DATA_BASETYPE_FLOAT; + u.baseType = type->isMatrix() ? UNIFORM_MATRIX : UNIFORM_FLOAT; + u.dataBaseType = DATA_BASETYPE_FLOAT; if (constarray != nullptr) { values.resize(constarray->size()); @@ -1003,7 +1163,8 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, } break; case glslang::EbtUint: - u.dataType = DATA_BASETYPE_UINT; + u.baseType = UNIFORM_UINT; + u.dataBaseType = DATA_BASETYPE_UINT; if (constarray != nullptr) { values.resize(constarray->size()); @@ -1012,7 +1173,8 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, } break; case glslang::EbtBool: - u.dataType = DATA_BASETYPE_BOOL; + u.baseType = UNIFORM_BOOL; + u.dataBaseType = DATA_BASETYPE_BOOL; if (constarray != nullptr) { values.resize(constarray->size()); @@ -1022,7 +1184,8 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, break; case glslang::EbtInt: default: - u.dataType = DATA_BASETYPE_INT; + u.baseType = UNIFORM_INT; + u.dataBaseType = DATA_BASETYPE_INT; if (constarray != nullptr) { values.resize(constarray->size()); @@ -1032,7 +1195,8 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, break; } - reflection.localUniforms[info.name] = u; + reflection.localUniforms[u.name] = u; + reflection.localUniformInitializerValues[u.name] = values; } } @@ -1070,18 +1234,29 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, return false; } - BufferReflection bufferReflection = {}; - bufferReflection.stride = (size_t) info.size; - bufferReflection.memberCount = (size_t) info.numMembers; + UniformInfo u = {}; + u.name = canonicaliizeUniformName(info.name); + u.location = -1; + + if (type->isSizedArray()) + u.count = type->getArraySizes()->getCumulativeSize(); + else + u.count = 1; + + u.bufferStride = (size_t) info.size; + u.bufferMemberCount = (size_t) info.numMembers; + + u.resourceIndex = reflection.bufferCount; + reflection.bufferCount += u.count; if (qualifiers.isReadOnly()) - bufferReflection.access = ACCESS_READ; + u.access = ACCESS_READ; else if (qualifiers.isWriteOnly()) - bufferReflection.access = ACCESS_WRITE; + u.access = ACCESS_WRITE; else - bufferReflection.access = (Access)(ACCESS_READ | ACCESS_WRITE); + u.access = (Access)(ACCESS_READ | ACCESS_WRITE); - reflection.storageBuffers[info.name] = bufferReflection; + reflection.storageBuffers[u.name] = u; } else { @@ -1090,6 +1265,21 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, } } + for (auto &kvp : reflection.texelBuffers) + reflection.allUniforms[kvp.first] = &kvp.second; + + for (auto &kvp : reflection.storageBuffers) + reflection.allUniforms[kvp.first] = &kvp.second; + + for (auto &kvp : reflection.sampledTextures) + reflection.allUniforms[kvp.first] = &kvp.second; + + for (auto &kvp : reflection.storageTextures) + reflection.allUniforms[kvp.first] = &kvp.second; + + for (auto &kvp : reflection.localUniforms) + reflection.allUniforms[kvp.first] = &kvp.second; + return true; } @@ -1216,41 +1406,6 @@ bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool intern return true; } -bool Shader::fillUniformReflectionData(UniformInfo &u) -{ - const auto &r = validationReflection; - - if (u.baseType == UNIFORM_STORAGETEXTURE) - { - const auto reflectionit = r.storageTextures.find(u.name); - if (reflectionit != r.storageTextures.end()) - { - u.storageTextureFormat = reflectionit->second.format; - u.access = reflectionit->second.access; - return true; - } - - // No reflection info - maybe glslang was better at detecting dead code - // than the driver's compiler? - return false; - } - else if (u.baseType == UNIFORM_STORAGEBUFFER) - { - const auto reflectionit = r.storageBuffers.find(u.name); - if (reflectionit != r.storageBuffers.end()) - { - u.bufferStride = reflectionit->second.stride; - u.bufferMemberCount = reflectionit->second.memberCount; - u.access = reflectionit->second.access; - return true; - } - - return false; - } - - return true; -} - std::string Shader::getShaderStageDebugName(ShaderStageType stage) const { std::string name = debugName; @@ -1265,6 +1420,26 @@ std::string Shader::getShaderStageDebugName(ShaderStageType stage) const return name; } +std::string Shader::canonicaliizeUniformName(const std::string &n) +{ + std::string name(n); + + // Some drivers/compilers append "[0]" to the end of array uniform names. + if (name.length() > 3) + { + size_t findpos = name.rfind("[0]"); + if (findpos != std::string::npos && findpos == name.length() - 3) + name.erase(name.length() - 3); + } + + return name; +} + +void Shader::handleUnknownUniformName(const char */*name*/) +{ + // TODO: do something here? +} + bool Shader::initialize() { return glslang::InitializeProcess(); diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index d9e53999b..51b538fd7 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -129,6 +129,10 @@ public: struct UniformInfo { + UniformType baseType; + uint32 stageMask; + bool active; + int location; int count; @@ -138,7 +142,6 @@ public: MatrixSize matrix; }; - UniformType baseType; DataBaseType dataBaseType; TextureType textureType; Access access; @@ -148,6 +151,8 @@ public: size_t bufferMemberCount; std::string name; + int resourceIndex; + union { void *data; @@ -157,12 +162,6 @@ public: }; size_t dataSize; - - union - { - Texture **textures; - Buffer **buffers; - }; }; union LocalUniformValue @@ -222,7 +221,7 @@ public: virtual int getVertexAttributeIndex(const std::string &name) = 0; - virtual const UniformInfo *getUniformInfo(const std::string &name) const = 0; + const UniformInfo *getUniformInfo(const std::string &name) const; virtual const UniformInfo *getUniformInfo(BuiltinUniform builtin) const = 0; virtual void updateUniform(const UniformInfo *info, int count) = 0; @@ -234,7 +233,7 @@ public: * Gets whether a uniform with the specified name exists and is actively * used in the shader. **/ - virtual bool hasUniform(const std::string &name) const = 0; + bool hasUniform(const std::string &name) const; /** * Sets the textures used when rendering a video. For internal use only. @@ -264,39 +263,31 @@ public: protected: - struct BufferReflection + struct Reflection { - size_t stride; - size_t memberCount; - Access access; - }; + std::map texelBuffers; + std::map storageBuffers; + std::map sampledTextures; + std::map storageTextures; + std::map localUniforms; - struct StorageTextureReflection - { - PixelFormat format; - Access access; - }; + std::map allUniforms; - struct LocalUniform - { - DataBaseType dataType; - std::vector initializerValues; - }; + std::map> localUniformInitializerValues; + + int textureCount; + int bufferCount; - struct ValidationReflection - { - std::map storageBuffers; - std::map storageTextures; - std::map localUniforms; int localThreadgroupSize[3]; bool usesPointSize; }; - bool fillUniformReflectionData(UniformInfo &u); - std::string getShaderStageDebugName(ShaderStageType stage) const; - static bool validateInternal(StrongRef stages[], std::string& err, ValidationReflection &reflection); + void handleUnknownUniformName(const char *name); + + static std::string canonicaliizeUniformName(const std::string &name); + static bool validateInternal(StrongRef stages[], std::string& err, Reflection &reflection); static DataBaseType getDataBaseType(PixelFormat format); static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b); @@ -305,7 +296,10 @@ protected: StrongRef stages[SHADERSTAGE_MAX_ENUM]; - ValidationReflection validationReflection; + Reflection reflection; + + std::vector activeTextures; + std::vector activeBuffers; std::string debugName; diff --git a/src/modules/graphics/ShaderStage.h b/src/modules/graphics/ShaderStage.h index 69214a3a8..fda4e5b9c 100644 --- a/src/modules/graphics/ShaderStage.h +++ b/src/modules/graphics/ShaderStage.h @@ -48,6 +48,14 @@ enum ShaderStageType SHADERSTAGE_MAX_ENUM }; +enum ShaderStageMask +{ + SHADERSTAGEMASK_NONE = 0, + SHADERSTAGEMASK_VERTEX = 1 << SHADERSTAGE_VERTEX, + SHADERSTAGEMASK_PIXEL = 1 << SHADERSTAGE_PIXEL, + SHADERSTAGEMASK_COMPUTE = 1 << SHADERSTAGE_COMPUTE, +}; + class ShaderStage : public love::Object { public: diff --git a/src/modules/graphics/metal/Shader.h b/src/modules/graphics/metal/Shader.h index 1e517a950..93ceec7af 100644 --- a/src/modules/graphics/metal/Shader.h +++ b/src/modules/graphics/metal/Shader.h @@ -107,12 +107,10 @@ public: void attach() override; std::string getWarnings() const override { return ""; } int getVertexAttributeIndex(const std::string &name) override; - const UniformInfo *getUniformInfo(const std::string &name) const override; const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override; void updateUniform(const UniformInfo *info, int count) override; void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override; void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override; - bool hasUniform(const std::string &name) const override; ptrdiff_t getHandle() const override { return 0; } void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override; @@ -140,13 +138,12 @@ private: }; void buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv_cross::SPIRType &type, size_t baseoffset, const std::string &basename); - void addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType); + void addImage(const spirv_cross::Resource &resource); void compileFromGLSLang(id device, const glslang::TProgram &program); id functions[SHADERSTAGE_MAX_ENUM]; UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM]; - std::map uniforms; uint8 *localUniformStagingData; uint8 *localUniformBufferData; diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 5ed42deb2..5379e1be6 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -299,132 +299,69 @@ void Shader::buildLocalUniforms(const spirv_cross::CompilerMSL &msl, const spirv continue; } + name = canonicaliizeUniformName(name); + if (offset + membersize > localUniformBufferSize) throw love::Exception("Invalid uniform offset + size for '%s' (offset=%d, size=%d, buffer size=%d)", name.c_str(), (int)offset, (int)membersize, (int)localUniformBufferSize); - UniformInfo u = {}; - u.name = name; + auto uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) + { + handleUnknownUniformName(name.c_str()); + continue; + } + + UniformInfo &u = *(uniformit->second); + u.active = true; + + if (u.dataSize > 0) + continue; + u.dataSize = membersize; - u.count = membertype.array.empty() ? 1 : membertype.array[0]; - u.components = 1; - u.data = localUniformStagingData + offset; - if (membertype.columns == 1) - { - if (membertype.basetype == SPIRType::Int) - u.baseType = UNIFORM_INT; - else if (membertype.basetype == SPIRType::UInt) - u.baseType = UNIFORM_UINT; - else - u.baseType = UNIFORM_FLOAT; - u.components = membertype.vecsize; - } - else - { - u.baseType = UNIFORM_MATRIX; - u.matrix.rows = membertype.vecsize; - u.matrix.columns = membertype.columns; - } - const auto &reflectionit = validationReflection.localUniforms.find(u.name); - if (reflectionit != validationReflection.localUniforms.end()) + const auto &reflectionit = reflection.localUniformInitializerValues.find(u.name); + if (reflectionit != reflection.localUniformInitializerValues.end()) { - const auto &localuniform = reflectionit->second; - const auto &values = localuniform.initializerValues; + const auto &values = reflectionit->second; if (!values.empty()) memcpy(u.data, values.data(), std::min(u.dataSize, values.size() * sizeof(LocalUniformValue))); } - uniforms[u.name] = u; - BuiltinUniform builtin = BUILTIN_MAX_ENUM; if (getConstant(u.name.c_str(), builtin)) { if (builtin == BUILTIN_UNIFORMS_PER_DRAW) builtinUniformDataOffset = offset; - builtinUniformInfo[builtin] = &uniforms[u.name]; + builtinUniformInfo[builtin] = &u; } updateUniform(&u, u.count); } } -void Shader::addImage(const spirv_cross::CompilerMSL &msl, const spirv_cross::Resource &resource, UniformType baseType) +void Shader::addImage(const spirv_cross::Resource &resource) { using namespace spirv_cross; - const SPIRType &basetype = msl.get_type(resource.base_type_id); - const SPIRType &type = msl.get_type(resource.type_id); - const SPIRType &imagetype = msl.get_type(basetype.image.type); - - UniformInfo u = {}; - u.baseType = baseType; - u.name = resource.name; - u.count = type.array.empty() ? 1 : type.array[0]; - u.isDepthSampler = type.image.depth; - u.components = 1; - - auto it = uniforms.find(u.name); - if (it != uniforms.end()) + std::string name = canonicaliizeUniformName(resource.name); + auto uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) return; - if (!fillUniformReflectionData(u)) - return; + UniformInfo &u = *(uniformit->second); - switch (imagetype.basetype) + if (u.dataSize == 0) { - case SPIRType::Float: - u.dataBaseType = DATA_BASETYPE_FLOAT; - break; - case SPIRType::Int: - u.dataBaseType = DATA_BASETYPE_INT; - break; - case SPIRType::UInt: - u.dataBaseType = DATA_BASETYPE_UINT; - break; - default: - break; + u.dataSize = sizeof(int) * u.count; + u.data = malloc(u.dataSize); + for (int i = 0; i < u.count; i++) + u.ints[i] = -1; // Initialized below, after compiling. } - switch (basetype.image.dim) - { - case spv::Dim2D: - u.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D; - u.textures = new love::graphics::Texture*[u.count]; - memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count); - break; - case spv::Dim3D: - u.textureType = TEXTURE_VOLUME; - u.textures = new love::graphics::Texture*[u.count]; - memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count); - break; - case spv::DimCube: - if (basetype.image.arrayed) - throw love::Exception("Cubemap Arrays are not currently supported."); - u.textureType = TEXTURE_CUBE; - u.textures = new love::graphics::Texture*[u.count]; - memset(u.textures, 0, sizeof(love::graphics::Texture *) * u.count); - break; - case spv::DimBuffer: - u.baseType = UNIFORM_TEXELBUFFER; - u.buffers = new love::graphics::Buffer*[u.count]; - memset(u.buffers, 0, sizeof(love::graphics::Buffer *) * u.count); - break; - default: - // TODO: error? continue? - break; - } - - u.dataSize = sizeof(int) * u.count; - u.data = malloc(u.dataSize); - for (int i = 0; i < u.count; i++) - u.ints[i] = -1; // Initialized below, after compiling. - - uniforms[u.name] = u; - BuiltinUniform builtin; - if (getConstant(resource.name.c_str(), builtin)) - builtinUniformInfo[builtin] = &uniforms[u.name]; + if (getConstant(name.c_str(), builtin)) + builtinUniformInfo[builtin] = &u; } void Shader::compileFromGLSLang(id device, const glslang::TProgram &program) @@ -473,19 +410,18 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p auto &msl = *mslpointer; auto interfacevars = msl.get_active_interface_variables(); + ShaderResources resources = msl.get_shader_resources(interfacevars); msl.set_enabled_interface_variables(interfacevars); - ShaderResources resources = msl.get_shader_resources(); - for (const auto &resource : resources.storage_images) { - addImage(msl, resource, UNIFORM_STORAGETEXTURE); + addImage(resource); } for (const auto &resource : resources.sampled_images) { - addImage(msl, resource, UNIFORM_SAMPLER); + addImage(resource); } for (const auto &resource : resources.uniform_buffers) @@ -536,32 +472,24 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p binding.msl_buffer = metalBufferIndices[stageindex]++; msl.add_msl_resource_binding(binding); - auto it = uniforms.find(resource.name); - if (it != uniforms.end()) + std::string name = canonicaliizeUniformName(resource.name); + auto uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) + { + handleUnknownUniformName(name.c_str()); + continue; + } + + UniformInfo &u = *(uniformit->second); + + if (u.dataSize > 0) continue; - const SPIRType &type = msl.get_type(resource.type_id); - - UniformInfo u = {}; - u.baseType = UNIFORM_STORAGEBUFFER; - u.components = 1; - u.name = resource.name; - u.count = type.array.empty() ? 1 : type.array[0]; - - if (!fillUniformReflectionData(u)) - continue; - - u.buffers = new love::graphics::Buffer*[u.count]; u.dataSize = sizeof(int) * u.count; u.data = malloc(u.dataSize); for (int i = 0; i < u.count; i++) - { u.ints[i] = -1; // Initialized below, after compiling. - u.buffers[i] = nullptr; - } - - uniforms[u.name] = u; } if (stageindex == SHADERSTAGE_VERTEX) @@ -634,7 +562,8 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p if (library == nil && err != nil) { NSLog(@"errors: %@", err); - throw love::Exception("Error compiling converted Metal shader code"); + NSString *errorstr = err.localizedDescription; + throw love::Exception("Error compiling converted Metal shader code:\n\n%s", errorstr.UTF8String); } functions[stageindex] = [library newFunctionWithName:library.functionNames[0]]; @@ -645,11 +574,12 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p auto setTextureBinding = [this](CompilerMSL &msl, int stageindex, const spirv_cross::Resource &resource) -> void { - auto it = uniforms.find(resource.name); - if (it == uniforms.end()) + std::string name = canonicaliizeUniformName(resource.name); + auto it = reflection.allUniforms.find(name); + if (it == reflection.allUniforms.end()) return; - UniformInfo &u = it->second; + UniformInfo &u = *(it->second); uint32 texturebinding = msl.get_automatic_msl_resource_binding(resource.id); uint32 samplerbinding = msl.get_automatic_msl_resource_binding_secondary(resource.id); @@ -657,10 +587,11 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p if (texturebinding == (uint32)-1) { // No valid binding, the uniform was likely optimized out because it's not used. - uniforms.erase(resource.name); return; } + u.active = true; + for (int i = 0; i < u.count; i++) { if (u.ints[i] == -1) @@ -702,8 +633,9 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p for (const auto &resource : resources.storage_buffers) { - auto it = uniforms.find(resource.name); - if (it == uniforms.end()) + std::string name = canonicaliizeUniformName(resource.name); + auto it = reflection.storageBuffers.find(name); + if (it == reflection.storageBuffers.end()) continue; UniformInfo &u = it->second; @@ -712,10 +644,11 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p if (bufferbinding == (uint32)-1) { // No valid binding, the uniform was likely optimized out because it's not used. - uniforms.erase(resource.name); continue; } + u.active = true; + for (int i = 0; i < u.count; i++) { if (u.ints[i] == -1) @@ -736,18 +669,18 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p } // Initialize default resource bindings. - for (auto &kvp : uniforms) + for (const auto &kvp : reflection.allUniforms) { - UniformInfo &info = kvp.second; - switch (info.baseType) + const UniformInfo *info = kvp.second; + switch (info->baseType) { case UNIFORM_SAMPLER: case UNIFORM_STORAGETEXTURE: - sendTextures(&info, info.textures, info.count); + sendTextures(info, &activeTextures[info->resourceIndex], info->count); break; case UNIFORM_TEXELBUFFER: case UNIFORM_STORAGEBUFFER: - sendBuffers(&info, info.buffers, info.count); + sendBuffers(info, &activeBuffers[info->resourceIndex], info->count); break; default: break; @@ -767,29 +700,13 @@ Shader::~Shader() cachedRenderPipelines.clear(); - for (const auto &it : uniforms) + for (const auto &it : reflection.allUniforms) { - const auto &u = it.second; + const auto &u = *(it.second); if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE) - { free(u.data); - for (int i = 0; i < u.count; i++) - { - if (u.textures[i] != nullptr) - u.textures[i]->release(); - } - delete[] u.textures; - } else if (u.baseType == UNIFORM_TEXELBUFFER || u.baseType == UNIFORM_STORAGEBUFFER) - { free(u.data); - for (int i = 0; i < u.count; i++) - { - if (u.buffers[i] != nullptr) - u.buffers[i]->release(); - } - delete[] u.buffers; - } } delete[] localUniformStagingData; @@ -813,12 +730,6 @@ int Shader::getVertexAttributeIndex(const std::string &name) return it != attributes.end() ? it->second : -1; } -const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const -{ - const auto it = uniforms.find(name); - return it != uniforms.end() ? &(it->second) : nullptr; -} - const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const { return builtinUniformInfo[(int)builtin]; @@ -826,6 +737,9 @@ const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const void Shader::updateUniform(const UniformInfo *info, int count) { + if (info->dataSize == 0) + return; + if (current == this) Graphics::flushBatchedDrawsGlobal(); @@ -895,12 +809,21 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex tex->retain(); - if (info->textures[i] != nullptr) - info->textures[i]->release(); + int resourceindex = info->resourceIndex + i; - info->textures[i] = tex; + if (activeTextures[resourceindex] != nullptr) + activeTextures[resourceindex]->release(); - auto &binding = textureBindings[info->ints[i]]; + activeTextures[resourceindex] = tex; + + if (info->dataSize == 0) + continue; + + int bindingindex = info->ints[i]; + if (bindingindex < 0) + continue; + + auto &binding = textureBindings[bindingindex]; if (isdefault && (binding.access & ACCESS_WRITE) != 0) { binding.texture = nil; @@ -949,18 +872,24 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe buffer->retain(); - if (info->buffers[i] != nullptr) - info->buffers[i]->release(); + int resourceindex = info->resourceIndex + i; - info->buffers[i] = buffer; + if (activeBuffers[resourceindex] != nullptr) + activeBuffers[resourceindex]->release(); - if (texelbinding) + activeBuffers[resourceindex] = buffer; + + if (info->dataSize == 0) + continue; + + int bindingindex = info->ints[i]; + if (texelbinding && bindingindex >= 0) { - textureBindings[info->ints[i]].texture = getMTLTexture(buffer); + textureBindings[bindingindex].texture = getMTLTexture(buffer); } - else if (storagebinding) + else if (storagebinding && bindingindex >= 0) { - auto &binding = bufferBindings[info->ints[i]]; + auto &binding = bufferBindings[bindingindex]; if (isdefault && (binding.access & ACCESS_WRITE) != 0) binding.buffer = nil; else @@ -987,11 +916,6 @@ void Shader::setVideoTextures(love::graphics::Texture *ytexture, love::graphics: } } -bool Shader::hasUniform(const std::string &name) const -{ - return uniforms.find(name) != uniforms.end(); -} - id Shader::getCachedRenderPipeline(const RenderPipelineKey &key) { auto it = cachedRenderPipelines.find(key); diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index eeca919c9..21e26fb0f 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -38,11 +38,6 @@ namespace graphics namespace opengl { -static bool isBuffer(Shader::UniformType utype) -{ - return utype == Shader::UNIFORM_TEXELBUFFER || utype == Shader::UNIFORM_STORAGEBUFFER; -} - Shader::Shader(StrongRef stages[SHADERSTAGE_MAX_ENUM], const CompileOptions &options) : love::graphics::Shader(stages, options) , program(0) @@ -58,32 +53,11 @@ Shader::~Shader() { unloadVolatile(); - for (const auto &p : uniforms) + for (const auto &p : reflection.allUniforms) { // Allocated with malloc(). - if (p.second.data != nullptr) - free(p.second.data); - - if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE) - { - for (int i = 0; i < p.second.count; i++) - { - if (p.second.textures[i] != nullptr) - p.second.textures[i]->release(); - } - - delete[] p.second.textures; - } - else if (isBuffer(p.second.baseType)) - { - for (int i = 0; i < p.second.count; i++) - { - if (p.second.buffers[i] != nullptr) - p.second.buffers[i]->release(); - } - - delete[] p.second.buffers; - } + if (p.second->data != nullptr) + free(p.second->data); } } @@ -96,6 +70,26 @@ void Shader::mapActiveUniforms() builtinUniformInfo[i] = nullptr; } + // Make sure all stored resources have their Volatiles loaded before + // the sendTextures/sendBuffers calls below, since they call getHandle(). + for (love::graphics::Texture *tex : activeTextures) + { + if (tex == nullptr) + continue; + Volatile *v = dynamic_cast(tex); + if (v != nullptr) + v->loadVolatile(); + } + + for (love::graphics::Buffer *buffer : activeBuffers) + { + if (buffer == nullptr) + continue; + Volatile *v = dynamic_cast(buffer); + if (v != nullptr) + v->loadVolatile(); + } + GLint activeprogram = 0; glGetIntegerv(GL_CURRENT_PROGRAM, &activeprogram); @@ -107,43 +101,39 @@ void Shader::mapActiveUniforms() GLchar cname[256]; const GLint bufsize = (GLint) (sizeof(cname) / sizeof(GLchar)); - std::map olduniforms = uniforms; - uniforms.clear(); - - auto gfx = Module::getInstance(Module::M_GRAPHICS); - for (int uindex = 0; uindex < numuniforms; uindex++) { GLsizei namelen = 0; GLenum gltype = 0; - UniformInfo u = {}; + int count = 0; - glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &u.count, &gltype, cname); + glGetActiveUniform(program, (GLuint) uindex, bufsize, &namelen, &count, &gltype, cname); - u.name = std::string(cname, (size_t) namelen); - u.location = glGetUniformLocation(program, u.name.c_str()); - u.access = ACCESS_READ; - computeUniformTypeInfo(gltype, u); + std::string name(cname, (size_t) namelen); + int location = glGetUniformLocation(program, name.c_str()); - // glGetActiveUniform appends "[0]" to the end of array uniform names... - if (u.name.length() > 3) + if (location == -1) + continue; + + name = canonicaliizeUniformName(name); + + const auto &uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) { - size_t findpos = u.name.find("[0]"); - if (findpos != std::string::npos && findpos == u.name.length() - 3) - u.name.erase(u.name.length() - 3); + handleUnknownUniformName(name.c_str()); + continue; } + UniformInfo &u = *uniformit->second; + + u.active = true; + u.location = location; + // If this is a built-in (LOVE-created) uniform, store the location. BuiltinUniform builtin = BUILTIN_MAX_ENUM; if (getConstant(u.name.c_str(), builtin)) builtinUniforms[int(builtin)] = u.location; - if (u.location == -1) - continue; - - if (!fillUniformReflectionData(u)) - continue; - if ((u.baseType == UNIFORM_SAMPLER && builtin != BUILTIN_TEXTURE_MAIN) || u.baseType == UNIFORM_TEXELBUFFER) { TextureUnit unit; @@ -175,183 +165,51 @@ void Shader::mapActiveUniforms() storageTextureBindings.push_back(binding); } - // Make sure previously set uniform data is preserved, and shader- - // initialized values are retrieved. - auto oldu = olduniforms.find(u.name); - if (oldu != olduniforms.end()) + if (u.dataSize == 0) { - u.data = oldu->second.data; - u.dataSize = oldu->second.dataSize; - u.textures = oldu->second.textures; + if (u.baseType == UNIFORM_MATRIX) + u.dataSize = sizeof(uint32) * u.matrix.rows * u.matrix.columns * u.count; + else + u.dataSize = sizeof(uint32) * u.components * u.count; - updateUniform(&u, u.count, true); + u.data = malloc(u.dataSize); + memset(u.data, 0, u.dataSize); + + const auto &valuesit = reflection.localUniformInitializerValues.find(u.name); + if (valuesit != reflection.localUniformInitializerValues.end()) + { + const auto &values = valuesit->second; + if (!values.empty()) + memcpy(u.data, values.data(), std::min(u.dataSize, sizeof(LocalUniformValue) * values.size())); + } } - else + + if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER) { - u.dataSize = 0; + int startunit = (int) textureUnits.size() - u.count; - switch (u.baseType) - { - case UNIFORM_FLOAT: - u.dataSize = sizeof(float) * u.components * u.count; - u.data = malloc(u.dataSize); - break; - case UNIFORM_INT: - case UNIFORM_BOOL: - case UNIFORM_SAMPLER: - case UNIFORM_STORAGETEXTURE: - case UNIFORM_TEXELBUFFER: - u.dataSize = sizeof(int) * u.components * u.count; - u.data = malloc(u.dataSize); - break; - case UNIFORM_UINT: - u.dataSize = sizeof(unsigned int) * u.components * u.count; - u.data = malloc(u.dataSize); - break; - case UNIFORM_MATRIX: - u.dataSize = sizeof(float) * ((size_t)u.matrix.rows * u.matrix.columns) * u.count; - u.data = malloc(u.dataSize); - break; - default: - break; - } + if (builtin == BUILTIN_TEXTURE_MAIN) + startunit = 0; - if (u.dataSize > 0) - { - memset(u.data, 0, u.dataSize); - - if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_TEXELBUFFER) - { - int startunit = (int) textureUnits.size() - u.count; - - if (builtin == BUILTIN_TEXTURE_MAIN) - startunit = 0; - - for (int i = 0; i < u.count; i++) - u.ints[i] = startunit + i; - - glUniform1iv(u.location, u.count, u.ints); - - if (u.baseType == UNIFORM_TEXELBUFFER) - { - u.buffers = new love::graphics::Buffer*[u.count]; - memset(u.buffers, 0, sizeof(Buffer *) * u.count); - } - else - { - u.textures = new love::graphics::Texture*[u.count]; - - auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType); - for (int i = 0; i < u.count; i++) - { - tex->retain(); - u.textures[i] = tex; - } - } - } - else if (u.baseType == UNIFORM_STORAGETEXTURE) - { - int startbinding = (int) storageTextureBindings.size() - u.count; - for (int i = 0; i < u.count; i++) - u.ints[i] = startbinding + i; - - glUniform1iv(u.location, u.count, u.ints); - - u.textures = new love::graphics::Texture*[u.count]; - - if ((u.access & ACCESS_WRITE) != 0) - { - memset(u.textures, 0, sizeof(Texture *) * u.count); - } - else - { - auto *tex = gfx->getDefaultTexture(u.textureType, u.dataBaseType); - for (int i = 0; i < u.count; i++) - { - tex->retain(); - u.textures[i] = tex; - } - } - } - } - - size_t offset = 0; - - // Store any shader-initialized values in our own memory. for (int i = 0; i < u.count; i++) - { - GLint location = u.location; - - if (u.count > 1) - { - std::ostringstream ss; - ss << i; - - std::string indexname = u.name + "[" + ss.str() + "]"; - location = glGetUniformLocation(program, indexname.c_str()); - } - - if (location == -1) - continue; - - switch (u.baseType) - { - case UNIFORM_FLOAT: - glGetUniformfv(program, location, &u.floats[offset]); - offset += u.components; - break; - case UNIFORM_INT: - case UNIFORM_BOOL: - glGetUniformiv(program, location, &u.ints[offset]); - offset += u.components; - break; - case UNIFORM_UINT: - glGetUniformuiv(program, location, &u.uints[offset]); - offset += u.components; - break; - case UNIFORM_MATRIX: - glGetUniformfv(program, location, &u.floats[offset]); - offset += (size_t)u.matrix.rows * u.matrix.columns; - break; - default: - break; - } - } + u.ints[i] = startunit + i; + } + else if (u.baseType == UNIFORM_STORAGETEXTURE) + { + int startbinding = (int) storageTextureBindings.size() - u.count; + for (int i = 0; i < u.count; i++) + u.ints[i] = startbinding + i; } - uniforms[u.name] = u; + updateUniform(&u, u.count, true); if (builtin != BUILTIN_MAX_ENUM) - builtinUniformInfo[(int)builtin] = &uniforms[u.name]; + builtinUniformInfo[(int)builtin] = &u; if (u.baseType == UNIFORM_SAMPLER || u.baseType == UNIFORM_STORAGETEXTURE) - { - // Make sure all stored textures have their Volatiles loaded before - // the sendTextures call, since it calls getHandle(). - for (int i = 0; i < u.count; i++) - { - if (u.textures[i] == nullptr) - continue; - Volatile *v = dynamic_cast(u.textures[i]); - if (v != nullptr) - v->loadVolatile(); - } - - sendTextures(&u, u.textures, u.count, true); - } + sendTextures(&u, &activeTextures[u.resourceIndex], u.count, true); else if (u.baseType == UNIFORM_TEXELBUFFER) - { - for (int i = 0; i < u.count; i++) - { - if (u.buffers[i] == nullptr) - continue; - Volatile *v = dynamic_cast(u.buffers[i]); - if (v != nullptr) - v->loadVolatile(); - } - - sendBuffers(&u, u.buffers, u.count, true); - } + sendBuffers(&u, &activeBuffers[u.resourceIndex], u.count, true); } if (gl.isBufferUsageSupported(BUFFERUSAGE_SHADER_STORAGE)) @@ -364,37 +222,28 @@ void Shader::mapActiveUniforms() for (int sindex = 0; sindex < numstoragebuffers; sindex++) { - UniformInfo u = {}; - u.baseType = UNIFORM_STORAGEBUFFER; - u.access = ACCESS_READ; - GLsizei namelength = 0; glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, sindex, 2048, &namelength, namebuffer); - u.name = std::string(namebuffer, namelength); - u.count = 1; + std::string name = canonicaliizeUniformName(std::string(namebuffer, namelength)); - if (!fillUniformReflectionData(u)) + const auto &uniformit = reflection.storageBuffers.find(name); + if (uniformit == reflection.storageBuffers.end()) + { + handleUnknownUniformName(name.c_str()); continue; - - // Make sure previously set uniform data is preserved, and shader- - // initialized values are retrieved. - auto oldu = olduniforms.find(u.name); - if (oldu != olduniforms.end()) - { - u.data = oldu->second.data; - u.dataSize = oldu->second.dataSize; - u.buffers = oldu->second.buffers; } - else + + UniformInfo &u = uniformit->second; + + u.active = true; + + if (u.dataSize == 0) { - u.dataSize = sizeof(int) * 1; + u.dataSize = sizeof(int) * u.count; u.data = malloc(u.dataSize); - - u.ints[0] = -1; - - u.buffers = new love::graphics::Buffer * [u.count]; - memset(u.buffers, 0, sizeof(Buffer*)* u.count); + for (int i = 0; i < u.count; i++) + u.ints[i] = -1; } // Unlike local uniforms and attributes, OpenGL doesn't auto-assign storage @@ -417,56 +266,13 @@ void Shader::mapActiveUniforms() if (u.access & ACCESS_WRITE) { p.second = (int)activeWritableStorageBuffers.size(); - activeWritableStorageBuffers.push_back(u.buffers[0]); + activeWritableStorageBuffers.push_back(activeBuffers[u.resourceIndex]); } storageBufferBindingIndexToActiveBinding[binding.bindingindex] = p; } - uniforms[u.name] = u; - - for (int i = 0; i < u.count; i++) - { - if (u.buffers[i] == nullptr) - continue; - Volatile* v = dynamic_cast(u.buffers[i]); - if (v != nullptr) - v->loadVolatile(); - } - - sendBuffers(&u, u.buffers, u.count, true); - } - } - - // Make sure uniforms that existed before but don't exist anymore are - // cleaned up. This theoretically shouldn't happen, but... - for (const auto &p : olduniforms) - { - if (uniforms.find(p.first) == uniforms.end()) - { - if (p.second.data != nullptr) - free(p.second.data); - - if (p.second.baseType == UNIFORM_SAMPLER || p.second.baseType == UNIFORM_STORAGETEXTURE) - { - for (int i = 0; i < p.second.count; i++) - { - if (p.second.textures[i] != nullptr) - p.second.textures[i]->release(); - } - - delete[] p.second.textures; - } - else if (isBuffer(p.second.baseType)) - { - for (int i = 0; i < p.second.count; i++) - { - if (p.second.buffers[i] != nullptr) - p.second.buffers[i]->release(); - } - - delete[] p.second.buffers; - } + sendBuffers(&u, &activeBuffers[u.resourceIndex], u.count, true); } } @@ -649,16 +455,6 @@ void Shader::attach() } } -const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const -{ - const auto it = uniforms.find(name); - - if (it == uniforms.end()) - return nullptr; - - return &(it->second); -} - const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const { return builtinUniformInfo[(int)builtin]; @@ -802,10 +598,12 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex tex->retain(); - if (info->textures[i] != nullptr) - info->textures[i]->release(); + int resourceindex = info->resourceIndex + i; - info->textures[i] = tex; + if (activeTextures[resourceindex] != nullptr) + activeTextures[resourceindex]->release(); + + activeTextures[resourceindex] = tex; if (isstoragetex) { @@ -885,10 +683,12 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe buffer->retain(); - if (info->buffers[i] != nullptr) - info->buffers[i]->release(); + int resourceindex = info->resourceIndex + i; - info->buffers[i] = buffer; + if (activeBuffers[resourceindex] != nullptr) + activeBuffers[resourceindex]->release(); + + activeBuffers[resourceindex] = buffer; if (texelbinding) { @@ -926,11 +726,6 @@ void Shader::flushBatchedDraws() const Graphics::flushBatchedDrawsGlobal(); } -bool Shader::hasUniform(const std::string &name) const -{ - return uniforms.find(name) != uniforms.end(); -} - ptrdiff_t Shader::getHandle() const { return program; @@ -1043,293 +838,6 @@ void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, } } -int Shader::getUniformTypeComponents(GLenum type) const -{ - switch (type) - { - case GL_INT: - case GL_UNSIGNED_INT: - case GL_FLOAT: - case GL_BOOL: - return 1; - case GL_INT_VEC2: - case GL_UNSIGNED_INT_VEC2: - case GL_FLOAT_VEC2: - case GL_FLOAT_MAT2: - case GL_BOOL_VEC2: - return 2; - case GL_INT_VEC3: - case GL_UNSIGNED_INT_VEC3: - case GL_FLOAT_VEC3: - case GL_FLOAT_MAT3: - case GL_BOOL_VEC3: - return 3; - case GL_INT_VEC4: - case GL_UNSIGNED_INT_VEC4: - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT4: - case GL_BOOL_VEC4: - return 4; - default: - return 1; - } -} - -Shader::MatrixSize Shader::getMatrixSize(GLenum type) const -{ - MatrixSize m; - - switch (type) - { - case GL_FLOAT_MAT2: - m.columns = m.rows = 2; - break; - case GL_FLOAT_MAT3: - m.columns = m.rows = 3; - break; - case GL_FLOAT_MAT4: - m.columns = m.rows = 4; - break; - case GL_FLOAT_MAT2x3: - m.columns = 2; - m.rows = 3; - break; - case GL_FLOAT_MAT2x4: - m.columns = 2; - m.rows = 4; - break; - case GL_FLOAT_MAT3x2: - m.columns = 3; - m.rows = 2; - break; - case GL_FLOAT_MAT3x4: - m.columns = 3; - m.rows = 4; - break; - case GL_FLOAT_MAT4x2: - m.columns = 4; - m.rows = 2; - break; - case GL_FLOAT_MAT4x3: - m.columns = 4; - m.rows = 3; - break; - default: - m.columns = m.rows = 0; - break; - } - - return m; -} - -void Shader::computeUniformTypeInfo(GLenum type, UniformInfo &u) -{ - u.isDepthSampler = false; - u.components = getUniformTypeComponents(type); - u.baseType = UNIFORM_UNKNOWN; - - switch (type) - { - case GL_INT: - case GL_INT_VEC2: - case GL_INT_VEC3: - case GL_INT_VEC4: - u.baseType = UNIFORM_INT; - u.dataBaseType = DATA_BASETYPE_INT; - break; - case GL_UNSIGNED_INT: - case GL_UNSIGNED_INT_VEC2: - case GL_UNSIGNED_INT_VEC3: - case GL_UNSIGNED_INT_VEC4: - u.baseType = UNIFORM_UINT; - u.dataBaseType = DATA_BASETYPE_UINT; - break; - case GL_FLOAT: - case GL_FLOAT_VEC2: - case GL_FLOAT_VEC3: - case GL_FLOAT_VEC4: - u.baseType = UNIFORM_FLOAT; - u.dataBaseType = DATA_BASETYPE_FLOAT; - break; - case GL_FLOAT_MAT2: - case GL_FLOAT_MAT3: - case GL_FLOAT_MAT4: - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT3x2: - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x2: - case GL_FLOAT_MAT4x3: - u.baseType = UNIFORM_MATRIX; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.matrix = getMatrixSize(type); - break; - case GL_BOOL: - case GL_BOOL_VEC2: - case GL_BOOL_VEC3: - case GL_BOOL_VEC4: - u.baseType = UNIFORM_BOOL; - u.dataBaseType = DATA_BASETYPE_BOOL; - break; - - case GL_SAMPLER_2D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D; - break; - case GL_SAMPLER_2D_SHADOW: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D; - u.isDepthSampler = true; - break; - case GL_INT_SAMPLER_2D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_2D; - break; - case GL_UNSIGNED_INT_SAMPLER_2D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_2D; - break; - case GL_SAMPLER_2D_ARRAY: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_SAMPLER_2D_ARRAY_SHADOW: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D_ARRAY; - u.isDepthSampler = true; - break; - case GL_INT_SAMPLER_2D_ARRAY: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_SAMPLER_3D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_INT_SAMPLER_3D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_UNSIGNED_INT_SAMPLER_3D: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_SAMPLER_CUBE: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_CUBE; - break; - case GL_SAMPLER_CUBE_SHADOW: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_CUBE; - u.isDepthSampler = true; - break; - case GL_INT_SAMPLER_CUBE: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_CUBE; - break; - case GL_UNSIGNED_INT_SAMPLER_CUBE: - u.baseType = UNIFORM_SAMPLER; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_CUBE; - break; - - case GL_SAMPLER_BUFFER: - u.baseType = UNIFORM_TEXELBUFFER; - u.dataBaseType = DATA_BASETYPE_FLOAT; - break; - case GL_INT_SAMPLER_BUFFER: - u.baseType = UNIFORM_TEXELBUFFER; - u.dataBaseType = DATA_BASETYPE_INT; - break; - case GL_UNSIGNED_INT_SAMPLER_BUFFER: - u.baseType = UNIFORM_TEXELBUFFER; - u.dataBaseType = DATA_BASETYPE_UINT; - break; - - case GL_IMAGE_2D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D; - break; - case GL_INT_IMAGE_2D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_2D; - break; - case GL_UNSIGNED_INT_IMAGE_2D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_2D; - break; - case GL_IMAGE_2D_ARRAY: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_INT_IMAGE_2D_ARRAY: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_2D_ARRAY; - break; - case GL_IMAGE_3D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_INT_IMAGE_3D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_UNSIGNED_INT_IMAGE_3D: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_VOLUME; - break; - case GL_IMAGE_CUBE: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_FLOAT; - u.textureType = TEXTURE_CUBE; - break; - case GL_INT_IMAGE_CUBE: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_INT; - u.textureType = TEXTURE_CUBE; - break; - case GL_UNSIGNED_INT_IMAGE_CUBE: - u.baseType = UNIFORM_STORAGETEXTURE; - u.dataBaseType = DATA_BASETYPE_UINT; - u.textureType = TEXTURE_CUBE; - break; - - default: - break; - } -} - } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index fd1e8c162..134ab97fb 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -63,12 +63,10 @@ public: void attach() override; std::string getWarnings() const override; int getVertexAttributeIndex(const std::string &name) override; - const UniformInfo *getUniformInfo(const std::string &name) const override; const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override; void updateUniform(const UniformInfo *info, int count) override; void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override; void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override; - bool hasUniform(const std::string &name) const override; ptrdiff_t getHandle() const override; void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override; @@ -100,10 +98,6 @@ private: void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count, bool internalupdate); void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalupdate); - int getUniformTypeComponents(GLenum type) const; - void computeUniformTypeInfo(GLenum type, UniformInfo &u); - MatrixSize getMatrixSize(GLenum type) const; - void flushBatchedDraws() const; // Get any warnings or errors generated only by the shader program object. @@ -120,9 +114,6 @@ private: std::map attributes; - // Uniform location buffer map - std::map uniforms; - // Texture unit pool for setting textures std::vector textureUnits; diff --git a/src/modules/graphics/vulkan/Shader.cpp b/src/modules/graphics/vulkan/Shader.cpp index 96f073caf..607ce9936 100644 --- a/src/modules/graphics/vulkan/Shader.cpp +++ b/src/modules/graphics/vulkan/Shader.cpp @@ -173,31 +173,6 @@ void Shader::unloadVolatile() if (shaderModules.empty()) return; - for (const auto &uniform : uniformInfos) - { - switch (uniform.second.baseType) - { - case UNIFORM_SAMPLER: - case UNIFORM_STORAGETEXTURE: - for (int i = 0; i < uniform.second.count; i++) - { - if (uniform.second.textures[i] != nullptr) - uniform.second.textures[i]->release(); - } - delete[] uniform.second.textures; - break; - case UNIFORM_TEXELBUFFER: - case UNIFORM_STORAGEBUFFER: - for (int i = 0; i < uniform.second.count; i++) - { - if (uniform.second.buffers[i] != nullptr) - uniform.second.buffers[i]->release(); - } - delete[] uniform.second.buffers; - break; - } - } - vgfx->queueCleanUp([shaderModules = std::move(shaderModules), device = device, descriptorSetLayout = descriptorSetLayout, pipelineLayout = pipelineLayout, descriptorPools = descriptorPools, computePipeline = computePipeline](){ for (const auto &pools : descriptorPools) { @@ -320,9 +295,9 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind currentUsedUniformStreamBuffersCount++; } - for (const auto &u : uniformInfos) + for (const auto &u : reflection.allUniforms) { - auto &info = u.second; + auto &info = *(u.second); if (usesLocalUniformData(&info)) continue; @@ -333,7 +308,7 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind for (int i = 0; i < info.count; i++) { - auto vkTexture = dynamic_cast(info.textures[i]); + auto vkTexture = dynamic_cast(activeTextures[info.resourceIndex + i]); if (vkTexture == nullptr) throw love::Exception("uniform variable %s is not set.", info.name.c_str()); @@ -374,13 +349,14 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind for (int i = 0; i < info.count; i++) { - if (info.buffers[i] == nullptr) + auto b = activeBuffers[info.resourceIndex + i]; + if (b == nullptr) throw love::Exception("uniform variable %s is not set.", info.name.c_str()); VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = (VkBuffer)info.buffers[i]->getHandle();; + bufferInfo.buffer = (VkBuffer)b->getHandle(); bufferInfo.offset = 0; - bufferInfo.range = info.buffers[i]->getSize(); + bufferInfo.range = b->getSize(); bufferInfos.push_back(bufferInfo); } @@ -396,15 +372,16 @@ void Shader::cmdPushDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBind write.dstSet = currentDescriptorSet; write.dstBinding = info.location; write.dstArrayElement = 0; - write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; write.descriptorCount = info.count; for (int i = 0; i < info.count; i++) { - if (info.buffers[i] == nullptr) + auto b = activeBuffers[info.resourceIndex + i]; + if (b == nullptr) throw love::Exception("uniform variable %s is not set.", info.name.c_str()); - bufferViews.push_back((VkBufferView)info.buffers[i]->getTexelBufferHandle()); + bufferViews.push_back((VkBufferView)b->getTexelBufferHandle()); } write.pTexelBufferView = &bufferViews[bufferViews.size() - info.count]; @@ -444,12 +421,6 @@ int Shader::getVertexAttributeIndex(const std::string &name) return it == attributes.end() ? -1 : it->second; } -const Shader::UniformInfo *Shader::getUniformInfo(const std::string &name) const -{ - const auto it = uniformInfos.find(name); - return it != uniformInfos.end() ? &(it->second) : nullptr; -} - const Shader::UniformInfo *Shader::getUniformInfo(BuiltinUniform builtin) const { return builtinUniformInfo[builtin]; @@ -468,9 +439,10 @@ void Shader::sendTextures(const UniformInfo *info, graphics::Texture **textures, { for (int i = 0; i < count; i++) { - auto oldTexture = info->textures[i]; - info->textures[i] = textures[i]; - info->textures[i]->retain(); + int resourceindex = info->resourceIndex + i; + auto oldTexture = activeTextures[resourceindex]; + activeTextures[resourceindex] = textures[i]; + activeTextures[resourceindex]->retain(); if (oldTexture) oldTexture->release(); } @@ -480,9 +452,10 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe { for (int i = 0; i < count; i++) { - auto oldBuffer = info->buffers[i]; - info->buffers[i] = buffers[i]; - info->buffers[i]->retain(); + int resourceindex = info->resourceIndex + i; + auto oldBuffer = activeBuffers[resourceindex]; + activeBuffers[resourceindex] = buffers[i]; + activeBuffers[resourceindex]->retain(); if (oldBuffer) oldBuffer->release(); } @@ -524,38 +497,25 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross:: continue; } - UniformInfo u{}; - u.name = name; + name = canonicaliizeUniformName(name); + + auto uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) + { + handleUnknownUniformName(name.c_str()); + continue; + } + + UniformInfo &u = *(uniformit->second); + + u.active = true; u.dataSize = memberSize; - u.count = memberType.array.empty() ? 1 : memberType.array[0]; - u.components = 1; u.data = localUniformStagingData.data() + offset; - if (memberType.columns == 1) + const auto &valuesit = reflection.localUniformInitializerValues.find(name); + if (valuesit != reflection.localUniformInitializerValues.end()) { - if (memberType.basetype == SPIRType::Int) - u.baseType = UNIFORM_INT; - else if (memberType.basetype == SPIRType::UInt) - u.baseType = UNIFORM_UINT; - else - u.baseType = UNIFORM_FLOAT; - u.components = memberType.vecsize; - } - else - { - u.baseType = UNIFORM_MATRIX; - u.matrix.rows = memberType.vecsize; - u.matrix.columns = memberType.columns; - } - - const auto &reflectionIt = validationReflection.localUniforms.find(u.name); - if (reflectionIt != validationReflection.localUniforms.end()) - { - const auto &localUniform = reflectionIt->second; - if (localUniform.dataType == DATA_BASETYPE_BOOL) - u.baseType = UNIFORM_BOOL; - - const auto &values = localUniform.initializerValues; + const auto &values = valuesit->second; if (!values.empty()) memcpy( u.data, @@ -563,14 +523,12 @@ void Shader::buildLocalUniforms(spirv_cross::Compiler &comp, const spirv_cross:: std::min(u.dataSize, values.size() * sizeof(LocalUniformValue))); } - uniformInfos[u.name] = u; - BuiltinUniform builtin = BUILTIN_MAX_ENUM; if (getConstant(u.name.c_str(), builtin)) { if (builtin == BUILTIN_UNIFORMS_PER_DRAW) builtinUniformDataOffset = offset; - builtinUniformInfo[builtin] = &uniformInfos[u.name]; + builtinUniformInfo[builtin] = &u; } } } @@ -643,8 +601,6 @@ void Shader::compileShaders() if (!program->mapIO()) throw love::Exception("mapIO failed"); - uniformInfos.clear(); - BindingMapper bindingMapper; for (int i = 0; i < SHADERSTAGE_MAX_ENUM; i++) @@ -695,119 +651,51 @@ void Shader::compileShaders() for (const auto &r : shaderResources.sampled_images) { - const SPIRType &basetype = comp.get_type(r.base_type_id); - const SPIRType &type = comp.get_type(r.type_id); - const SPIRType &imagetype = comp.get_type(basetype.image.type); - - graphics::Shader::UniformInfo info; - info.location = bindingMapper(comp, spirv, r.name, r.id); - info.baseType = UNIFORM_SAMPLER; - info.name = r.name; - info.count = type.array.empty() ? 1 : type.array[0]; - info.isDepthSampler = type.image.depth; - info.components = 1; - - switch (imagetype.basetype) + std::string name = canonicaliizeUniformName(r.name); + auto uniformit = reflection.allUniforms.find(name); + if (uniformit == reflection.allUniforms.end()) { - case SPIRType::Float: - info.dataBaseType = DATA_BASETYPE_FLOAT; - break; - case SPIRType::Int: - info.dataBaseType = DATA_BASETYPE_INT; - break; - case SPIRType::UInt: - info.dataBaseType = DATA_BASETYPE_UINT; - break; - default: - break; + handleUnknownUniformName(name.c_str()); + continue; } - switch (basetype.image.dim) - { - case spv::Dim2D: - info.textureType = basetype.image.arrayed ? TEXTURE_2D_ARRAY : TEXTURE_2D; - info.textures = new love::graphics::Texture *[info.count]; - break; - case spv::Dim3D: - info.textureType = TEXTURE_VOLUME; - info.textures = new love::graphics::Texture *[info.count]; - break; - case spv::DimCube: - if (basetype.image.arrayed) { - throw love::Exception("cubemap arrays are not currently supported"); - } - info.textureType = TEXTURE_CUBE; - info.textures = new love::graphics::Texture *[info.count]; - break; - case spv::DimBuffer: - info.baseType = UNIFORM_TEXELBUFFER; - info.buffers = new love::graphics::Buffer *[info.count]; - break; - default: - throw love::Exception("unknown dim"); - } + UniformInfo &u = *(uniformit->second); + u.active = true; + u.location = bindingMapper(comp, spirv, name, r.id); - if (info.baseType == UNIFORM_TEXELBUFFER) - { - for (int i = 0; i < info.count; i++) - info.buffers[i] = nullptr; - } - else - { - for (int i = 0; i < info.count; i++) - { - info.textures[i] = nullptr; - } - } - - uniformInfos[r.name] = info; BuiltinUniform builtin; - if (getConstant(r.name.c_str(), builtin)) - builtinUniformInfo[builtin] = &uniformInfos[info.name]; + if (getConstant(name.c_str(), builtin)) + builtinUniformInfo[builtin] = &u; } for (const auto &r : shaderResources.storage_buffers) { - const auto &type = comp.get_type(r.type_id); - - UniformInfo u{}; - u.baseType = UNIFORM_STORAGEBUFFER; - u.components = 1; - u.name = r.name; - u.count = type.array.empty() ? 1 : type.array[0]; - - if (!fillUniformReflectionData(u)) + std::string name = canonicaliizeUniformName(r.name); + auto &uniformit = reflection.storageBuffers.find(name); + if (uniformit == reflection.storageBuffers.end()) + { + handleUnknownUniformName(name.c_str()); continue; + } - u.location = bindingMapper(comp, spirv, r.name, r.id); - u.buffers = new love::graphics::Buffer *[u.count]; - - for (int i = 0; i < u.count; i++) - u.buffers[i] = nullptr; - - uniformInfos[u.name] = u; + UniformInfo &u = uniformit->second; + u.active = true; + u.location = bindingMapper(comp, spirv, name, r.id); } for (const auto &r : shaderResources.storage_images) { - const auto &type = comp.get_type(r.type_id); - - UniformInfo u{}; - u.baseType = UNIFORM_STORAGETEXTURE; - u.components = 1; - u.name = r.name; - u.count = type.array.empty() ? 1 : type.array[0]; - - if (!fillUniformReflectionData(u)) + std::string name = canonicaliizeUniformName(r.name); + auto &uniformit = reflection.storageBuffers.find(name); + if (uniformit == reflection.storageBuffers.end()) + { + handleUnknownUniformName(name.c_str()); continue; + } - u.textures = new love::graphics::Texture *[u.count]; - u.location = bindingMapper(comp, spirv, r.name, r.id); - - for (int i = 0; i < u.count; i++) - u.textures[i] = nullptr; - - uniformInfos[u.name] = u; + UniformInfo &u = uniformit->second; + u.active = true; + u.location = bindingMapper(comp, spirv, name, r.id); } if (shaderStage == SHADERSTAGE_VERTEX) @@ -875,9 +763,12 @@ void Shader::compileShaders() if (localUniformData.size() > 0) numBuffers++; - for (const auto &u : uniformInfos) + for (const auto kvp : reflection.allUniforms) { - switch (u.second.baseType) + if (kvp.second->location < 0) + continue; + + switch (kvp.second->baseType) { case UNIFORM_SAMPLER: case UNIFORM_STORAGETEXTURE: @@ -905,16 +796,16 @@ void Shader::createDescriptorSetLayout() else stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - for (auto const &entry : uniformInfos) + for (auto const &entry : reflection.allUniforms) { - auto type = Vulkan::getDescriptorType(entry.second.baseType); + auto type = Vulkan::getDescriptorType(entry.second->baseType); if (type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) { VkDescriptorSetLayoutBinding layoutBinding{}; - layoutBinding.binding = entry.second.location; + layoutBinding.binding = entry.second->location; layoutBinding.descriptorType = type; - layoutBinding.descriptorCount = entry.second.count; + layoutBinding.descriptorCount = entry.second->count; layoutBinding.stageFlags = stageFlags; bindings.push_back(layoutBinding); @@ -976,10 +867,13 @@ void Shader::createDescriptorPoolSizes() descriptorPoolSizes.push_back(size); } - for (const auto &entry : uniformInfos) + for (const auto &entry : reflection.allUniforms) { + if (entry.second->location < 0) + continue; + VkDescriptorPoolSize size{}; - auto type = Vulkan::getDescriptorType(entry.second.baseType); + auto type = Vulkan::getDescriptorType(entry.second->baseType); if (type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) continue; @@ -1012,29 +906,26 @@ void Shader::setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cb for (size_t i = 0; i < textures.size(); i++) { - if (builtinUniformInfo[builtIns[i]] != nullptr) + const UniformInfo *u = builtinUniformInfo[builtIns[i]]; + if (u != nullptr) { textures[i]->retain(); - if (builtinUniformInfo[builtIns[i]]->textures[0]) - builtinUniformInfo[builtIns[i]]->textures[0]->release(); - builtinUniformInfo[builtIns[i]]->textures[0] = textures[i]; + if (activeTextures[u->resourceIndex]) + activeTextures[u->resourceIndex]->release(); + activeTextures[u->resourceIndex] = textures[i]; } } } -bool Shader::hasUniform(const std::string &name) const -{ - return uniformInfos.find(name) != uniformInfos.end(); -} - void Shader::setMainTex(graphics::Texture *texture) { - if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN] != nullptr) + const UniformInfo *u = builtinUniformInfo[BUILTIN_TEXTURE_MAIN]; + if (u != nullptr) { texture->retain(); - if (builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]) - builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0]->release(); - builtinUniformInfo[BUILTIN_TEXTURE_MAIN]->textures[0] = texture; + if (activeTextures[u->resourceIndex]) + activeTextures[u->resourceIndex]->release(); + activeTextures[u->resourceIndex] = texture; } } diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 58632285c..d48fa09ca 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -76,7 +76,6 @@ public: int getVertexAttributeIndex(const std::string &name) override; - const UniformInfo *getUniformInfo(const std::string &name) const override; const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override; void updateUniform(const UniformInfo *info, int count) override; @@ -84,8 +83,6 @@ public: void sendTextures(const UniformInfo *info, graphics::Texture **textures, int count) override; void sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count) override; - bool hasUniform(const std::string &name) const override; - void setVideoTextures(graphics::Texture *ytexture, graphics::Texture *cbtexture, graphics::Texture *crtexture) override; void setMainTex(graphics::Texture *texture); @@ -126,7 +123,6 @@ private: bool isCompute = false; - std::unordered_map uniformInfos; UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM]; std::unique_ptr uniformBufferObjectBuffer; diff --git a/src/modules/graphics/wrap_Shader.cpp b/src/modules/graphics/wrap_Shader.cpp index 69a9ee941..51de56b8c 100644 --- a/src/modules/graphics/wrap_Shader.cpp +++ b/src/modules/graphics/wrap_Shader.cpp @@ -446,7 +446,7 @@ int w_Shader_send(lua_State *L) const char *name = luaL_checkstring(L, 2); const Shader::UniformInfo *info = shader->getUniformInfo(name); - if (info == nullptr) + if (info == nullptr || !info->active) return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name); if (luax_istype(L, 3, Data::type) || (info->baseType == Shader::UNIFORM_MATRIX && luax_istype(L, 4, Data::type))) @@ -461,14 +461,11 @@ int w_Shader_sendColors(lua_State *L) const char *name = luaL_checkstring(L, 2); const Shader::UniformInfo *info = shader->getUniformInfo(name); - if (info == nullptr) - { - luax_pushboolean(L, false); - return 1; - } + if (info == nullptr || !info->active) + return luaL_error(L, "Shader uniform '%s' does not exist.\nA common error is to define but not use the variable.", name); if (info->baseType != Shader::UNIFORM_FLOAT || info->components < 3) - return luaL_error(L, "sendColor can only be used on vec3 or vec4 uniforms."); + return luaL_error(L, "Shader:sendColor can only be used with vec3 or vec4 uniforms."); if (luax_istype(L, 3, Data::type)) w_Shader_sendData(L, 3, shader, info, true);