mirror of
https://github.com/love2d/love.git
synced 2026-08-17 19:23:38 +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;
|
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()
|
bool Shader::initialize()
|
||||||
{
|
{
|
||||||
return glslang::InitializeProcess();
|
return glslang::InitializeProcess();
|
||||||
|
|||||||
@@ -243,6 +243,9 @@ protected:
|
|||||||
|
|
||||||
static bool validateInternal(ShaderStage* vertex, ShaderStage* pixel, std::string& err, ValidationReflection &reflection);
|
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];
|
StrongRef<ShaderStage> stages[ShaderStage::STAGE_MAX_ENUM];
|
||||||
|
|
||||||
ValidationReflection validationReflection;
|
ValidationReflection validationReflection;
|
||||||
|
|||||||
@@ -722,37 +722,8 @@ void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **tex
|
|||||||
|
|
||||||
if (tex != nullptr)
|
if (tex != nullptr)
|
||||||
{
|
{
|
||||||
const SamplerState &sampler = tex->getSamplerState();
|
if (!validateTexture(info, tex, internalUpdate))
|
||||||
if (!tex->isReadable())
|
continue;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tex->retain();
|
tex->retain();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,33 +753,12 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
|||||||
Shader::sendBuffers(info, buffers, count, false);
|
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)
|
void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffers, int count, bool internalUpdate)
|
||||||
{
|
{
|
||||||
uint32 requiredtypeflags = 0;
|
|
||||||
|
|
||||||
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
|
bool texelbinding = info->baseType == UNIFORM_TEXELBUFFER;
|
||||||
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
|
bool storagebinding = info->baseType == UNIFORM_STORAGEBUFFER;
|
||||||
|
|
||||||
if (texelbinding)
|
if (!texelbinding && !storagebinding)
|
||||||
requiredtypeflags = BUFFERUSAGEFLAG_TEXEL;
|
|
||||||
else if (storagebinding)
|
|
||||||
requiredtypeflags = BUFFERUSAGEFLAG_SHADER_STORAGE;
|
|
||||||
|
|
||||||
if (requiredtypeflags == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool shaderactive = current == this;
|
bool shaderactive = current == this;
|
||||||
@@ -825,49 +775,8 @@ void Shader::sendBuffers(const UniformInfo *info, love::graphics::Buffer **buffe
|
|||||||
|
|
||||||
if (buffer != nullptr)
|
if (buffer != nullptr)
|
||||||
{
|
{
|
||||||
if ((buffer->getUsageFlags() & requiredtypeflags) == 0)
|
if (!validateBuffer(info, buffer, internalUpdate))
|
||||||
{
|
continue;
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer->retain();
|
buffer->retain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user