diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 592855828..b6fa380cd 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -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(); diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 3660bd16f..49d46a69e 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -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 stages[ShaderStage::STAGE_MAX_ENUM]; ValidationReflection validationReflection; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 7a02616d9..df3ab428e 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -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(); }