Remove default empty state of sf::Shader

This commit is contained in:
Chris Thrasher 2024-05-18 19:53:07 -06:00
parent 504b850f03
commit 7234fc149b
5 changed files with 201 additions and 199 deletions

View File

@ -96,8 +96,7 @@ int main()
// Create all of our graphics resources // Create all of our graphics resources
sf::Text hudText(font); sf::Text hudText(font);
sf::Text statusText(font); sf::Text statusText(font);
sf::Shader terrainShader; std::optional<sf::Shader> terrainShader;
const sf::RenderStates terrainStates(&terrainShader);
sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static); sf::VertexBuffer terrain(sf::PrimitiveType::Triangles, sf::VertexBuffer::Usage::Static);
// Set up our text drawables // Set up our text drawables
@ -115,18 +114,13 @@ int main()
// Staging buffer for our terrain data that we will upload to our VertexBuffer // Staging buffer for our terrain data that we will upload to our VertexBuffer
std::vector<sf::Vertex> terrainStagingBuffer; std::vector<sf::Vertex> 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 // 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"); 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"); statusText.setString("Failed to load shader program");
} }
else else
@ -188,7 +182,7 @@ int main()
} }
// Arrow key pressed: // Arrow key pressed:
if (prerequisitesSupported && event.is<sf::Event::KeyPressed>()) if (terrainShader.has_value() && event.is<sf::Event::KeyPressed>())
{ {
switch (event.getIf<sf::Event::KeyPressed>()->code) switch (event.getIf<sf::Event::KeyPressed>()->code)
{ {
@ -218,7 +212,7 @@ int main()
window.draw(statusText); window.draw(statusText);
if (prerequisitesSupported) if (terrainShader.has_value())
{ {
{ {
const std::lock_guard lock(workQueueMutex); const std::lock_guard lock(workQueueMutex);
@ -238,8 +232,8 @@ int main()
bufferUploadPending = false; bufferUploadPending = false;
} }
terrainShader.setUniform("lightFactor", lightFactor); terrainShader->setUniform("lightFactor", lightFactor);
window.draw(terrain, terrainStates); window.draw(terrain, sf::RenderStates(&*terrainShader));
} }
} }

View File

@ -38,9 +38,6 @@ public:
return false; return false;
m_sprite.emplace(m_texture); 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); m_shader.setUniform("texture", sf::Shader::CurrentTexture);
return true; return true;
@ -60,7 +57,7 @@ public:
private: private:
sf::Texture m_texture; sf::Texture m_texture;
std::optional<sf::Sprite> m_sprite; std::optional<sf::Sprite> 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.setCharacterSize(22);
m_text.setPosition({30.f, 20.f}); m_text.setPosition({30.f, 20.f});
// Load the shader return true;
return m_shader.loadFromFile("resources/wave.vert", "resources/blur.frag");
} }
void onUpdate(float time, float x, float y) override void onUpdate(float time, float x, float y) override
@ -118,7 +114,7 @@ public:
private: private:
sf::Text m_text; 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}}); m_points.append({{x, y}, {r, g, b}});
} }
// Load the shader return true;
return m_shader.loadFromFile("resources/storm.vert", "resources/blink.frag");
} }
void onUpdate(float time, float x, float y) override void onUpdate(float time, float x, float y) override
@ -171,7 +166,7 @@ public:
private: private:
sf::VertexArray m_points; 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); m_entities.push_back(entity);
} }
// Load the shader // Set the shader uniform
if (!m_shader.loadFromFile("resources/edge.frag", sf::Shader::Type::Fragment))
return false;
m_shader.setUniform("texture", sf::Shader::CurrentTexture); m_shader.setUniform("texture", sf::Shader::CurrentTexture);
return true; return true;
@ -254,7 +247,7 @@ private:
sf::Texture m_entityTexture; sf::Texture m_entityTexture;
std::optional<sf::Sprite> m_backgroundSprite; std::optional<sf::Sprite> m_backgroundSprite;
std::vector<sf::Sprite> m_entities; std::vector<sf::Sprite> 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")) if (!m_logoTexture.loadFromFile("resources/logo.png"))
return false; 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); m_shader.setUniform("texture", sf::Shader::CurrentTexture);
// Set the render resolution (used for proper scaling) // Set the render resolution (used for proper scaling)
@ -328,7 +318,8 @@ public:
private: private:
sf::Texture m_logoTexture; sf::Texture m_logoTexture;
sf::Transform m_transform; sf::Transform m_transform;
sf::Shader m_shader; sf::Shader m_shader{
sf::Shader::loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag").value()};
sf::VertexArray m_pointCloud; sf::VertexArray m_pointCloud;
}; };

