graphics: most uniform reflection is handled at a higher level instead of backend code.

Fixes boolean uniforms when Metal is used.
This commit is contained in:
Sasha Szpakowski
2024-03-07 21:24:11 -04:00
parent 99af5765fc
commit 2079f657e7
10 changed files with 560 additions and 1079 deletions
+241 -66
View File
@@ -691,9 +691,48 @@ Shader::Shader(StrongRef<ShaderStage> _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<Graphics>(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<ShaderStage> 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 <typename T>
static T convertData(const glslang::TConstUnion &data)
{
@@ -903,7 +1002,7 @@ static T convertData(const glslang::TConstUnion &data)
}
}
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, ValidationReflection &reflection)
bool Shader::validateInternal(StrongRef<ShaderStage> stages[], std::string &err, Reflection &reflection)
{
glslang::TProgram program;
@@ -946,6 +1045,9 @@ bool Shader::validateInternal(StrongRef<ShaderStage> 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<ShaderStage> 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<ShaderStage> 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<LocalUniformValue> 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<ShaderStage> 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<ShaderStage> 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<ShaderStage> 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<ShaderStage> 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<ShaderStage> 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<ShaderStage> 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();
+27 -33
View File
@@ -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<std::string, UniformInfo> texelBuffers;
std::map<std::string, UniformInfo> storageBuffers;
std::map<std::string, UniformInfo> sampledTextures;
std::map<std::string, UniformInfo> storageTextures;
std::map<std::string, UniformInfo> localUniforms;
struct StorageTextureReflection
{
PixelFormat format;
Access access;
};
std::map<std::string, UniformInfo *> allUniforms;
struct LocalUniform
{
DataBaseType dataType;
std::vector<LocalUniformValue> initializerValues;
};
std::map<std::string, std::vector<LocalUniformValue>> localUniformInitializerValues;
int textureCount;
int bufferCount;
struct ValidationReflection
{
std::map<std::string, BufferReflection> storageBuffers;
std::map<std::string, StorageTextureReflection> storageTextures;
std::map<std::string, LocalUniform> localUniforms;
int localThreadgroupSize[3];
bool usesPointSize;
};
bool fillUniformReflectionData(UniformInfo &u);
std::string getShaderStageDebugName(ShaderStageType stage) const;
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, ValidationReflection &reflection);
void handleUnknownUniformName(const char *name);
static std::string canonicaliizeUniformName(const std::string &name);
static bool validateInternal(StrongRef<ShaderStage> stages[], std::string& err, Reflection &reflection);
static DataBaseType getDataBaseType(PixelFormat format);
static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b);
@@ -305,7 +296,10 @@ protected:
StrongRef<ShaderStage> stages[SHADERSTAGE_MAX_ENUM];
ValidationReflection validationReflection;
Reflection reflection;
std::vector<Texture *> activeTextures;
std::vector<Buffer *> activeBuffers;
std::string debugName;
+8
View File
@@ -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:
+1 -4
View File
@@ -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<MTLDevice> device, const glslang::TProgram &program);
id<MTLFunction> functions[SHADERSTAGE_MAX_ENUM];
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
std::map<std::string, UniformInfo> uniforms;
uint8 *localUniformStagingData;
uint8 *localUniformBufferData;
+94 -170
View File
@@ -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<MTLDevice> device, const glslang::TProgram &program)
@@ -473,19 +410,18 @@ void Shader::compileFromGLSLang(id<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLDevice> 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<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineKey &key)
{
auto it = cachedRenderPipelines.find(key);
+98 -590
View File
@@ -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<love::graphics::ShaderStage> 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<Volatile *>(tex);
if (v != nullptr)
v->loadVolatile();
}
for (love::graphics::Buffer *buffer : activeBuffers)
{
if (buffer == nullptr)
continue;
Volatile *v = dynamic_cast<Volatile *>(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<std::string, UniformInfo> olduniforms = uniforms;
uniforms.clear();
auto gfx = Module::getInstance<love::graphics::Graphics>(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<Volatile *>(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<Volatile *>(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<Volatile*>(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
-9
View File
@@ -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<std::string, GLint> attributes;
// Uniform location buffer map
std::map<std::string, UniformInfo> uniforms;
// Texture unit pool for setting textures
std::vector<TextureUnit> textureUnits;
+87 -196
View File
@@ -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<Texture*>(info.textures[i]);
auto vkTexture = dynamic_cast<Texture*>(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;
}
}
-4
View File
@@ -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<std::string, graphics::Shader::UniformInfo> uniformInfos;
UniformInfo *builtinUniformInfo[BUILTIN_MAX_ENUM];
std::unique_ptr<StreamBuffer> uniformBufferObjectBuffer;
+4 -7
View File
@@ -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);