From 7234fc149bf21b12fbb10deec73025fdfec07808 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Sat, 18 May 2024 19:53:07 -0600 Subject: [PATCH] Remove default empty state of `sf::Shader` --- examples/island/Island.cpp | 26 +++--- examples/shader/Shader.cpp | 31 +++---- include/SFML/Graphics/Shader.hpp | 73 +++++++-------- src/SFML/Graphics/Shader.cpp | 150 ++++++++++++++++--------------- test/Graphics/Shader.test.cpp | 120 +++++++++++++------------ 5 files changed, 201 insertions(+), 199 deletions(-) diff --git a/examples/island/Island.cpp b/examples/island/Island.cpp index 0ce43c6f7..96ca0982e 100644 --- a/examples/island/Island.cpp +++ b/examples/island/Island.cpp @@ -94,11 +94,10 @@ int main() const auto font = sf::Font::loadFromFile("resources/tuffy.ttf").value(); // Create all of our graphics resources - sf::Text hudText(font); - sf::Text statusText(font); - sf::Shader terrainShader; - const sf::RenderStates terrainStates(&terrainShader); - sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static); + sf::Text hudText(font); + sf::Text statusText(font); + std::optional terrainShader; + sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static); // Set up our text drawables statusText.setCharacterSize(28); @@ -115,18 +114,13 @@ int main() // Staging buffer for our terrain data that we will upload to our VertexBuffer std::vector terrainStagingBuffer; - // Check whether the prerequisites are supported - bool prerequisitesSupported = sf::VertexBuffer::isAvailable() && sf::Shader::isAvailable(); - // Set up our graphics resources and set the status text accordingly - if (!prerequisitesSupported) + if (!sf::VertexBuffer::isAvailable() || !sf::Shader::isAvailable()) { statusText.setString("Shaders and/or Vertex Buffers Unsupported"); } - else if (!terrainShader.loadFromFile("resources/terrain.vert", "resources/terrain.frag")) + else if (!(terrainShader = sf::Shader::loadFromFile("resources/terrain.vert", "resources/terrain.frag"))) { - prerequisitesSupported = false; - statusText.setString("Failed to load shader program"); } else @@ -188,7 +182,7 @@ int main() } // Arrow key pressed: - if (prerequisitesSupported && event.is()) + if (terrainShader.has_value() && event.is()) { switch (event.getIf()->code) { @@ -218,7 +212,7 @@ int main() window.draw(statusText); - if (prerequisitesSupported) + if (terrainShader.has_value()) { { const std::lock_guard lock(workQueueMutex); @@ -238,8 +232,8 @@ int main() bufferUploadPending = false; } - terrainShader.setUniform("lightFactor", lightFactor); - window.draw(terrain, terrainStates); + terrainShader->setUniform("lightFactor", lightFactor); + window.draw(terrain, sf::RenderStates(&*terrainShader)); } } diff --git a/examples/shader/Shader.cpp b/examples/shader/Shader.cpp index c5694d74b..1096d2ae7 100644 --- a/examples/shader/Shader.cpp +++ b/examples/shader/Shader.cpp @@ -38,9 +38,6 @@ public: return false; m_sprite.emplace(m_texture); - // Load the shader - if (!m_shader.loadFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment)) - return false; m_shader.setUniform("texture", sf::Shader::CurrentTexture); return true; @@ -60,7 +57,7 @@ public: private: sf::Texture m_texture; std::optional m_sprite; - sf::Shader m_shader; + sf::Shader m_shader{sf::Shader::loadFromFile("resources/pixelate.frag", sf::Shader::Type::Fragment).value()}; }; @@ -99,8 +96,7 @@ public: m_text.setCharacterSize(22); m_text.setPosition({30.f, 20.f}); - // Load the shader - return m_shader.loadFromFile("resources/wave.vert", "resources/blur.frag"); + return true; } void onUpdate(float time, float x, float y) override @@ -118,7 +114,7 @@ public: private: sf::Text m_text; - sf::Shader m_shader; + sf::Shader m_shader{sf::Shader::loadFromFile("resources/wave.vert", "resources/blur.frag").value()}; }; @@ -150,8 +146,7 @@ public: m_points.append({{x, y}, {r, g, b}}); } - // Load the shader - return m_shader.loadFromFile("resources/storm.vert", "resources/blink.frag"); + return true; } void onUpdate(float time, float x, float y) override @@ -171,7 +166,7 @@ public: private: sf::VertexArray m_points; - sf::Shader m_shader; + sf::Shader m_shader{sf::Shader::loadFromFile("resources/storm.vert", "resources/blink.frag").value()}; }; @@ -211,9 +206,7 @@ public: m_entities.push_back(entity); } - // Load the shader - if (!m_shader.loadFromFile("resources/edge.frag", sf::Shader::Type::Fragment)) - return false; + // Set the shader uniform m_shader.setUniform("texture", sf::Shader::CurrentTexture); return true; @@ -254,7 +247,7 @@ private: sf::Texture m_entityTexture; std::optional m_backgroundSprite; std::vector m_entities; - sf::Shader m_shader; + sf::Shader m_shader{sf::Shader::loadFromFile("resources/edge.frag", sf::Shader::Type::Fragment).value()}; }; @@ -287,9 +280,6 @@ public: if (!m_logoTexture.loadFromFile("resources/logo.png")) return false; - // Load the shader - if (!m_shader.loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag")) - return false; m_shader.setUniform("texture", sf::Shader::CurrentTexture); // Set the render resolution (used for proper scaling) @@ -326,9 +316,10 @@ public: } private: - sf::Texture m_logoTexture; - sf::Transform m_transform; - sf::Shader m_shader; + sf::Texture m_logoTexture; + sf::Transform m_transform; + sf::Shader m_shader{ + sf::Shader::loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag").value()}; sf::VertexArray m_pointCloud; }; diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 4bf47b4f1..bb4914a99 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -83,14 +84,6 @@ public: // NOLINTNEXTLINE(readability-identifier-naming) static inline CurrentTextureType CurrentTexture; - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - /// This constructor creates an invalid shader. - /// - //////////////////////////////////////////////////////////// - Shader() = default; - //////////////////////////////////////////////////////////// /// \brief Destructor /// @@ -136,12 +129,12 @@ public: /// \param filename Path of the vertex, geometry or fragment shader file to load /// \param type Type of shader (vertex, geometry or fragment) /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromMemory, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromFile(const std::filesystem::path& filename, Type type); + [[nodiscard]] static std::optional loadFromFile(const std::filesystem::path& filename, Type type); //////////////////////////////////////////////////////////// /// \brief Load both the vertex and fragment shaders from files @@ -157,13 +150,13 @@ public: /// \param vertexShaderFilename Path of the vertex shader file to load /// \param fragmentShaderFilename Path of the fragment shader file to load /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromMemory, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromFile(const std::filesystem::path& vertexShaderFilename, - const std::filesystem::path& fragmentShaderFilename); + [[nodiscard]] static std::optional loadFromFile(const std::filesystem::path& vertexShaderFilename, + const std::filesystem::path& fragmentShaderFilename); //////////////////////////////////////////////////////////// /// \brief Load the vertex, geometry and fragment shaders from files @@ -180,14 +173,14 @@ public: /// \param geometryShaderFilename Path of the geometry shader file to load /// \param fragmentShaderFilename Path of the fragment shader file to load /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromMemory, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromFile(const std::filesystem::path& vertexShaderFilename, - const std::filesystem::path& geometryShaderFilename, - const std::filesystem::path& fragmentShaderFilename); + [[nodiscard]] static std::optional loadFromFile(const std::filesystem::path& vertexShaderFilename, + const std::filesystem::path& geometryShaderFilename, + const std::filesystem::path& fragmentShaderFilename); //////////////////////////////////////////////////////////// /// \brief Load the vertex, geometry or fragment shader from a source code in memory @@ -202,12 +195,12 @@ public: /// \param shader String containing the source code of the shader /// \param type Type of shader (vertex, geometry or fragment) /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromMemory(const std::string& shader, Type type); + [[nodiscard]] static std::optional loadFromMemory(const std::string& shader, Type type); //////////////////////////////////////////////////////////// /// \brief Load both the vertex and fragment shaders from source codes in memory @@ -223,12 +216,13 @@ public: /// \param vertexShader String containing the source code of the vertex shader /// \param fragmentShader String containing the source code of the fragment shader /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader); + [[nodiscard]] static std::optional loadFromMemory(const std::string& vertexShader, + const std::string& fragmentShader); //////////////////////////////////////////////////////////// /// \brief Load the vertex, geometry and fragment shaders from source codes in memory @@ -245,14 +239,14 @@ public: /// \param geometryShader String containing the source code of the geometry shader /// \param fragmentShader String containing the source code of the fragment shader /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromStream /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromMemory(const std::string& vertexShader, - const std::string& geometryShader, - const std::string& fragmentShader); + [[nodiscard]] static std::optional loadFromMemory(const std::string& vertexShader, + const std::string& geometryShader, + const std::string& fragmentShader); //////////////////////////////////////////////////////////// /// \brief Load the vertex, geometry or fragment shader from a custom stream @@ -267,12 +261,12 @@ public: /// \param stream Source stream to read from /// \param type Type of shader (vertex, geometry or fragment) /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromMemory /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromStream(InputStream& stream, Type type); + [[nodiscard]] static std::optional loadFromStream(InputStream& stream, Type type); //////////////////////////////////////////////////////////// /// \brief Load both the vertex and fragment shaders from custom streams @@ -288,12 +282,13 @@ public: /// \param vertexShaderStream Source stream to read the vertex shader from /// \param fragmentShaderStream Source stream to read the fragment shader from /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromMemory /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream); + [[nodiscard]] static std::optional loadFromStream(InputStream& vertexShaderStream, + InputStream& fragmentShaderStream); //////////////////////////////////////////////////////////// /// \brief Load the vertex, geometry and fragment shaders from custom streams @@ -310,14 +305,14 @@ public: /// \param geometryShaderStream Source stream to read the geometry shader from /// \param fragmentShaderStream Source stream to read the fragment shader from /// - /// \return True if loading succeeded, false if it failed + /// \return Shader if loading succeeded, `std::nullopt` if it failed /// /// \see loadFromFile, loadFromMemory /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool loadFromStream(InputStream& vertexShaderStream, - InputStream& geometryShaderStream, - InputStream& fragmentShaderStream); + [[nodiscard]] static std::optional loadFromStream(InputStream& vertexShaderStream, + InputStream& geometryShaderStream, + InputStream& fragmentShaderStream); //////////////////////////////////////////////////////////// /// \brief Specify value for \p float uniform @@ -650,6 +645,12 @@ public: static bool isGeometryAvailable(); private: + //////////////////////////////////////////////////////////// + /// \brief Construct from shader program + /// + //////////////////////////////////////////////////////////// + explicit Shader(unsigned int shaderProgram); + //////////////////////////////////////////////////////////// /// \brief Compile the shader(s) and create the program /// @@ -660,10 +661,12 @@ private: /// \param geometryShaderCode Source code of the geometry shader /// \param fragmentShaderCode Source code of the fragment shader /// - /// \return True on success, false if any error happened + /// \return Shader on success, `std::nullopt` if any error happened /// //////////////////////////////////////////////////////////// - [[nodiscard]] bool compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode); + [[nodiscard]] static std::optional compile(const char* vertexShaderCode, + const char* geometryShaderCode, + const char* fragmentShaderCode); //////////////////////////////////////////////////////////// /// \brief Bind all the textures used by the shader diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index f7d669ae7..3d7b0c5ff 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -269,14 +269,14 @@ Shader& Shader::operator=(Shader&& right) noexcept } //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& filename, Type type) +std::optional 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; + return std::nullopt; } // Compile the shader program @@ -290,15 +290,15 @@ bool Shader::loadFromFile(const std::filesystem::path& filename, Type type) //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, - const std::filesystem::path& fragmentShaderFilename) +std::optional 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; + return std::nullopt; } // Read the fragment shader file @@ -306,7 +306,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, if (!getFileContents(fragmentShaderFilename, fragmentShader)) { err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; - return false; + return std::nullopt; } // Compile the shader program @@ -315,16 +315,16 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, - const std::filesystem::path& geometryShaderFilename, - const std::filesystem::path& fragmentShaderFilename) +std::optional 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; + return std::nullopt; } // Read the geometry shader file @@ -332,7 +332,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, if (!getFileContents(geometryShaderFilename, geometryShader)) { err() << "Failed to open geometry shader file\n" << formatDebugPathInfo(geometryShaderFilename) << std::endl; - return false; + return std::nullopt; } // Read the fragment shader file @@ -340,7 +340,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, if (!getFileContents(fragmentShaderFilename, fragmentShader)) { err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; - return false; + return std::nullopt; } // Compile the shader program @@ -349,7 +349,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& shader, Type type) +std::optional Shader::loadFromMemory(const std::string& shader, Type type) { // Compile the shader program if (type == Type::Vertex) @@ -362,7 +362,7 @@ bool Shader::loadFromMemory(const std::string& shader, Type type) //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) +std::optional Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) { // Compile the shader program return compile(vertexShader.c_str(), nullptr, fragmentShader.c_str()); @@ -370,7 +370,9 @@ bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& geometryShader, const std::string& fragmentShader) +std::optional Shader::loadFromMemory(const std::string& vertexShader, + const std::string& geometryShader, + const std::string& fragmentShader) { // Compile the shader program return compile(vertexShader.c_str(), geometryShader.c_str(), fragmentShader.c_str()); @@ -378,14 +380,14 @@ bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& stream, Type type) +std::optional 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; + return std::nullopt; } // Compile the shader program @@ -399,14 +401,14 @@ bool Shader::loadFromStream(InputStream& stream, Type type) //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream) +std::optional 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; + return std::nullopt; } // Read the fragment shader code from the stream @@ -414,7 +416,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragme if (!getStreamContents(fragmentShaderStream, fragmentShader)) { err() << "Failed to read fragment shader from stream" << std::endl; - return false; + return std::nullopt; } // Compile the shader program @@ -423,14 +425,16 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragme //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream) +std::optional 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; + return std::nullopt; } // Read the geometry shader code from the stream @@ -438,7 +442,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet if (!getStreamContents(geometryShaderStream, geometryShader)) { err() << "Failed to read geometry shader from stream" << std::endl; - return false; + return std::nullopt; } // Read the fragment shader code from the stream @@ -446,7 +450,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet if (!getStreamContents(fragmentShaderStream, fragmentShader)) { err() << "Failed to read fragment shader from stream" << std::endl; - return false; + return std::nullopt; } // Compile the shader program @@ -768,7 +772,13 @@ bool Shader::isGeometryAvailable() //////////////////////////////////////////////////////////// -bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) +Shader::Shader(unsigned int shaderProgram) : m_shaderProgram(shaderProgram) +{ +} + + +//////////////////////////////////////////////////////////// +std::optional Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) { const TransientContextLock lock; @@ -777,7 +787,7 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod { 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; + return std::nullopt; } // Make sure we can use geometry shaders @@ -785,21 +795,9 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod { 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; + return std::nullopt; } - // 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(); - // Create the program GLEXT_GLhandle shaderProgram{}; glCheck(shaderProgram = GLEXT_glCreateProgramObject()); @@ -823,7 +821,7 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod err() << "Failed to compile vertex shader:" << '\n' << log << std::endl; glCheck(GLEXT_glDeleteObject(vertexShader)); glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; + return std::nullopt; } // Attach the shader to the program, and delete it (not needed anymore) @@ -849,7 +847,7 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod err() << "Failed to compile geometry shader:" << '\n' << log << std::endl; glCheck(GLEXT_glDeleteObject(geometryShader)); glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; + return std::nullopt; } // Attach the shader to the program, and delete it (not needed anymore) @@ -876,7 +874,7 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod err() << "Failed to compile fragment shader:" << '\n' << log << std::endl; glCheck(GLEXT_glDeleteObject(fragmentShader)); glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; + return std::nullopt; } // Attach the shader to the program, and delete it (not needed anymore) @@ -896,16 +894,14 @@ bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCod glCheck(GLEXT_glGetInfoLog(shaderProgram, sizeof(log), nullptr, log)); err() << "Failed to link shader:" << '\n' << log << std::endl; glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; + return std::nullopt; } - 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; + return Shader(castFromGlHandle(shaderProgram)); } @@ -970,72 +966,72 @@ Shader& Shader::operator=(Shader&& right) noexcept = default; //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */) +std::optional Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, - const std::filesystem::path& /* fragmentShaderFilename */) +std::optional Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, + const std::filesystem::path& /* fragmentShaderFilename */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, - const std::filesystem::path& /* geometryShaderFilename */, - const std::filesystem::path& /* fragmentShaderFilename */) +std::optional Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, + const std::filesystem::path& /* geometryShaderFilename */, + const std::filesystem::path& /* fragmentShaderFilename */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& /* shader */, Type /* type */) +std::optional Shader::loadFromMemory(const std::string& /* shader */, Type /* type */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& /* vertexShader */, const std::string& /* fragmentShader */) +std::optional Shader::loadFromMemory(const std::string& /* vertexShader */, const std::string& /* fragmentShader */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& /* vertexShader */, - const std::string& /* geometryShader */, - const std::string& /* fragmentShader */) +std::optional Shader::loadFromMemory(const std::string& /* vertexShader */, + const std::string& /* geometryShader */, + const std::string& /* fragmentShader */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& /* stream */, Type /* type */) +std::optional Shader::loadFromStream(InputStream& /* stream */, Type /* type */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */) +std::optional Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */) { - return false; + return std::nullopt; } //////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, - InputStream& /* geometryShaderStream */, - InputStream& /* fragmentShaderStream */) +std::optional Shader::loadFromStream(InputStream& /* vertexShaderStream */, + InputStream& /* geometryShaderStream */, + InputStream& /* fragmentShaderStream */) { - return false; + return std::nullopt; } @@ -1199,9 +1195,17 @@ bool Shader::isGeometryAvailable() //////////////////////////////////////////////////////////// -bool Shader::compile(const char* /* vertexShaderCode */, const char* /* geometryShaderCode */, const char* /* fragmentShaderCode */) +Shader::Shader(unsigned int shaderProgram) : m_shaderProgram(shaderProgram) { - return false; +} + + +//////////////////////////////////////////////////////////// +std::optional Shader::compile(const char* /* vertexShaderCode */, + const char* /* geometryShaderCode */, + const char* /* fragmentShaderCode */) +{ + return std::nullopt; } diff --git a/test/Graphics/Shader.test.cpp b/test/Graphics/Shader.test.cpp index 7fbbce449..7557f0143 100644 --- a/test/Graphics/Shader.test.cpp +++ b/test/Graphics/Shader.test.cpp @@ -147,12 +147,11 @@ TEST_CASE("[Graphics] sf::Shader (Dummy Implementation)", skipShaderDummyTests() SECTION("loadFromMemory()") { - sf::Shader shader; - CHECK_FALSE(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex)); - CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); - CHECK_FALSE(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment)); - CHECK_FALSE(shader.loadFromMemory(vertexSource, fragmentSource)); - CHECK_FALSE(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource)); + CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value()); + CHECK_FALSE(sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry).has_value()); + CHECK_FALSE(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value()); + CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, fragmentSource).has_value()); + CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, geometrySource, fragmentSource).has_value()); } } @@ -160,84 +159,90 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests()) { SECTION("Type traits") { + STATIC_CHECK(!std::is_default_constructible_v); STATIC_CHECK(!std::is_copy_constructible_v); STATIC_CHECK(!std::is_copy_assignable_v); STATIC_CHECK(std::is_nothrow_move_constructible_v); STATIC_CHECK(std::is_nothrow_move_assignable_v); } - SECTION("Construction") - { - const sf::Shader shader; - CHECK(shader.getNativeHandle() == 0); - } - SECTION("Move semantics") { SECTION("Construction") { - sf::Shader movedShader; + sf::Shader movedShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value(); const sf::Shader shader = std::move(movedShader); - CHECK(shader.getNativeHandle() == 0); + CHECK(shader.getNativeHandle() != 0); } SECTION("Assignment") { - sf::Shader movedShader; - sf::Shader shader; - shader = std::move(movedShader); - CHECK(shader.getNativeHandle() == 0); + sf::Shader movedShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value(); + sf::Shader shader = sf::Shader::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment).value(); + shader = std::move(movedShader); + CHECK(shader.getNativeHandle() != 0); } } SECTION("loadFromFile()") { - sf::Shader shader; - SECTION("One shader") { - CHECK(!shader.loadFromFile("does-not-exist.vert", sf::Shader::Type::Vertex)); + CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", sf::Shader::Type::Vertex)); - CHECK(shader.loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isAvailable()); + const auto vertexShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex); + CHECK(vertexShader.has_value() == sf::Shader::isAvailable()); + if (vertexShader) + CHECK(static_cast(vertexShader->getNativeHandle()) == sf::Shader::isAvailable()); - CHECK(shader.loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isAvailable()); + const auto fragmentShader = sf::Shader::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment); + CHECK(fragmentShader.has_value() == sf::Shader::isAvailable()); + if (fragmentShader) + CHECK(static_cast(fragmentShader->getNativeHandle()) == sf::Shader::isAvailable()); } SECTION("Two shaders") { - CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.frag")); - CHECK(!shader.loadFromFile("Graphics/shader.vert", "does-not-exist.frag")); - CHECK(shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.frag") == sf::Shader::isAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isAvailable()); + CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", "Graphics/shader.frag")); + CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "does-not-exist.frag")); + + const auto shader = sf::Shader::loadFromFile("Graphics/shader.vert", "Graphics/shader.frag"); + CHECK(shader.has_value() == sf::Shader::isAvailable()); + if (shader) + CHECK(static_cast(shader->getNativeHandle()) == sf::Shader::isAvailable()); } SECTION("Three shaders") { - CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag")); - CHECK(!shader.loadFromFile("Graphics/shader.vert", "does-not-exist.geom", "Graphics/shader.frag")); - CHECK(!shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "does-not-exist.frag")); - CHECK(shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "Graphics/shader.frag") == - sf::Shader::isGeometryAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable()); + CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag")); + CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "does-not-exist.geom", "Graphics/shader.frag")); + CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "Graphics/shader.geom", "does-not-exist.frag")); + + const auto shader = sf::Shader::loadFromFile("Graphics/shader.vert", + "Graphics/shader.geom", + "Graphics/shader.frag"); + CHECK(shader.has_value() == sf::Shader::isGeometryAvailable()); + if (shader) + CHECK(static_cast(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable()); } } SECTION("loadFromMemory()") { - sf::Shader shader; - CHECK(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); - CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); - CHECK(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); - CHECK(shader.loadFromMemory(vertexSource, fragmentSource) == sf::Shader::isAvailable()); - CHECK(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource) == sf::Shader::isGeometryAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isAvailable()); + CHECK(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value() == sf::Shader::isAvailable()); + CHECK(!sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); + CHECK(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value() == + sf::Shader::isAvailable()); + CHECK(sf::Shader::loadFromMemory(vertexSource, fragmentSource).has_value() == sf::Shader::isAvailable()); + + const auto shader = sf::Shader::loadFromMemory(vertexSource, geometrySource, fragmentSource); + CHECK(shader.has_value() == sf::Shader::isGeometryAvailable()); + if (shader) + CHECK(static_cast(shader->getNativeHandle()) == sf::Shader::isAvailable()); } SECTION("loadFromStream()") { - sf::Shader shader; sf::FileInputStream vertexShaderStream; REQUIRE(vertexShaderStream.open("Graphics/shader.vert")); @@ -251,26 +256,31 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests()) SECTION("One shader") { - REQUIRE(!shader.loadFromStream(emptyStream, sf::Shader::Type::Vertex)); - REQUIRE(shader.loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); - REQUIRE(shader.loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); + CHECK(!sf::Shader::loadFromStream(emptyStream, sf::Shader::Type::Vertex)); + CHECK(sf::Shader::loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex).has_value() == + sf::Shader::isAvailable()); + CHECK(sf::Shader::loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment).has_value() == + sf::Shader::isAvailable()); } SECTION("Two shaders") { - REQUIRE(!shader.loadFromStream(emptyStream, fragmentShaderStream)); - REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream)); - REQUIRE(shader.loadFromStream(vertexShaderStream, fragmentShaderStream) == sf::Shader::isAvailable()); + CHECK(!sf::Shader::loadFromStream(emptyStream, fragmentShaderStream)); + CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream)); + CHECK(sf::Shader::loadFromStream(vertexShaderStream, fragmentShaderStream).has_value() == + sf::Shader::isAvailable()); } SECTION("Three shaders") { - REQUIRE(!shader.loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream)); - REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream)); - REQUIRE(!shader.loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream)); - REQUIRE(shader.loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream) == - sf::Shader::isGeometryAvailable()); - CHECK(static_cast(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable()); + CHECK(!sf::Shader::loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream)); + CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream)); + CHECK(!sf::Shader::loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream)); + + const auto shader = sf::Shader::loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream); + CHECK(shader.has_value() == sf::Shader::isGeometryAvailable()); + if (shader) + CHECK(static_cast(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable()); } } }