View File

@ -34,6 +34,7 @@
#include <SFML/Window/GlResource.hpp> #include <SFML/Window/GlResource.hpp>
#include <filesystem> #include <filesystem>
#include <optional>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -83,14 +84,6 @@ public:
// NOLINTNEXTLINE(readability-identifier-naming) // NOLINTNEXTLINE(readability-identifier-naming)
static inline CurrentTextureType CurrentTexture; static inline CurrentTextureType CurrentTexture;
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates an invalid shader.
///
////////////////////////////////////////////////////////////
Shader() = default;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destructor /// \brief Destructor
/// ///
@ -136,12 +129,12 @@ public:
/// \param filename Path of the vertex, geometry or fragment shader file to load /// \param filename Path of the vertex, geometry or fragment shader file to load
/// \param type Type of shader (vertex, geometry or fragment) /// \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 /// \see loadFromMemory, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& filename, Type type); [[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& filename, Type type);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from files /// \brief Load both the vertex and fragment shaders from files
@ -157,12 +150,12 @@ public:
/// \param vertexShaderFilename Path of the vertex shader file to load /// \param vertexShaderFilename Path of the vertex shader file to load
/// \param fragmentShaderFilename Path of the fragment 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 /// \see loadFromMemory, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& vertexShaderFilename, [[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename); const std::filesystem::path& fragmentShaderFilename);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -180,12 +173,12 @@ public:
/// \param geometryShaderFilename Path of the geometry shader file to load /// \param geometryShaderFilename Path of the geometry shader file to load
/// \param fragmentShaderFilename Path of the fragment 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 /// \see loadFromMemory, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromFile(const std::filesystem::path& vertexShaderFilename, [[nodiscard]] static std::optional<Shader> loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename, const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename); const std::filesystem::path& fragmentShaderFilename);
@ -202,12 +195,12 @@ public:
/// \param shader String containing the source code of the shader /// \param shader String containing the source code of the shader
/// \param type Type of shader (vertex, geometry or fragment) /// \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 /// \see loadFromFile, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const std::string& shader, Type type); [[nodiscard]] static std::optional<Shader> loadFromMemory(const std::string& shader, Type type);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from source codes in memory /// \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 vertexShader String containing the source code of the vertex shader
/// \param fragmentShader String containing the source code of the fragment 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 /// \see loadFromFile, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader); [[nodiscard]] static std::optional<Shader> loadFromMemory(const std::string& vertexShader,
const std::string& fragmentShader);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry and fragment shaders from source codes in memory /// \brief Load the vertex, geometry and fragment shaders from source codes in memory
@ -245,12 +239,12 @@ public:
/// \param geometryShader String containing the source code of the geometry shader /// \param geometryShader String containing the source code of the geometry shader
/// \param fragmentShader String containing the source code of the fragment 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 /// \see loadFromFile, loadFromStream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromMemory(const std::string& vertexShader, [[nodiscard]] static std::optional<Shader> loadFromMemory(const std::string& vertexShader,
const std::string& geometryShader, const std::string& geometryShader,
const std::string& fragmentShader); const std::string& fragmentShader);
@ -267,12 +261,12 @@ public:
/// \param stream Source stream to read from /// \param stream Source stream to read from
/// \param type Type of shader (vertex, geometry or fragment) /// \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 /// \see loadFromFile, loadFromMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& stream, Type type); [[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& stream, Type type);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load both the vertex and fragment shaders from custom streams /// \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 vertexShaderStream Source stream to read the vertex shader from
/// \param fragmentShaderStream Source stream to read the fragment 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 /// \see loadFromFile, loadFromMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream); [[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& vertexShaderStream,
InputStream& fragmentShaderStream);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Load the vertex, geometry and fragment shaders from custom streams /// \brief Load the vertex, geometry and fragment shaders from custom streams
@ -310,12 +305,12 @@ public:
/// \param geometryShaderStream Source stream to read the geometry shader from /// \param geometryShaderStream Source stream to read the geometry shader from
/// \param fragmentShaderStream Source stream to read the fragment 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 /// \see loadFromFile, loadFromMemory
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] bool loadFromStream(InputStream& vertexShaderStream, [[nodiscard]] static std::optional<Shader> loadFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream, InputStream& geometryShaderStream,
InputStream& fragmentShaderStream); InputStream& fragmentShaderStream);
@ -650,6 +645,12 @@ public:
static bool isGeometryAvailable(); static bool isGeometryAvailable();
private: private:
////////////////////////////////////////////////////////////
/// \brief Construct from shader program
///
////////////////////////////////////////////////////////////
explicit Shader(unsigned int shaderProgram);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Compile the shader(s) and create the program /// \brief Compile the shader(s) and create the program
/// ///
@ -660,10 +661,12 @@ private:
/// \param geometryShaderCode Source code of the geometry shader /// \param geometryShaderCode Source code of the geometry shader
/// \param fragmentShaderCode Source code of the fragment 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<Shader> compile(const char* vertexShaderCode,
const char* geometryShaderCode,
const char* fragmentShaderCode);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Bind all the textures used by the shader /// \brief Bind all the textures used by the shader

