mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
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:
+241
-66
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user