//////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) // // 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. // //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SFML_OPENGL_ES #if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS) #define castToGlHandle(x) reinterpret_cast(std::ptrdiff_t{x}) #define castFromGlHandle(x) static_cast(reinterpret_cast(x)) #else #define castToGlHandle(x) (x) #define castFromGlHandle(x) (x) #endif namespace { // Retrieve the maximum number of texture units available std::size_t getMaxTextureUnits() { static const GLint maxUnits = [] { GLint value = 0; glCheck(glGetIntegerv(GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value)); return value; }(); return static_cast(maxUnits); } // Read the contents of a file into an array of char bool getFileContents(const std::filesystem::path& filename, std::vector& buffer) { if (auto file = std::ifstream(filename, std::ios_base::binary)) { file.seekg(0, std::ios_base::end); const std::ifstream::pos_type size = file.tellg(); if (size > 0) { file.seekg(0, std::ios_base::beg); buffer.resize(static_cast(size)); file.read(buffer.data(), static_cast(size)); } buffer.push_back('\0'); return true; } return false; } // Read the contents of a stream into an array of char bool getStreamContents(sf::InputStream& stream, std::vector& buffer) { bool success = false; const std::optional size = stream.getSize(); if (size > std::size_t{0}) { buffer.resize(*size); if (!stream.seek(0).has_value()) { sf::err() << "Failed to seek shader stream" << std::endl; return false; } const std::optional read = stream.read(buffer.data(), *size); success = (read == size); } buffer.push_back('\0'); return success; } // Transforms an array of 2D vectors into a contiguous array of scalars std::vector flatten(const sf::Vector2f* vectorArray, std::size_t length) { const std::size_t vectorSize = 2; std::vector contiguous(vectorSize * length); for (std::size_t i = 0; i < length; ++i) { contiguous[vectorSize * i] = vectorArray[i].x; contiguous[vectorSize * i + 1] = vectorArray[i].y; } return contiguous; } // Transforms an array of 3D vectors into a contiguous array of scalars std::vector flatten(const sf::Vector3f* vectorArray, std::size_t length) { const std::size_t vectorSize = 3; std::vector contiguous(vectorSize * length); for (std::size_t i = 0; i < length; ++i) { contiguous[vectorSize * i] = vectorArray[i].x; contiguous[vectorSize * i + 1] = vectorArray[i].y; contiguous[vectorSize * i + 2] = vectorArray[i].z; } return contiguous; } // Transforms an array of 4D vectors into a contiguous array of scalars std::vector flatten(const sf::Glsl::Vec4* vectorArray, std::size_t length) { const std::size_t vectorSize = 4; std::vector contiguous(vectorSize * length); for (std::size_t i = 0; i < length; ++i) { contiguous[vectorSize * i] = vectorArray[i].x; contiguous[vectorSize * i + 1] = vectorArray[i].y; contiguous[vectorSize * i + 2] = vectorArray[i].z; contiguous[vectorSize * i + 3] = vectorArray[i].w; } return contiguous; } } // namespace namespace sf { //////////////////////////////////////////////////////////// struct Shader::UniformBinder { //////////////////////////////////////////////////////////// /// \brief Constructor: set up state before uniform is set /// //////////////////////////////////////////////////////////// UniformBinder(Shader& shader, const std::string& name) : currentProgram(castToGlHandle(shader.m_shaderProgram)) { if (currentProgram) { // Enable program object savedProgram = glCheck(GLEXT_glGetHandle(GLEXT_GL_PROGRAM_OBJECT)); if (currentProgram != savedProgram) glCheck(GLEXT_glUseProgramObject(currentProgram)); // Store uniform location for further use outside constructor location = shader.getUniformLocation(name); } } //////////////////////////////////////////////////////////// /// \brief Destructor: restore state after uniform is set /// //////////////////////////////////////////////////////////// ~UniformBinder() { // Disable program object if (currentProgram && (currentProgram != savedProgram)) glCheck(GLEXT_glUseProgramObject(savedProgram)); } //////////////////////////////////////////////////////////// /// \brief Deleted copy constructor /// //////////////////////////////////////////////////////////// UniformBinder(const UniformBinder&) = delete; //////////////////////////////////////////////////////////// /// \brief Deleted copy assignment /// //////////////////////////////////////////////////////////// UniformBinder& operator=(const UniformBinder&) = delete; TransientContextLock lock; //!< Lock to keep context active while uniform is bound GLEXT_GLhandle savedProgram{}; //!< Handle to the previously active program object GLEXT_GLhandle currentProgram; //!< Handle to the program object of the modified sf::Shader instance GLint location{-1}; //!< Uniform location, used by the surrounding sf::Shader code }; //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& filename, Type type) { if (!loadFromFile(filename, type)) throw sf::Exception("Failed to load shader from file"); } //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& fragmentShaderFilename) { if (!loadFromFile(vertexShaderFilename, fragmentShaderFilename)) throw sf::Exception("Failed to load shader from files"); } //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& geometryShaderFilename, const std::filesystem::path& fragmentShaderFilename) { if (!loadFromFile(vertexShaderFilename, geometryShaderFilename, fragmentShaderFilename)) throw sf::Exception("Failed to load shader from files"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view shader, Type type) { if (!loadFromMemory(shader, type)) throw sf::Exception("Failed to load shader from memory"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view vertexShader, std::string_view fragmentShader) { if (!loadFromMemory(vertexShader, fragmentShader)) throw sf::Exception("Failed to load shader from memory"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view vertexShader, std::string_view geometryShader, std::string_view fragmentShader) { if (!loadFromMemory(vertexShader, geometryShader, fragmentShader)) throw sf::Exception("Failed to load shader from memory"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& stream, Type type) { if (!loadFromStream(stream, type)) throw sf::Exception("Failed to load shader from stream"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& vertexShaderStream, InputStream& fragmentShaderStream) { if (!loadFromStream(vertexShaderStream, fragmentShaderStream)) throw sf::Exception("Failed to load shader from streams"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream) { if (!loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream)) throw sf::Exception("Failed to load shader from streams"); } //////////////////////////////////////////////////////////// Shader::~Shader() { const TransientContextLock lock; // Destroy effect program if (m_shaderProgram) glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); } //////////////////////////////////////////////////////////// Shader::Shader(Shader&& source) noexcept : m_shaderProgram(std::exchange(source.m_shaderProgram, 0u)), m_currentTexture(std::exchange(source.m_currentTexture, -1)), m_textures(std::move(source.m_textures)), m_uniforms(std::move(source.m_uniforms)) { } //////////////////////////////////////////////////////////// Shader& Shader::operator=(Shader&& right) noexcept { // Make sure we aren't moving ourselves. if (&right == this) { return *this; } if (m_shaderProgram) { // Destroy effect program const TransientContextLock lock; glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); } // Move the contents of right. m_shaderProgram = std::exchange(right.m_shaderProgram, 0u); m_currentTexture = std::exchange(right.m_currentTexture, -1); m_textures = std::move(right.m_textures); m_uniforms = std::move(right.m_uniforms); return *this; } //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& filename, Type type) { // Read the file std::vector shader; if (!getFileContents(filename, shader)) { err() << "Failed to open shader file\n" << formatDebugPathInfo(filename) << std::endl; return false; } // Compile the shader program if (type == Type::Vertex) return compile(shader.data(), {}, {}); if (type == Type::Geometry) return compile({}, shader.data(), {}); return compile({}, {}, shader.data()); } //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& fragmentShaderFilename) { // Read the vertex shader file std::vector vertexShader; if (!getFileContents(vertexShaderFilename, vertexShader)) { err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl; return false; } // Read the fragment shader file std::vector fragmentShader; if (!getFileContents(fragmentShaderFilename, fragmentShader)) { err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; return false; } // Compile the shader program return compile(vertexShader.data(), {}, fragmentShader.data()); } //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, const std::filesystem::path& geometryShaderFilename, const std::filesystem::path& fragmentShaderFilename) { // Read the vertex shader file std::vector vertexShader; if (!getFileContents(vertexShaderFilename, vertexShader)) { err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl; return false; } // Read the geometry shader file std::vector geometryShader; if (!getFileContents(geometryShaderFilename, geometryShader)) { err() << "Failed to open geometry shader file\n" << formatDebugPathInfo(geometryShaderFilename) << std::endl; return false; } // Read the fragment shader file std::vector fragmentShader; if (!getFileContents(fragmentShaderFilename, fragmentShader)) { err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; return false; } // Compile the shader program return compile(vertexShader.data(), geometryShader.data(), fragmentShader.data()); } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view shader, Type type) { // Compile the shader program if (type == Type::Vertex) return compile(shader, {}, {}); if (type == Type::Geometry) return compile({}, shader, {}); return compile({}, {}, shader); } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view vertexShader, std::string_view fragmentShader) { // Compile the shader program return compile(vertexShader, {}, fragmentShader); } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view vertexShader, std::string_view geometryShader, std::string_view fragmentShader) { // Compile the shader program return compile(vertexShader, geometryShader, fragmentShader); } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& stream, Type type) { // Read the shader code from the stream std::vector shader; if (!getStreamContents(stream, shader)) { err() << "Failed to read shader from stream" << std::endl; return false; } // Compile the shader program if (type == Type::Vertex) return compile(shader.data(), {}, {}); if (type == Type::Geometry) return compile({}, shader.data(), {}); return compile({}, {}, shader.data()); } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream) { // Read the vertex shader code from the stream std::vector vertexShader; if (!getStreamContents(vertexShaderStream, vertexShader)) { err() << "Failed to read vertex shader from stream" << std::endl; return false; } // Read the fragment shader code from the stream std::vector fragmentShader; if (!getStreamContents(fragmentShaderStream, fragmentShader)) { err() << "Failed to read fragment shader from stream" << std::endl; return false; } // Compile the shader program return compile(vertexShader.data(), {}, fragmentShader.data()); } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream) { // Read the vertex shader code from the stream std::vector vertexShader; if (!getStreamContents(vertexShaderStream, vertexShader)) { err() << "Failed to read vertex shader from stream" << std::endl; return false; } // Read the geometry shader code from the stream std::vector geometryShader; if (!getStreamContents(geometryShaderStream, geometryShader)) { err() << "Failed to read geometry shader from stream" << std::endl; return false; } // Read the fragment shader code from the stream std::vector fragmentShader; if (!getStreamContents(fragmentShaderStream, fragmentShader)) { err() << "Failed to read fragment shader from stream" << std::endl; return false; } // Compile the shader program return compile(vertexShader.data(), geometryShader.data(), fragmentShader.data()); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, float x) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform1f(binder.location, x)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, Glsl::Vec2 v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform2f(binder.location, v.x, v.y)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Vec3& v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform3f(binder.location, v.x, v.y, v.z)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Vec4& v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform4f(binder.location, v.x, v.y, v.z, v.w)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, int x) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform1i(binder.location, x)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, Glsl::Ivec2 v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform2i(binder.location, v.x, v.y)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Ivec3& v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform3i(binder.location, v.x, v.y, v.z)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Ivec4& v) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform4i(binder.location, v.x, v.y, v.z, v.w)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, bool x) { setUniform(name, static_cast(x)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, Glsl::Bvec2 v) { setUniform(name, Glsl::Ivec2(v)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Bvec3& v) { setUniform(name, Glsl::Ivec3(v)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Bvec4& v) { setUniform(name, Glsl::Ivec4(v)); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Mat3& matrix) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniformMatrix3fv(binder.location, 1, GL_FALSE, matrix.array.data())); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Mat4& matrix) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniformMatrix4fv(binder.location, 1, GL_FALSE, matrix.array.data())); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Texture& texture) { if (!m_shaderProgram) return; const TransientContextLock lock; // Find the location of the variable in the shader const int location = getUniformLocation(name); if (location != -1) { // Store the location -> texture mapping const auto it = m_textures.find(location); if (it == m_textures.end()) { // New entry, make sure there are enough texture units if (m_textures.size() + 1 >= getMaxTextureUnits()) { err() << "Impossible to use texture " << std::quoted(name) << " for shader: all available texture units are used" << std::endl; return; } m_textures[location] = &texture; } else { // Location already used, just replace the texture it->second = &texture; } } } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, CurrentTextureType) { if (!m_shaderProgram) return; const TransientContextLock lock; // Find the location of the variable in the shader m_currentTexture = getUniformLocation(name); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) { const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform1fv(binder.location, static_cast(length), scalarArray)); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) { std::vector contiguous = flatten(vectorArray, length); const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform2fv(binder.location, static_cast(length), contiguous.data())); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) { std::vector contiguous = flatten(vectorArray, length); const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform3fv(binder.location, static_cast(length), contiguous.data())); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) { std::vector contiguous = flatten(vectorArray, length); const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniform4fv(binder.location, static_cast(length), contiguous.data())); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) { static const std::size_t matrixSize = matrixArray[0].array.size(); std::vector contiguous(matrixSize * length); for (std::size_t i = 0; i < length; ++i) priv::copyMatrix(matrixArray[i].array.data(), matrixSize, &contiguous[matrixSize * i]); const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniformMatrix3fv(binder.location, static_cast(length), GL_FALSE, contiguous.data())); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) { static const std::size_t matrixSize = matrixArray[0].array.size(); std::vector contiguous(matrixSize * length); for (std::size_t i = 0; i < length; ++i) priv::copyMatrix(matrixArray[i].array.data(), matrixSize, &contiguous[matrixSize * i]); const UniformBinder binder(*this, name); if (binder.location != -1) glCheck(GLEXT_glUniformMatrix4fv(binder.location, static_cast(length), GL_FALSE, contiguous.data())); } //////////////////////////////////////////////////////////// unsigned int Shader::getNativeHandle() const { return m_shaderProgram; } //////////////////////////////////////////////////////////// void Shader::bind(const Shader* shader) { const TransientContextLock lock; // Make sure that we can use shaders if (!isAvailable()) { err() << "Failed to bind or unbind shader: your system doesn't support shaders " << "(you should test Shader::isAvailable() before trying to use the Shader class)" << std::endl; return; } if (shader && shader->m_shaderProgram) { // Enable the program glCheck(GLEXT_glUseProgramObject(castToGlHandle(shader->m_shaderProgram))); // Bind the textures shader->bindTextures(); // Bind the current texture if (shader->m_currentTexture != -1) glCheck(GLEXT_glUniform1i(shader->m_currentTexture, 0)); } else { // Bind no shader glCheck(GLEXT_glUseProgramObject({})); } } //////////////////////////////////////////////////////////// bool Shader::isAvailable() { static const bool available = [] { const TransientContextLock contextLock; // Make sure that extensions are initialized priv::ensureExtensionsInit(); return GLEXT_multitexture && GLEXT_shading_language_100 && GLEXT_shader_objects && GLEXT_vertex_shader && GLEXT_fragment_shader; }(); return available; } //////////////////////////////////////////////////////////// bool Shader::isGeometryAvailable() { static const bool available = [] { const TransientContextLock contextLock; // Make sure that extensions are initialized priv::ensureExtensionsInit(); return isAvailable() && (GLEXT_geometry_shader4 || GLEXT_GL_VERSION_3_2); }(); return available; } //////////////////////////////////////////////////////////// bool Shader::compile(std::string_view vertexShaderCode, std::string_view geometryShaderCode, std::string_view fragmentShaderCode) { const TransientContextLock lock; // First make sure that we can use shaders if (!isAvailable()) { err() << "Failed to create a shader: your system doesn't support shaders " << "(you should test Shader::isAvailable() before trying to use the Shader class)" << std::endl; return false; } // Make sure we can use geometry shaders if (!geometryShaderCode.empty() && !isGeometryAvailable()) { err() << "Failed to create a shader: your system doesn't support geometry shaders " << "(you should test Shader::isGeometryAvailable() before trying to use geometry shaders)" << std::endl; return false; } // Create the program const GLEXT_GLhandle shaderProgram = glCheck(GLEXT_glCreateProgramObject()); // Helper function for shader creation const auto createAndAttachShader = [shaderProgram](GLenum shaderType, const char* shaderTypeStr, std::string_view shaderCode) { // Create and compile the shader const GLEXT_GLhandle shader = glCheck(GLEXT_glCreateShaderObject(shaderType)); const GLcharARB* sourceCode = shaderCode.data(); const auto sourceCodeLength = static_cast(shaderCode.length()); glCheck(GLEXT_glShaderSource(shader, 1, &sourceCode, &sourceCodeLength)); glCheck(GLEXT_glCompileShader(shader)); // Check the compile log GLint success = 0; glCheck(GLEXT_glGetObjectParameteriv(shader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); if (success == GL_FALSE) { std::array log{}; glCheck(GLEXT_glGetInfoLog(shader, static_cast(log.size()), nullptr, log.data())); err() << "Failed to compile " << shaderTypeStr << " shader:" << '\n' << log.data() << std::endl; glCheck(GLEXT_glDeleteObject(shader)); glCheck(GLEXT_glDeleteObject(shaderProgram)); return false; } // Attach the shader to the program, and delete it (not needed anymore) glCheck(GLEXT_glAttachObject(shaderProgram, shader)); glCheck(GLEXT_glDeleteObject(shader)); return true; }; // Create the vertex shader if needed if (!vertexShaderCode.empty()) if (!createAndAttachShader(GLEXT_GL_VERTEX_SHADER, "vertex", vertexShaderCode)) return false; // Create the geometry shader if needed if (!geometryShaderCode.empty()) if (!createAndAttachShader(GLEXT_GL_GEOMETRY_SHADER, "geometry", geometryShaderCode)) return false; // Create the fragment shader if needed if (!fragmentShaderCode.empty()) if (!createAndAttachShader(GLEXT_GL_FRAGMENT_SHADER, "fragment", fragmentShaderCode)) return false; // Link the program glCheck(GLEXT_glLinkProgram(shaderProgram)); // Check the link log GLint success = 0; glCheck(GLEXT_glGetObjectParameteriv(shaderProgram, GLEXT_GL_OBJECT_LINK_STATUS, &success)); if (success == GL_FALSE) { std::array log{}; glCheck(GLEXT_glGetInfoLog(shaderProgram, static_cast(log.size()), nullptr, log.data())); err() << "Failed to link shader:" << '\n' << log.data() << std::endl; glCheck(GLEXT_glDeleteObject(shaderProgram)); return false; } // Destroy the shader if it was already created if (m_shaderProgram) { glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); m_shaderProgram = 0; } // Reset the internal state m_currentTexture = -1; m_textures.clear(); m_uniforms.clear(); m_shaderProgram = castFromGlHandle(shaderProgram); // Force an OpenGL flush, so that the shader will appear updated // in all contexts immediately (solves problems in multi-threaded apps) glCheck(glFlush()); return true; } //////////////////////////////////////////////////////////// void Shader::bindTextures() const { auto it = m_textures.begin(); for (std::size_t i = 0; i < m_textures.size(); ++i) { const auto index = static_cast(i + 1); glCheck(GLEXT_glUniform1i(it->first, index)); glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0 + static_cast(index))); Texture::bind(it->second); ++it; } // Make sure that the texture unit which is left active is the number 0 glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); } //////////////////////////////////////////////////////////// int Shader::getUniformLocation(const std::string& name) { // Check the cache if (const auto it = m_uniforms.find(name); it != m_uniforms.end()) { // Already in cache, return it return it->second; } // Not in cache, request the location from OpenGL const int location = GLEXT_glGetUniformLocation(castToGlHandle(m_shaderProgram), name.c_str()); m_uniforms.try_emplace(name, location); if (location == -1) err() << "Uniform " << std::quoted(name) << " not found in shader" << std::endl; return location; } } // namespace sf #else // SFML_OPENGL_ES // OpenGL ES 1 doesn't support GLSL shaders at all, we have to provide an empty implementation namespace sf { //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& /* filename */, Type /* type */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& /* vertexShaderFilename */, const std::filesystem::path& /* fragmentShaderFilename */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(const std::filesystem::path& /* vertexShaderFilename */, const std::filesystem::path& /* geometryShaderFilename */, const std::filesystem::path& /* fragmentShaderFilename */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view /* shader */, Type /* type */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view /* vertexShader */, std::string_view /* fragmentShader */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(std::string_view /* vertexShader */, std::string_view /* geometryShader */, std::string_view /* fragmentShader */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& /* stream */, Type /* type */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::Shader(InputStream& /* vertexShaderStream */, InputStream& /* geometryShaderStream */, InputStream& /* fragmentShaderStream */) { throw sf::Exception("Shaders are not supported with OpenGL ES 1"); } //////////////////////////////////////////////////////////// Shader::~Shader() = default; //////////////////////////////////////////////////////////// Shader::Shader(Shader&& source) noexcept = default; //////////////////////////////////////////////////////////// Shader& Shader::operator=(Shader&& right) noexcept = default; //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, const std::filesystem::path& /* fragmentShaderFilename */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, const std::filesystem::path& /* geometryShaderFilename */, const std::filesystem::path& /* fragmentShaderFilename */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view /* shader */, Type /* type */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view /* vertexShader */, std::string_view /* fragmentShader */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromMemory(std::string_view /* vertexShader */, std::string_view /* geometryShader */, std::string_view /* fragmentShader */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& /* stream */, Type /* type */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */) { return false; } //////////////////////////////////////////////////////////// bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* geometryShaderStream */, InputStream& /* fragmentShaderStream */) { return false; } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, float) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, Glsl::Vec2) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Vec3&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Vec4&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, int) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, Glsl::Ivec2) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Ivec3&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Ivec4&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, bool) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, Glsl::Bvec2) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Bvec3&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Bvec4&) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Mat3& /* matrix */) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Glsl::Mat4& /* matrix */) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, const Texture& /* texture */) { } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& /* name */, CurrentTextureType) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const float* /* scalarArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const Glsl::Vec2* /* vectorArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const Glsl::Vec3* /* vectorArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const Glsl::Vec4* /* vectorArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const Glsl::Mat3* /* matrixArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& /* name */, const Glsl::Mat4* /* matrixArray */, std::size_t /* length */) { } //////////////////////////////////////////////////////////// unsigned int Shader::getNativeHandle() const { return 0; } //////////////////////////////////////////////////////////// void Shader::bind(const Shader* /* shader */) { } //////////////////////////////////////////////////////////// bool Shader::isAvailable() { return false; } //////////////////////////////////////////////////////////// bool Shader::isGeometryAvailable() { return false; } //////////////////////////////////////////////////////////// bool Shader::compile(std::string_view /* vertexShaderCode */, std::string_view /* geometryShaderCode */, std::string_view /* fragmentShaderCode */) { return false; } //////////////////////////////////////////////////////////// void Shader::bindTextures() const { } } // namespace sf #endif // SFML_OPENGL_ES