Move some shader binding validation code out of the opengl backend

This commit is contained in:
Alex Szpakowski
2021-03-30 19:28:42 -03:00
parent 2064c140ac
commit 7eb3d4343a
3 changed files with 116 additions and 96 deletions
+108
View File
@@ -726,6 +726,114 @@ bool Shader::validateInternal(ShaderStage *vertex, ShaderStage *pixel, std::stri
return true;
}
bool Shader::validateTexture(const UniformInfo *info, Texture *tex, bool internalUpdate)
{
const SamplerState &sampler = tex->getSamplerState();
if (!tex->isReadable())
{
if (internalUpdate)
return false;
else
throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader.");
}
else if (info->isDepthSampler != sampler.depthSampleMode.hasValue)
{
if (internalUpdate)
return false;
else if (info->isDepthSampler)
throw love::Exception("Depth comparison samplers in shaders can only be used with depth textures which have depth comparison set.");
else
throw love::Exception("Depth textures which have depth comparison set can only be used with depth/shadow samplers in shaders.");
}
else if (tex->getTextureType() != info->textureType)
{
if (internalUpdate)
return false;
else
{
const char *textypestr = "unknown";
const char *shadertextypestr = "unknown";
Texture::getConstant(tex->getTextureType(), textypestr);
Texture::getConstant(info->textureType, shadertextypestr);
throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr);
}
}
return true;
}
static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
{
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
return true;
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
return true;
return false;
}
bool Shader::validateBuffer(const UniformInfo *info, Buffer *buffer, bool internalUpdate)
{
uint32 requiredtypeflags = 0;
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
if (texelbinding)
requiredtypeflags = BUFFERUSAGEFLAG_TEXEL;
else if (storagebinding)
requiredtypeflags = BUFFERUSAGEFLAG_SHADER_STORAGE;
if ((buffer->getUsageFlags() & requiredtypeflags) == 0)
{
if (internalUpdate)
return false;
else if (texelbinding)
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
else if (storagebinding)
throw love::Exception("Shader uniform '%s' is a shader storage buffer block, but the given Buffer was not created with shader storage buffer capabilities.", info->name.c_str());
else
throw love::Exception("Shader uniform '%s' does not match the types supported by the given Buffer.", info->name.c_str());
}
if (texelbinding)
{
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
{
if (internalUpdate)
return false;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
}
}
else if (storagebinding)
{
if (info->bufferStride != buffer->getArrayStride())
{
if (internalUpdate)
return false;
else
throw love::Exception("Shader storage block '%s' has an array stride of %d bytes, but the given Buffer has an array stride of %d bytes.",
info->name.c_str(), info->bufferStride, buffer->getArrayStride());
}
else if (info->bufferMemberCount != buffer->getDataMembers().size())
{
if (internalUpdate)
return false;
else
throw love::Exception("Shader storage block '%s' has a struct with %d fields, but the given Buffer has a format with %d members.",
info->name.c_str(), info->bufferMemberCount, buffer->getDataMembers().size());
}
}
return true;
}
bool Shader::initialize()
{
return glslang::InitializeProcess();
+3
View File
@@ -243,6 +243,9 @@ protected:
static bool validateInternal(ShaderStage* vertex, ShaderStage* pixel, std::string& err, ValidationReflection &reflection);
static bool validateTexture(const UniformInfo *info, Texture *tex, bool internalUpdate);
static bool validateBuffer(const UniformInfo *info, Buffer *buffer, bool internalUpdate);
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
ValidationReflection validationReflection;
+5 -96
View File
@@ -722,37 +722,8 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
if (tex != nullptr)
{
const SamplerState &sampler = tex->getSamplerState();
if (!tex->isReadable())
{
if (internalUpdate)
continue;
else
throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader.");
}
else if (info->isDepthSampler != sampler.depthSampleMode.hasValue)
{
if (internalUpdate)
continue;
else if (info->isDepthSampler)
throw love::Exception("Depth comparison samplers in shaders can only be used with depth textures which have depth comparison set.");
else
throw love::Exception("Depth textures which have depth comparison set can only be used with depth/shadow samplers in shaders.");
}
else if (tex->getTextureType() != info->textureType)
{
if (internalUpdate)
continue;
else
{
const char *textypestr = "unknown";
const char *shadertextypestr = "unknown";
Texture::getConstant(tex->getTextureType(), textypestr);
Texture::getConstant(info->textureType, shadertextypestr);
throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr);
}
}
if (!validateTexture(info, tex, internalUpdate))
continue;
tex->retain();
}
@@ -782,33 +753,12 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
Shader::sendBuffers(info, buffers, count, false);
}
static bool isTexelBufferTypeCompatible(DataBaseType a, DataBaseType b)
{
if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM)
return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM;
if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT)
return true;
if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT)
return true;
return false;
}
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
{
uint32 requiredtypeflags = 0;
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
if (texelbinding)
requiredtypeflags = BUFFERUSAGEFLAG_TEXEL;
else if (storagebinding)
requiredtypeflags = BUFFERUSAGEFLAG_SHADER_STORAGE;
if (requiredtypeflags == 0)
if (!texelbinding && !storagebinding)
return;
bool shaderactive = current == this;
@@ -825,49 +775,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
if (buffer != nullptr)
{
if ((buffer->getUsageFlags() & requiredtypeflags) == 0)
{
if (internalUpdate)
continue;
else if (texelbinding)
throw love::Exception("Shader uniform '%s' is a texel buffer, but the given Buffer was not created with texel buffer capabilities.", info->name.c_str());
else if (storagebinding)
throw love::Exception("Shader uniform '%s' is a shader storage buffer block, but the given Buffer was not created with shader storage buffer capabilities.", info->name.c_str());
else
throw love::Exception("Shader uniform '%s' does not match the types supported by the given Buffer.", info->name.c_str());
}
if (texelbinding)
{
DataBaseType basetype = buffer->getDataMember(0).info.baseType;
if (!isTexelBufferTypeCompatible(basetype, info->texelBufferType))
{
if (internalUpdate)
continue;
else
throw love::Exception("Texel buffer's data format base type must match the variable declared in the shader.");
}
}
else if (storagebinding)
{
if (info->bufferStride != buffer->getArrayStride())
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader storage block '%s' has an array stride of %d bytes, but the given Buffer has an array stride of %d bytes.",
info->name.c_str(), info->bufferStride, buffer->getArrayStride());
}
else if (info->bufferMemberCount != buffer->getDataMembers().size())
{
if (internalUpdate)
continue;
else
throw love::Exception("Shader storage block '%s' has a struct with %d fields, but the given Buffer has a format with %d members.",
info->name.c_str(), info->bufferMemberCount, buffer->getDataMembers().size());
}
}
if (!validateBuffer(info, buffer, internalUpdate))
continue;
buffer->retain();
}