/** * Copyright (c) 2006-2020 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ // LOVE #include "Shader.h" #include "Graphics.h" #include "math/MathModule.h" // glslang #include "libraries/glslang/glslang/Public/ShaderLang.h" // C++ #include namespace love { namespace graphics { static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes."); love::Type Shader::type("Shader", &Object::type); Shader *Shader::current = nullptr; Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr}; Shader::Shader(ShaderStage *vertex, ShaderStage *pixel) : stages() { std::string err; if (!validate(vertex, pixel, err)) throw love::Exception("%s", err.c_str()); stages[ShaderStage::STAGE_VERTEX] = vertex; stages[ShaderStage::STAGE_PIXEL] = pixel; } Shader::~Shader() { for (int i = 0; i < STANDARD_MAX_ENUM; i++) { if (this == standardShaders[i]) standardShaders[i] = nullptr; } if (current == this) attachDefault(STANDARD_DEFAULT); } void Shader::attachDefault(StandardShader defaultType) { Shader *defaultshader = standardShaders[defaultType]; if (defaultshader == nullptr) { current = nullptr; return; } if (current != defaultshader) defaultshader->attach(); } bool Shader::isDefaultActive() { for (int i = 0; i < STANDARD_MAX_ENUM; i++) { if (current == standardShaders[i]) return true; } return false; } TextureType Shader::getMainTextureType() const { const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN); return info != nullptr ? info->textureType : TEXTURE_MAX_ENUM; } void Shader::checkMainTextureType(TextureType textype, bool isDepthSampler) const { const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN); if (info == nullptr) return; if (info->textureType != TEXTURE_MAX_ENUM && info->textureType != textype) { const char *textypestr = "unknown"; const char *shadertextypestr = "unknown"; Texture::getConstant(textype, textypestr); Texture::getConstant(info->textureType, shadertextypestr); throw love::Exception("Texture's type (%s) must match the type of the shader's main texture type (%s).", textypestr, shadertextypestr); } if (info->isDepthSampler != isDepthSampler) { 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."); } } void Shader::checkMainTexture(Texture *tex) const { if (!tex->isReadable()) throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader."); checkMainTextureType(tex->getTextureType(), tex->getSamplerState().depthSampleMode.hasValue); } bool Shader::validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err) { glslang::TProgram program; if (vertex != nullptr) program.addShader(vertex->getGLSLangShader()); if (pixel != nullptr) program.addShader(pixel->getGLSLangShader()); if (!program.link(EShMsgDefault)) { err = "Cannot compile shader:\n\n" + std::string(program.getInfoLog()) + "\n" + std::string(program.getInfoDebugLog()); return false; } return true; } bool Shader::initialize() { return glslang::InitializeProcess(); } void Shader::deinitialize() { glslang::FinalizeProcess(); } bool Shader::getConstant(const char *in, Language &out) { return languages.find(in, out); } bool Shader::getConstant(Language in, const char *&out) { return languages.find(in, out); } bool Shader::getConstant(const char *in, BuiltinUniform &out) { return builtinNames.find(in, out); } bool Shader::getConstant(BuiltinUniform in, const char *&out) { return builtinNames.find(in, out); } StringMap::Entry Shader::languageEntries[] = { { "glsl1", LANGUAGE_GLSL1 }, { "essl1", LANGUAGE_ESSL1 }, { "glsl3", LANGUAGE_GLSL3 }, { "essl3", LANGUAGE_ESSL3 }, }; StringMap Shader::languages(Shader::languageEntries, sizeof(Shader::languageEntries)); StringMap::Entry Shader::builtinNameEntries[] = { { "MainTex", BUILTIN_TEXTURE_MAIN }, { "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y }, { "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB }, { "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR }, { "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW }, { "love_PointSize", BUILTIN_POINT_SIZE }, }; StringMap Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries)); } // graphics } // love