mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Move some shader binding validation code out of the opengl backend
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user