View File

@ -269,14 +269,14 @@ Shader& Shader::operator=(Shader&& right) noexcept
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& filename, Type type) std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& filename, Type type)
{ {
// Read the file // Read the file
std::vector<char> shader; std::vector<char> shader;
if (!getFileContents(filename, shader)) if (!getFileContents(filename, shader))
{ {
err() << "Failed to open shader file\n" << formatDebugPathInfo(filename) << std::endl; err() << "Failed to open shader file\n" << formatDebugPathInfo(filename) << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // Compile the shader program
@ -290,7 +290,7 @@ bool Shader::loadFromFile(const std::filesystem::path& filename, Type type)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& fragmentShaderFilename) const std::filesystem::path& fragmentShaderFilename)
{ {
// Read the vertex shader file // Read the vertex shader file
@ -298,7 +298,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
if (!getFileContents(vertexShaderFilename, vertexShader)) if (!getFileContents(vertexShaderFilename, vertexShader))
{ {
err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl; err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl;
return false; return std::nullopt;
} }
// Read the fragment shader file // Read the fragment shader file
@ -306,7 +306,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
if (!getFileContents(fragmentShaderFilename, fragmentShader)) if (!getFileContents(fragmentShaderFilename, fragmentShader))
{ {
err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // Compile the shader program
@ -315,7 +315,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename, std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
const std::filesystem::path& geometryShaderFilename, const std::filesystem::path& geometryShaderFilename,
const std::filesystem::path& fragmentShaderFilename) const std::filesystem::path& fragmentShaderFilename)
{ {
@ -324,7 +324,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
if (!getFileContents(vertexShaderFilename, vertexShader)) if (!getFileContents(vertexShaderFilename, vertexShader))
{ {
err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl; err() << "Failed to open vertex shader file\n" << formatDebugPathInfo(vertexShaderFilename) << std::endl;
return false; return std::nullopt;
} }
// Read the geometry shader file // Read the geometry shader file
@ -332,7 +332,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
if (!getFileContents(geometryShaderFilename, geometryShader)) if (!getFileContents(geometryShaderFilename, geometryShader))
{ {
err() << "Failed to open geometry shader file\n" << formatDebugPathInfo(geometryShaderFilename) << std::endl; err() << "Failed to open geometry shader file\n" << formatDebugPathInfo(geometryShaderFilename) << std::endl;
return false; return std::nullopt;
} }
// Read the fragment shader file // Read the fragment shader file
@ -340,7 +340,7 @@ bool Shader::loadFromFile(const std::filesystem::path& vertexShaderFilename,
if (!getFileContents(fragmentShaderFilename, fragmentShader)) if (!getFileContents(fragmentShaderFilename, fragmentShader))
{ {
err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl; err() << "Failed to open fragment shader file\n" << formatDebugPathInfo(fragmentShaderFilename) << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // 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> Shader::loadFromMemory(const std::string& shader, Type type)
{ {
// Compile the shader program // Compile the shader program
if (type == Type::Vertex) 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> Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader)
{ {
// Compile the shader program // Compile the shader program
return compile(vertexShader.c_str(), nullptr, fragmentShader.c_str()); 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> Shader::loadFromMemory(const std::string& vertexShader,
const std::string& geometryShader,
const std::string& fragmentShader)
{ {
// Compile the shader program // Compile the shader program
return compile(vertexShader.c_str(), geometryShader.c_str(), fragmentShader.c_str()); 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> Shader::loadFromStream(InputStream& stream, Type type)
{ {
// Read the shader code from the stream // Read the shader code from the stream
std::vector<char> shader; std::vector<char> shader;
if (!getStreamContents(stream, shader)) if (!getStreamContents(stream, shader))
{ {
err() << "Failed to read shader from stream" << std::endl; err() << "Failed to read shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // 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> Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream)
{ {
// Read the vertex shader code from the stream // Read the vertex shader code from the stream
std::vector<char> vertexShader; std::vector<char> vertexShader;
if (!getStreamContents(vertexShaderStream, vertexShader)) if (!getStreamContents(vertexShaderStream, vertexShader))
{ {
err() << "Failed to read vertex shader from stream" << std::endl; err() << "Failed to read vertex shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Read the fragment shader code from the stream // Read the fragment shader code from the stream
@ -414,7 +416,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragme
if (!getStreamContents(fragmentShaderStream, fragmentShader)) if (!getStreamContents(fragmentShaderStream, fragmentShader))
{ {
err() << "Failed to read fragment shader from stream" << std::endl; err() << "Failed to read fragment shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // 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> Shader::loadFromStream(InputStream& vertexShaderStream,
InputStream& geometryShaderStream,
InputStream& fragmentShaderStream)
{ {
// Read the vertex shader code from the stream // Read the vertex shader code from the stream
std::vector<char> vertexShader; std::vector<char> vertexShader;
if (!getStreamContents(vertexShaderStream, vertexShader)) if (!getStreamContents(vertexShaderStream, vertexShader))
{ {
err() << "Failed to read vertex shader from stream" << std::endl; err() << "Failed to read vertex shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Read the geometry shader code from the stream // Read the geometry shader code from the stream
@ -438,7 +442,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet
if (!getStreamContents(geometryShaderStream, geometryShader)) if (!getStreamContents(geometryShaderStream, geometryShader))
{ {
err() << "Failed to read geometry shader from stream" << std::endl; err() << "Failed to read geometry shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Read the fragment shader code from the stream // Read the fragment shader code from the stream
@ -446,7 +450,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet
if (!getStreamContents(fragmentShaderStream, fragmentShader)) if (!getStreamContents(fragmentShaderStream, fragmentShader))
{ {
err() << "Failed to read fragment shader from stream" << std::endl; err() << "Failed to read fragment shader from stream" << std::endl;
return false; return std::nullopt;
} }
// Compile the shader program // 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> Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode)
{ {
const TransientContextLock lock; 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 " 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; << "(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 // 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 " 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; << "(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 // Create the program
GLEXT_GLhandle shaderProgram{}; GLEXT_GLhandle shaderProgram{};
glCheck(shaderProgram = GLEXT_glCreateProgramObject()); 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; err() << "Failed to compile vertex shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(vertexShader)); glCheck(GLEXT_glDeleteObject(vertexShader));
glCheck(GLEXT_glDeleteObject(shaderProgram)); glCheck(GLEXT_glDeleteObject(shaderProgram));
return false; return std::nullopt;
} }
// Attach the shader to the program, and delete it (not needed anymore) // 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; err() << "Failed to compile geometry shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(geometryShader)); glCheck(GLEXT_glDeleteObject(geometryShader));
glCheck(GLEXT_glDeleteObject(shaderProgram)); glCheck(GLEXT_glDeleteObject(shaderProgram));
return false; return std::nullopt;
} }
// Attach the shader to the program, and delete it (not needed anymore) // 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; err() << "Failed to compile fragment shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(fragmentShader)); glCheck(GLEXT_glDeleteObject(fragmentShader));
glCheck(GLEXT_glDeleteObject(shaderProgram)); glCheck(GLEXT_glDeleteObject(shaderProgram));
return false; return std::nullopt;
} }
// Attach the shader to the program, and delete it (not needed anymore) // 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)); glCheck(GLEXT_glGetInfoLog(shaderProgram, sizeof(log), nullptr, log));
err() << "Failed to link shader:" << '\n' << log << std::endl; err() << "Failed to link shader:" << '\n' << log << std::endl;
glCheck(GLEXT_glDeleteObject(shaderProgram)); glCheck(GLEXT_glDeleteObject(shaderProgram));
return false; return std::nullopt;
} }
m_shaderProgram = castFromGlHandle(shaderProgram);
// Force an OpenGL flush, so that the shader will appear updated // Force an OpenGL flush, so that the shader will appear updated
// in all contexts immediately (solves problems in multi-threaded apps) // in all contexts immediately (solves problems in multi-threaded apps)
glCheck(glFlush()); 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> Shader::loadFromFile(const std::filesystem::path& /* filename */, Type /* type */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */) const std::filesystem::path& /* fragmentShaderFilename */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */, std::optional<Shader> Shader::loadFromFile(const std::filesystem::path& /* vertexShaderFilename */,
const std::filesystem::path& /* geometryShaderFilename */, const std::filesystem::path& /* geometryShaderFilename */,
const std::filesystem::path& /* fragmentShaderFilename */) const std::filesystem::path& /* fragmentShaderFilename */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromMemory(const std::string& /* shader */, Type /* type */) std::optional<Shader> 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> Shader::loadFromMemory(const std::string& /* vertexShader */, const std::string& /* fragmentShader */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromMemory(const std::string& /* vertexShader */, std::optional<Shader> Shader::loadFromMemory(const std::string& /* vertexShader */,
const std::string& /* geometryShader */, const std::string& /* geometryShader */,
const std::string& /* fragmentShader */) const std::string& /* fragmentShader */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* stream */, Type /* type */) std::optional<Shader> Shader::loadFromStream(InputStream& /* stream */, Type /* type */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */) std::optional<Shader> Shader::loadFromStream(InputStream& /* vertexShaderStream */, InputStream& /* fragmentShaderStream */)
{ {
return false; return std::nullopt;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
bool Shader::loadFromStream(InputStream& /* vertexShaderStream */, std::optional<Shader> Shader::loadFromStream(InputStream& /* vertexShaderStream */,
InputStream& /* geometryShaderStream */, InputStream& /* geometryShaderStream */,
InputStream& /* fragmentShaderStream */) 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> Shader::compile(const char* /* vertexShaderCode */,
const char* /* geometryShaderCode */,
const char* /* fragmentShaderCode */)
{
return std::nullopt;
} }

View File

@ -147,12 +147,11 @@ TEST_CASE("[Graphics] sf::Shader (Dummy Implementation)", skipShaderDummyTests()
SECTION("loadFromMemory()") SECTION("loadFromMemory()")
{ {
sf::Shader shader; CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value());
CHECK_FALSE(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex)); CHECK_FALSE(sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry).has_value());
CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); CHECK_FALSE(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value());
CHECK_FALSE(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment)); CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, fragmentSource).has_value());
CHECK_FALSE(shader.loadFromMemory(vertexSource, fragmentSource)); CHECK_FALSE(sf::Shader::loadFromMemory(vertexSource, geometrySource, fragmentSource).has_value());
CHECK_FALSE(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource));
} }
} }
@ -160,84 +159,90 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
{ {
SECTION("Type traits") SECTION("Type traits")
{ {
STATIC_CHECK(!std::is_default_constructible_v<sf::Shader>);
STATIC_CHECK(!std::is_copy_constructible_v<sf::Shader>); STATIC_CHECK(!std::is_copy_constructible_v<sf::Shader>);
STATIC_CHECK(!std::is_copy_assignable_v<sf::Shader>); STATIC_CHECK(!std::is_copy_assignable_v<sf::Shader>);
STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::Shader>); STATIC_CHECK(std::is_nothrow_move_constructible_v<sf::Shader>);
STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Shader>); STATIC_CHECK(std::is_nothrow_move_assignable_v<sf::Shader>);
} }
SECTION("Construction")
{
const sf::Shader shader;
CHECK(shader.getNativeHandle() == 0);
}
SECTION("Move semantics") SECTION("Move semantics")
{ {
SECTION("Construction") 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); const sf::Shader shader = std::move(movedShader);
CHECK(shader.getNativeHandle() == 0); CHECK(shader.getNativeHandle() != 0);
} }
SECTION("Assignment") SECTION("Assignment")
{ {
sf::Shader movedShader; sf::Shader movedShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex).value();
sf::Shader shader; sf::Shader shader = sf::Shader::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment).value();
shader = std::move(movedShader); shader = std::move(movedShader);
CHECK(shader.getNativeHandle() == 0); CHECK(shader.getNativeHandle() != 0);
} }
} }
SECTION("loadFromFile()") SECTION("loadFromFile()")
{ {
sf::Shader shader;
SECTION("One 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()); const auto vertexShader = sf::Shader::loadFromFile("Graphics/shader.vert", sf::Shader::Type::Vertex);
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable()); CHECK(vertexShader.has_value() == sf::Shader::isAvailable());
if (vertexShader)
CHECK(static_cast<bool>(vertexShader->getNativeHandle()) == sf::Shader::isAvailable());
CHECK(shader.loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); const auto fragmentShader = sf::Shader::loadFromFile("Graphics/shader.frag", sf::Shader::Type::Fragment);
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable()); CHECK(fragmentShader.has_value() == sf::Shader::isAvailable());
if (fragmentShader)
CHECK(static_cast<bool>(fragmentShader->getNativeHandle()) == sf::Shader::isAvailable());
} }
SECTION("Two shaders") SECTION("Two shaders")
{ {
CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.frag")); CHECK(!sf::Shader::loadFromFile("does-not-exist.vert", "Graphics/shader.frag"));
CHECK(!shader.loadFromFile("Graphics/shader.vert", "does-not-exist.frag")); CHECK(!sf::Shader::loadFromFile("Graphics/shader.vert", "does-not-exist.frag"));
CHECK(shader.loadFromFile("Graphics/shader.vert", "Graphics/shader.frag") == sf::Shader::isAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable()); const auto shader = sf::Shader::loadFromFile("Graphics/shader.vert", "Graphics/shader.frag");
CHECK(shader.has_value() == sf::Shader::isAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isAvailable());
} }
SECTION("Three shaders") SECTION("Three shaders")
{ {
CHECK(!shader.loadFromFile("does-not-exist.vert", "Graphics/shader.geom", "Graphics/shader.frag")); CHECK(!sf::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(!sf::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(!sf::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()); const auto shader = sf::Shader::loadFromFile("Graphics/shader.vert",
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable()); "Graphics/shader.geom",
"Graphics/shader.frag");
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable());
} }
} }
SECTION("loadFromMemory()") SECTION("loadFromMemory()")
{ {
sf::Shader shader; CHECK(sf::Shader::loadFromMemory(vertexSource, sf::Shader::Type::Vertex).has_value() == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); CHECK(!sf::Shader::loadFromMemory(geometrySource, sf::Shader::Type::Geometry));
CHECK_FALSE(shader.loadFromMemory(geometrySource, sf::Shader::Type::Geometry)); CHECK(sf::Shader::loadFromMemory(fragmentSource, sf::Shader::Type::Fragment).has_value() ==
CHECK(shader.loadFromMemory(fragmentSource, sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, fragmentSource) == sf::Shader::isAvailable()); CHECK(sf::Shader::loadFromMemory(vertexSource, fragmentSource).has_value() == sf::Shader::isAvailable());
CHECK(shader.loadFromMemory(vertexSource, geometrySource, fragmentSource) == sf::Shader::isGeometryAvailable());
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isAvailable()); const auto shader = sf::Shader::loadFromMemory(vertexSource, geometrySource, fragmentSource);
CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isAvailable());
} }
SECTION("loadFromStream()") SECTION("loadFromStream()")
{ {
sf::Shader shader;
sf::FileInputStream vertexShaderStream; sf::FileInputStream vertexShaderStream;
REQUIRE(vertexShaderStream.open("Graphics/shader.vert")); REQUIRE(vertexShaderStream.open("Graphics/shader.vert"));
@ -251,26 +256,31 @@ TEST_CASE("[Graphics] sf::Shader", skipShaderFullTests())
SECTION("One shader") SECTION("One shader")
{ {
REQUIRE(!shader.loadFromStream(emptyStream, sf::Shader::Type::Vertex)); CHECK(!sf::Shader::loadFromStream(emptyStream, sf::Shader::Type::Vertex));
REQUIRE(shader.loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex) == sf::Shader::isAvailable()); CHECK(sf::Shader::loadFromStream(vertexShaderStream, sf::Shader::Type::Vertex).has_value() ==
REQUIRE(shader.loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment) == sf::Shader::isAvailable()); sf::Shader::isAvailable());
CHECK(sf::Shader::loadFromStream(fragmentShaderStream, sf::Shader::Type::Fragment).has_value() ==
sf::Shader::isAvailable());
} }
SECTION("Two shaders") SECTION("Two shaders")
{ {
REQUIRE(!shader.loadFromStream(emptyStream, fragmentShaderStream)); CHECK(!sf::Shader::loadFromStream(emptyStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream)); CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream));
REQUIRE(shader.loadFromStream(vertexShaderStream, fragmentShaderStream) == sf::Shader::isAvailable()); CHECK(sf::Shader::loadFromStream(vertexShaderStream, fragmentShaderStream).has_value() ==
sf::Shader::isAvailable());
} }
SECTION("Three shaders") SECTION("Three shaders")
{ {
REQUIRE(!shader.loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream)); CHECK(!sf::Shader::loadFromStream(emptyStream, geometryShaderStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream)); CHECK(!sf::Shader::loadFromStream(vertexShaderStream, emptyStream, fragmentShaderStream));
REQUIRE(!shader.loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream)); CHECK(!sf::Shader::loadFromStream(vertexShaderStream, geometryShaderStream, emptyStream));
REQUIRE(shader.loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream) ==
sf::Shader::isGeometryAvailable()); const auto shader = sf::Shader::loadFromStream(vertexShaderStream, geometryShaderStream, fragmentShaderStream);
CHECK(static_cast<bool>(shader.getNativeHandle()) == sf::Shader::isGeometryAvailable()); CHECK(shader.has_value() == sf::Shader::isGeometryAvailable());
if (shader)
CHECK(static_cast<bool>(shader->getNativeHandle()) == sf::Shader::isGeometryAvailable());
} }
} }
} }