Merge branch 'master' of https://github.com/SFML/SFML into comparisonbenchmark

This commit is contained in:
vittorioromeo 2024-09-23 17:50:40 +02:00
commit bb0f88594f
20 changed files with 325 additions and 317 deletions

View File

@ -32,6 +32,7 @@ jobs:
- { name: Windows VS2019 x64, os: windows-2019, flags: -DSFML_USE_MESA3D=ON -GNinja }
- { name: Windows VS2022 x86, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -GNinja }
- { name: Windows VS2022 x64, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -GNinja }
- { name: Windows VS2022 arm64, os: windows-2022, flags: -DSFML_USE_MESA3D=OFF -GNinja -DSFML_BUILD_TEST_SUITE=OFF }
- { name: Windows VS2022 ClangCL MSBuild, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -T ClangCL } # ninja doesn't support specifying the toolset, so use the ClangCL toolset to test building with MSBuild as well
- { name: Windows VS2022 OpenGL ES, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -DSFML_OPENGL_ES=ON -GNinja }
- { name: Windows VS2022 Unity, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -DCMAKE_UNITY_BUILD=ON -GNinja }
@ -117,7 +118,7 @@ jobs:
if: contains(matrix.platform.name, 'Windows VS') && !contains(matrix.platform.name, 'MSBuild')
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ contains(matrix.platform.name, 'x86') && 'x86' || 'x64' }}
arch: ${{ contains(matrix.platform.name, 'arm64') && 'amd64_arm64' || contains(matrix.platform.name, 'x86') && 'x86' || 'x64' }}
# Although the CMake configuration will run with 3.24 on Windows and 3.22
# elsewhere, we install 3.25 on Windows in order to support specifying
@ -237,7 +238,7 @@ jobs:
find build/bin -name test-sfml-window -or -name test-sfml-window.exe -exec sh -c "{} *sf::Context* --section=\"Version String\" --success | grep OpenGL" \;
- name: Test (Windows)
if: runner.os == 'Windows' && !contains(matrix.platform.name, 'MinGW')
if: runner.os == 'Windows' && !contains(matrix.platform.name, 'MinGW') && !contains(matrix.platform.name, 'arm64')
run: cmake --build build --target runtests --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }}
- name: Test (Linux/macOS/MinGW)
@ -291,7 +292,7 @@ jobs:
ctest --test-dir build --output-on-failure -C ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} --repeat until-pass:3
- name: Upload Coverage Report to Coveralls
if: matrix.type.name == 'Debug' && github.repository == 'SFML/SFML' && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') # Disable upload in forks
if: matrix.type.name == 'Debug' && github.repository == 'SFML/SFML' && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') && !contains(matrix.platform.name, 'arm64') # Disable upload in forks
uses: coverallsapp/github-action@v2
with:
file: ./build/coverage.out

View File

@ -6,9 +6,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set(OPENGL_ES 0)
# detect the architecture
if(${CMAKE_GENERATOR_PLATFORM} MATCHES "ARM64")
set(ARCH_ARM64 1)
elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "" AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM64")
if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "ARM64" OR "${MSVC_CXX_ARCHITECTURE_ID}" MATCHES "ARM64" OR "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "ARM64")
set(ARCH_ARM64 1)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(ARCH_X86 1)

View File

@ -4,11 +4,11 @@ set(MESA3D_SHA256 "FEF8A643689414A70347AE8027D24674DEFD85E8D6428C8A9D4145BB3F44A
get_filename_component(MESA3D_ARCHIVE "${MESA3D_URL}" NAME)
get_filename_component(MESA3D_ARCHIVE_DIRECTORY "${MESA3D_URL}" NAME_WLE)
if(${ARCH_X64})
if(ARCH_X64)
set(MESA3D_ARCH "x64")
elseif(${ARCH_X86})
elseif(ARCH_X86)
set(MESA3D_ARCH "x86")
else()
elseif(SFML_USE_MESA3D)
message(FATAL_ERROR "Mesa 3D does currently not support the target architecture.")
endif()
@ -61,7 +61,7 @@ if(SFML_OS_WINDOWS AND SFML_USE_MESA3D)
add_custom_target(install-mesa3d DEPENDS ${MESA3D_INSTALLED_FILES})
set_target_properties(install-mesa3d PROPERTIES EXCLUDE_FROM_ALL ON)
elseif(SFML_OS_WINDOWS AND EXISTS "${MESA3D_ARCH_PATH}")
elseif(SFML_OS_WINDOWS AND MESA3D_ARCH AND EXISTS "${MESA3D_ARCH_PATH}")
# we are removing the files
# compile a list of file names that we have to remove

View File

@ -24,12 +24,10 @@
namespace
{
// Width and height of the application window
const unsigned int windowWidth = 800;
const unsigned int windowHeight = 600;
const sf::Vector2u windowSize(800, 600);
// Resolution of the generated terrain
const unsigned int resolutionX = 800;
const unsigned int resolutionY = 600;
const sf::Vector2u resolution(800, 600);
// Thread pool parameters
const unsigned int threadCount = 4;
@ -68,7 +66,7 @@ float edgeDropoffExponent = 1.5f;
float snowcapHeight = 0.6f;
// Terrain lighting parameters
float heightFactor = windowHeight / 2.0f;
float heightFactor = static_cast<float>(windowSize.y) / 2.0f;
float heightFlatten = 3.0f;
float lightFactor = 0.7f;
} // namespace
@ -88,7 +86,7 @@ void generateTerrain(sf::Vertex* vertexBuffer);
int main()
{
// Create the window of the application
sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
sf::RenderWindow window(sf::VideoMode(windowSize), "SFML Island", sf::Style::Titlebar | sf::Style::Close);
window.setVerticalSyncEnabled(true);
const sf::Font font("resources/tuffy.ttf");
@ -133,14 +131,14 @@ int main()
}
// Create our VertexBuffer with enough space to hold all the terrain geometry
if (!terrain.create(resolutionX * resolutionY * 6))
if (!terrain.create(resolution.x * resolution.y * 6))
{
std::cerr << "Failed to create vertex buffer" << std::endl;
return EXIT_FAILURE;
}
// Resize the staging buffer to be able to hold all the terrain geometry
terrainStagingBuffer.resize(resolutionX * resolutionY * 6);
terrainStagingBuffer.resize(resolution.x * resolution.y * 6);
// Generate the initial terrain
generateTerrain(terrainStagingBuffer.data());
@ -152,7 +150,7 @@ int main()
}
// Center the status text
statusText.setPosition((sf::Vector2f(windowWidth, windowHeight) - statusText.getLocalBounds().size) / 2.f);
statusText.setPosition((sf::Vector2f(windowSize) - statusText.getLocalBounds().size) / 2.f);
// Set up an array of pointers to our settings for arrow navigation
constexpr std::array<Setting, 9> settings = {
@ -279,58 +277,45 @@ int main()
/// Get the terrain elevation at the given coordinates.
///
////////////////////////////////////////////////////////////
float getElevation(float x, float y)
float getElevation(sf::Vector2u position)
{
x = x / resolutionX - 0.5f;
y = y / resolutionY - 0.5f;
const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) -
sf::Vector2f(0.5f, 0.5f);
float elevation = 0.0f;
for (int i = 0; i < perlinOctaves; ++i)
{
elevation += stb_perlin_noise3(x * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i)),
y * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i)),
0,
0,
0,
0) *
const sf::Vector2f scaled = normalized * perlinFrequency * static_cast<float>(std::pow(perlinFrequencyBase, i));
elevation += stb_perlin_noise3(scaled.x, scaled.y, 0, 0, 0, 0) *
static_cast<float>(std::pow(perlinFrequencyBase, -i));
}
elevation = (elevation + 1.f) / 2.f;
const float distance = 2.0f * std::sqrt(x * x + y * y);
const float distance = 2.0f * normalized.length();
elevation = (elevation + heightBase) * (1.0f - edgeFactor * std::pow(distance, edgeDropoffExponent));
elevation = std::clamp(elevation, 0.0f, 1.0f);
return elevation;
}
float getElevation(unsigned int x, unsigned int y)
{
return getElevation(static_cast<float>(x), static_cast<float>(y));
}
////////////////////////////////////////////////////////////
/// Get the terrain moisture at the given coordinates.
///
////////////////////////////////////////////////////////////
float getMoisture(float x, float y)
float getMoisture(sf::Vector2u position)
{
x = x / resolutionX - 0.5f;
y = y / resolutionY - 0.5f;
const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) -
sf::Vector2f(0.5f, 0.5f);
const sf::Vector2f transformed = normalized * 4.f + sf::Vector2f(0.5f, 0.5f);
const float moisture = stb_perlin_noise3(x * 4.f + 0.5f, y * 4.f + 0.5f, 0, 0, 0, 0);
const float moisture = stb_perlin_noise3(transformed.x, transformed.y, 0, 0, 0, 0);
return (moisture + 1.f) / 2.f;
}
float getMoisture(unsigned int x, unsigned int y)
{
return getMoisture(static_cast<float>(x), static_cast<float>(y));
}
////////////////////////////////////////////////////////////
/// Get the lowlands terrain color for the given moisture.
@ -372,18 +357,16 @@ sf::Color getHighlandsTerrainColor(float elevation, float moisture)
{
const sf::Color lowlandsColor = getLowlandsTerrainColor(moisture);
sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144)
: colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f),
128 + (56 * (moisture - 0.6f) / 0.4f),
144 - (9 * (moisture - 0.6f) / 0.4f));
const sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144)
: colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f),
128 + (56 * (moisture - 0.6f) / 0.4f),
144 - (9 * (moisture - 0.6f) / 0.4f));
const float factor = std::min((elevation - 0.4f) / 0.1f, 1.f);
color.r = static_cast<std::uint8_t>(lowlandsColor.r * (1.f - factor) + color.r * factor);
color.g = static_cast<std::uint8_t>(lowlandsColor.g * (1.f - factor) + color.g * factor);
color.b = static_cast<std::uint8_t>(lowlandsColor.b * (1.f - factor) + color.b * factor);
return color;
return colorFromFloats(lowlandsColor.r * (1.f - factor) + color.r * factor,
lowlandsColor.g * (1.f - factor) + color.g * factor,
lowlandsColor.b * (1.f - factor) + color.b * factor);
}
@ -396,15 +379,13 @@ sf::Color getSnowcapTerrainColor(float elevation, float moisture)
{
const sf::Color highlandsColor = getHighlandsTerrainColor(elevation, moisture);
sf::Color color = sf::Color::White;
const sf::Color color = sf::Color::White;
const float factor = std::min((elevation - snowcapHeight) / 0.05f, 1.f);
color.r = static_cast<std::uint8_t>(highlandsColor.r * (1.f - factor) + color.r * factor);
color.g = static_cast<std::uint8_t>(highlandsColor.g * (1.f - factor) + color.g * factor);
color.b = static_cast<std::uint8_t>(highlandsColor.b * (1.f - factor) + color.b * factor);
return color;
return colorFromFloats(highlandsColor.r * (1.f - factor) + color.r * factor,
highlandsColor.g * (1.f - factor) + color.g * factor,
highlandsColor.b * (1.f - factor) + color.b * factor);
}
@ -446,9 +427,7 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
const sf::Vector3f deltaX(1, 0, (std::pow(right, heightFlatten) - std::pow(left, heightFlatten)) * heightFactor);
const sf::Vector3f deltaY(0, 1, (std::pow(top, heightFlatten) - std::pow(bottom, heightFlatten)) * heightFactor);
sf::Vector3f crossProduct(deltaX.y * deltaY.z - deltaX.z * deltaY.y,
deltaX.z * deltaY.x - deltaX.x * deltaY.z,
deltaX.x * deltaY.y - deltaX.y * deltaY.x);
sf::Vector3f crossProduct = deltaX.cross(deltaY);
// Scale cross product to make z component 1.0f so we can drop it
crossProduct /= crossProduct.z;
@ -458,6 +437,26 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
}
////////////////////////////////////////////////////////////
/// Compute the vertex representing the terrain at the given
/// coordinates.
///
////////////////////////////////////////////////////////////
const auto scalingFactors = sf::Vector2f(windowSize).componentWiseDiv(sf::Vector2f(resolution));
sf::Vertex computeVertex(sf::Vector2u position)
{
sf::Vertex vertex;
vertex.position = sf::Vector2f(position).componentWiseMul(scalingFactors);
vertex.color = getTerrainColor(getElevation(position), getMoisture(position));
vertex.texCoords = computeNormal(getElevation(position - sf::Vector2u(1, 0)),
getElevation(position + sf::Vector2u(1, 0)),
getElevation(position + sf::Vector2u(0, 1)),
getElevation(position - sf::Vector2u(0, 1)));
return vertex;
};
////////////////////////////////////////////////////////////
/// Process a terrain generation work item. Use the vector
/// of vertices as scratch memory and upload the data to
@ -466,23 +465,20 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top)
////////////////////////////////////////////////////////////
void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem)
{
const unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
const unsigned int rowBlockSize = (resolution.y / blockCount) + 1;
const unsigned int rowStart = rowBlockSize * workItem.index;
if (rowStart >= resolutionY)
if (rowStart >= resolution.y)
return;
const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolutionY);
const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolution.y);
const unsigned int rowCount = rowEnd - rowStart;
const float scalingFactorX = float{windowWidth} / float{resolutionX};
const float scalingFactorY = float{windowHeight} / float{resolutionY};
for (unsigned int y = rowStart; y < rowEnd; ++y)
{
for (unsigned int x = 0; x < resolutionX; ++x)
for (unsigned int x = 0; x < resolution.x; ++x)
{
const unsigned int arrayIndexBase = ((y - rowStart) * resolutionX + x) * 6;
const unsigned int arrayIndexBase = ((y - rowStart) * resolution.x + x) * 6;
// Top left corner (first triangle)
if (x > 0)
@ -491,17 +487,11 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
}
else if (y > rowStart)
{
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolutionX * 6 + 1];
vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolution.x * 6 + 1];
}
else
{
vertices[arrayIndexBase + 0].position = sf::Vector2f(static_cast<float>(x) * scalingFactorX,
static_cast<float>(y) * scalingFactorY);
vertices[arrayIndexBase + 0].color = getTerrainColor(getElevation(x, y), getMoisture(x, y));
vertices[arrayIndexBase + 0].texCoords = computeNormal(getElevation(x - 1, y),
getElevation(x + 1, y),
getElevation(x, y + 1),
getElevation(x, y - 1));
vertices[arrayIndexBase + 0] = computeVertex({x, y});
}
// Bottom left corner (first triangle)
@ -511,23 +501,11 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
}
else
{
vertices[arrayIndexBase + 1].position = sf::Vector2f(static_cast<float>(x) * scalingFactorX,
static_cast<float>(y + 1) * scalingFactorY);
vertices[arrayIndexBase + 1].color = getTerrainColor(getElevation(x, y + 1), getMoisture(x, y + 1));
vertices[arrayIndexBase + 1].texCoords = computeNormal(getElevation(x - 1, y + 1),
getElevation(x + 1, y + 1),
getElevation(x, y + 2),
getElevation(x, y));
vertices[arrayIndexBase + 1] = computeVertex({x, y + 1});
}
// Bottom right corner (first triangle)
vertices[arrayIndexBase + 2].position = sf::Vector2f(static_cast<float>(x + 1) * scalingFactorX,
static_cast<float>(y + 1) * scalingFactorY);
vertices[arrayIndexBase + 2].color = getTerrainColor(getElevation(x + 1, y + 1), getMoisture(x + 1, y + 1));
vertices[arrayIndexBase + 2].texCoords = computeNormal(getElevation(x, y + 1),
getElevation(x + 2, y + 1),
getElevation(x + 1, y + 2),
getElevation(x + 1, y));
vertices[arrayIndexBase + 2] = computeVertex({x + 1, y + 1});
// Top left corner (second triangle)
vertices[arrayIndexBase + 3] = vertices[arrayIndexBase + 0];
@ -538,25 +516,19 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
// Top right corner (second triangle)
if (y > rowStart)
{
vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolutionX * 6 + 2];
vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolution.x * 6 + 2];
}
else
{
vertices[arrayIndexBase + 5].position = sf::Vector2f(static_cast<float>(x + 1) * scalingFactorX,
static_cast<float>(y) * scalingFactorY);
vertices[arrayIndexBase + 5].color = getTerrainColor(getElevation(x + 1, y), getMoisture(x + 1, y));
vertices[arrayIndexBase + 5].texCoords = computeNormal(getElevation(x, y),
getElevation(x + 2, y),
getElevation(x + 1, y + 1),
getElevation(x + 1, y - 1));
vertices[arrayIndexBase + 5] = computeVertex({x + 1, y});
}
}
}
// Copy the resulting geometry from our thread-local buffer into the target buffer
std::memcpy(workItem.targetBuffer + (resolutionX * rowStart * 6),
std::memcpy(workItem.targetBuffer + (resolution.x * rowStart * 6),
vertices.data(),
sizeof(sf::Vertex) * resolutionX * rowCount * 6);
sizeof(sf::Vertex) * resolution.x * rowCount * 6);
}
@ -568,9 +540,9 @@ void processWorkItem(std::vector<sf::Vertex>& vertices, const WorkItem& workItem
////////////////////////////////////////////////////////////
void threadFunction()
{
const unsigned int rowBlockSize = (resolutionY / blockCount) + 1;
const unsigned int rowBlockSize = (resolution.y / blockCount) + 1;
std::vector<sf::Vertex> vertices(resolutionX * rowBlockSize * 6);
std::vector<sf::Vertex> vertices(resolution.x * rowBlockSize * 6);
WorkItem workItem = {nullptr, 0};

View File

@ -346,11 +346,11 @@ private:
/// {
/// // Open the source and get audio settings
/// ...
/// unsigned int channelCount = ...;
/// unsigned int sampleRate = ...;
/// unsigned int channelCount = 2; // Stereo
/// unsigned int sampleRate = 44100; // 44100 Hz
///
/// // Initialize the stream -- important!
/// initialize(channelCount, sampleRate);
/// initialize(channelCount, sampleRate, {sf::SoundChannel::FrontLeft, sf::SoundChannel::FrontRight});
/// return true;
/// }
///

View File

@ -180,9 +180,8 @@ public:
/// If \a `state` is `State::Fullscreen`, then \a `mode` must be
/// a valid video mode.
///
/// The last parameter is an optional structure specifying
/// advanced OpenGL context settings such as anti-aliasing,
/// depth-buffer bits, etc.
/// The last parameter is a structure specifying advanced OpenGL
/// context settings such as anti-aliasing, depth-buffer bits, etc.
///
/// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window)
/// \param title Title of the window
@ -193,6 +192,38 @@ public:
////////////////////////////////////////////////////////////
virtual void create(VideoMode mode, const String& title, std::uint32_t style, State state, const ContextSettings& settings);
////////////////////////////////////////////////////////////
/// \brief Create (or recreate) the window
///
/// If the window was already created, it closes it first.
/// If \a `state` is `State::Fullscreen`, then \a `mode` must be
/// a valid video mode.
///
/// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window)
/// \param title Title of the window
/// \param state %Window state
///
////////////////////////////////////////////////////////////
void create(VideoMode mode, const String& title, State state) override;
////////////////////////////////////////////////////////////
/// \brief Create (or recreate) the window
///
/// If the window was already created, it closes it first.
/// If \a `state` is `State::Fullscreen`, then \a `mode` must be
/// a valid video mode.
///
/// The last parameter is a structure specifying advanced OpenGL
/// context settings such as anti-aliasing, depth-buffer bits, etc.
///
/// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window)
/// \param title Title of the window
/// \param state %Window state
/// \param settings Additional settings for the underlying OpenGL context
///
////////////////////////////////////////////////////////////
virtual void create(VideoMode mode, const String& title, State state, const ContextSettings& settings);
////////////////////////////////////////////////////////////
/// \brief Create (or recreate) the window from an existing control
///

View File

@ -157,6 +157,20 @@ public:
////////////////////////////////////////////////////////////
virtual void create(VideoMode mode, const String& title, std::uint32_t style = Style::Default, State state = State::Windowed);
////////////////////////////////////////////////////////////
/// \brief Create (or recreate) the window
///
/// If the window was already created, it closes it first.
/// If \a `state` is `State::Fullscreen`, then \a `mode` must be
/// a valid video mode.
///
/// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window)
/// \param title Title of the window
/// \param state %Window state
///
////////////////////////////////////////////////////////////
virtual void create(VideoMode mode, const String& title, State state);
////////////////////////////////////////////////////////////
/// \brief Create (or recreate) the window from an existing control
///

View File

@ -362,13 +362,10 @@ std::uint64_t SoundFileReaderFlac::read(std::int16_t* samples, std::uint64_t max
if (left > maxCount)
{
// There are more leftovers than needed
std::copy(m_clientData.leftovers.begin(),
m_clientData.leftovers.begin() + static_cast<std::vector<std::int16_t>::difference_type>(maxCount),
samples);
std::vector<std::int16_t> leftovers(m_clientData.leftovers.begin() +
static_cast<std::vector<std::int16_t>::difference_type>(maxCount),
m_clientData.leftovers.end());
m_clientData.leftovers.swap(leftovers);
const auto signedMaxCount = static_cast<std::vector<std::int16_t>::difference_type>(maxCount);
std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.begin() + signedMaxCount, samples);
m_clientData.leftovers = std::vector<std::int16_t>(m_clientData.leftovers.begin() + signedMaxCount,
m_clientData.leftovers.end());
return maxCount;
}

View File

@ -31,81 +31,52 @@
#include <SFML/System/Err.hpp>
#include <ostream>
#include <string>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression)
bool glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression)
{
// Get the last error
const GLenum errorCode = glGetError();
if (errorCode != GL_NO_ERROR)
const auto logError = [&](const char* error, const char* description)
{
std::string error = "Unknown error";
std::string description = "No description";
// Decode the error code
switch (errorCode)
{
case GL_INVALID_ENUM:
{
error = "GL_INVALID_ENUM";
description = "An unacceptable value has been specified for an enumerated argument.";
break;
}
case GL_INVALID_VALUE:
{
error = "GL_INVALID_VALUE";
description = "A numeric argument is out of range.";
break;
}
case GL_INVALID_OPERATION:
{
error = "GL_INVALID_OPERATION";
description = "The specified operation is not allowed in the current state.";
break;
}
case GL_STACK_OVERFLOW:
{
error = "GL_STACK_OVERFLOW";
description = "This command would cause a stack overflow.";
break;
}
case GL_STACK_UNDERFLOW:
{
error = "GL_STACK_UNDERFLOW";
description = "This command would cause a stack underflow.";
break;
}
case GL_OUT_OF_MEMORY:
{
error = "GL_OUT_OF_MEMORY";
description = "There is not enough memory left to execute the command.";
break;
}
case GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION:
{
error = "GL_INVALID_FRAMEBUFFER_OPERATION";
description = "The object bound to FRAMEBUFFER_BINDING is not \"framebuffer complete\".";
break;
}
}
// Log the error
err() << "An internal OpenGL call failed in " << file.filename() << "(" << line << ")."
<< "\nExpression:\n " << expression << "\nError description:\n " << error << "\n " << description << '\n'
<< std::endl;
return false;
};
switch (glGetError())
{
case GL_NO_ERROR:
return true;
case GL_INVALID_ENUM:
return logError("GL_INVALID_ENUM", "An unacceptable value has been specified for an enumerated argument.");
case GL_INVALID_VALUE:
return logError("GL_INVALID_VALUE", "A numeric argument is out of range.");
case GL_INVALID_OPERATION:
return logError("GL_INVALID_OPERATION", "The specified operation is not allowed in the current state.");
case GL_STACK_OVERFLOW:
return logError("GL_STACK_OVERFLOW", "This command would cause a stack overflow.");
case GL_STACK_UNDERFLOW:
return logError("GL_STACK_UNDERFLOW", "This command would cause a stack underflow.");
case GL_OUT_OF_MEMORY:
return logError("GL_OUT_OF_MEMORY", "There is not enough memory left to execute the command.");
case GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION:
return logError("GL_INVALID_FRAMEBUFFER_OPERATION",
"The object bound to FRAMEBUFFER_BINDING is not \"framebuffer complete\".");
default:
return logError("Unknown error", "Unknown description");
}
}
} // namespace sf::priv

View File

@ -63,7 +63,9 @@ namespace sf::priv
/// \param line Line number of the source file where the call is located
/// \param expression The evaluated expression as a string
///
/// \return `false` if an error occurred, `true` otherwise
///
////////////////////////////////////////////////////////////
void glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression);
bool glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression);
} // namespace sf::priv

View File

@ -46,6 +46,7 @@
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include <cassert>
#include <cstring>
@ -154,7 +155,7 @@ void Image::resize(Vector2u size, Color color)
}
// Commit the new pixel buffer
m_pixels.swap(newPixels);
m_pixels = std::move(newPixels);
// Assign the new size
m_size = size;
@ -179,7 +180,7 @@ void Image::resize(Vector2u size, const std::uint8_t* pixels)
std::vector<std::uint8_t> newPixels(pixels, pixels + size.x * size.y * 4);
// Commit the new pixel buffer
m_pixels.swap(newPixels);
m_pixels = std::move(newPixels);
// Assign the new size
m_size = size;

View File

@ -312,9 +312,7 @@ Vector2f Text::findCharacterPos(std::size_t index) const
}
// Transform the position to global coordinates
position = getTransform().transformPoint(position);
return position;
return getTransform().transformPoint(position);
}

View File

@ -33,139 +33,88 @@
#include <glad/egl.h>
#include <ostream>
#include <string>
namespace sf::priv
{
////////////////////////////////////////////////////////////
void eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression)
bool eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression)
{
// Obtain information about the success or failure of the most recent EGL
// function called in the current thread
const EGLint errorCode = eglGetError();
if (errorCode != EGL_SUCCESS)
const auto logError = [&](const char* error, const char* description)
{
std::string error = "unknown error";
std::string description = "no description";
// Decode the error code returned
switch (errorCode)
{
case EGL_NOT_INITIALIZED:
{
error = "EGL_NOT_INITIALIZED";
description = "EGL is not initialized, or could not be initialized, for the specified display";
break;
}
case EGL_BAD_ACCESS:
{
error = "EGL_BAD_ACCESS";
description =
"EGL cannot access a requested resource (for example, a context is bound in another thread)";
break;
}
case EGL_BAD_ALLOC:
{
error = "EGL_BAD_ALLOC";
description = "EGL failed to allocate resources for the requested operation";
break;
}
case EGL_BAD_ATTRIBUTE:
{
error = "EGL_BAD_ATTRIBUTE";
description = "an unrecognized attribute or attribute value was passed in an attribute list";
break;
}
case EGL_BAD_CONTEXT:
{
error = "EGL_BAD_CONTEXT";
description = "an EGLContext argument does not name a valid EGLContext";
break;
}
case EGL_BAD_CONFIG:
{
error = "EGL_BAD_CONFIG";
description = "an EGLConfig argument does not name a valid EGLConfig";
break;
}
case EGL_BAD_CURRENT_SURFACE:
{
error = "EGL_BAD_CURRENT_SURFACE";
description =
"the current surface of the calling thread is a window, pbuffer, or pixmap that is no longer valid";
break;
}
case EGL_BAD_DISPLAY:
{
error = "EGL_BAD_DISPLAY";
description =
"an EGLDisplay argument does not name a valid EGLDisplay; or, EGL is not initialized on the "
"specified EGLDisplay";
break;
}
case EGL_BAD_SURFACE:
{
error = "EGL_BAD_SURFACE";
description =
"an EGLSurface argument does not name a valid surface (window, pbuffer, or pixmap) configured for "
"rendering";
break;
}
case EGL_BAD_MATCH:
{
error = "EGL_BAD_MATCH";
description =
"arguments are inconsistent; for example, an otherwise valid context requires buffers (e.g. depth "
"or stencil) not allocated by an otherwise valid surface";
break;
}
case EGL_BAD_PARAMETER:
{
error = "EGL_BAD_PARAMETER";
description = "one or more argument values are invalid";
break;
}
case EGL_BAD_NATIVE_PIXMAP:
{
error = "EGL_BAD_NATIVE_PIXMAP";
description = "an EGLNativePixmapType argument does not refer to a valid native pixmap";
break;
}
case EGL_BAD_NATIVE_WINDOW:
{
error = "EGL_BAD_NATIVE_WINDOW";
description = "an EGLNativeWindowType argument does not refer to a valid native window";
break;
}
case EGL_CONTEXT_LOST:
{
error = "EGL_CONTEXT_LOST";
description =
"a power management event has occurred. The application must destroy all contexts and reinitialize "
"client API state and objects to continue rendering";
break;
}
}
// Log the error
err() << "An internal EGL call failed in " << file.filename() << " (" << line << ") : "
<< "\nExpression:\n " << expression << "\nError description:\n " << error << "\n " << description << '\n'
<< std::endl;
return false;
};
switch (eglGetError())
{
case EGL_SUCCESS:
return true;
case EGL_NOT_INITIALIZED:
return logError("EGL_NOT_INITIALIZED",
"EGL is not initialized, or could not be initialized, for the specified display");
case EGL_BAD_ACCESS:
return logError("EGL_BAD_ACCESS",
"EGL cannot access a requested resource (for example, a context is bound in another "
"thread)");
case EGL_BAD_ALLOC:
return logError("EGL_BAD_ALLOC", "EGL failed to allocate resources for the requested operation");
case EGL_BAD_ATTRIBUTE:
return logError("EGL_BAD_ATTRIBUTE",
"an unrecognized attribute or attribute value was passed in an attribute list");
case EGL_BAD_CONTEXT:
return logError("EGL_BAD_CONTEXT", "an EGLContext argument does not name a valid EGLContext");
case EGL_BAD_CONFIG:
return logError("EGL_BAD_CONFIG", "an EGLConfig argument does not name a valid EGLConfig");
case EGL_BAD_CURRENT_SURFACE:
return logError("EGL_BAD_CURRENT_SURFACE",
"the current surface of the calling thread is a window, pbuffer, or pixmap that is no "
"longer valid");
case EGL_BAD_DISPLAY:
return logError("EGL_BAD_DISPLAY",
"an EGLDisplay argument does not name a valid EGLDisplay; or, EGL is not initialized on "
"the specified EGLDisplay");
case EGL_BAD_SURFACE:
return logError("EGL_BAD_SURFACE",
"an EGLSurface argument does not name a valid surface (window, pbuffer, or pixmap) "
"configured for rendering");
case EGL_BAD_MATCH:
return logError("EGL_BAD_MATCH",
"arguments are inconsistent; for example, an otherwise valid context requires buffers "
"(e.g. depth or stencil) not allocated by an otherwise valid surface");
case EGL_BAD_PARAMETER:
return logError("EGL_BAD_PARAMETER", "one or more argument values are invalid");
case EGL_BAD_NATIVE_PIXMAP:
return logError("EGL_BAD_NATIVE_PIXMAP",
"an EGLNativePixmapType argument does not refer to a valid native pixmap");
case EGL_BAD_NATIVE_WINDOW:
return logError("EGL_BAD_NATIVE_WINDOW",
"an EGLNativeWindowType argument does not refer to a valid native window");
case EGL_CONTEXT_LOST:
return logError("EGL_CONTEXT_LOST",
"a power management event has occurred. The application must destroy all contexts and "
"reinitialize client API state and objects to continue rendering");
default:
return logError("Unknown error", "Unknown description");
}
}

View File

@ -63,7 +63,9 @@ namespace sf::priv
/// \param line Line number of the source file where the call is located
/// \param expression The evaluated expression as a string
///
/// \return `false` if an error occurred, `true` otherwise
///
////////////////////////////////////////////////////////////
void eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression);
bool eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression);
} // namespace sf::priv

View File

@ -95,6 +95,20 @@ void Window::create(VideoMode mode, const String& title, std::uint32_t style, St
}
////////////////////////////////////////////////////////////
void Window::create(VideoMode mode, const String& title, State state)
{
Window::create(mode, title, sf::Style::Default, state, ContextSettings{});
}
////////////////////////////////////////////////////////////
void Window::create(VideoMode mode, const String& title, State state, const ContextSettings& settings)
{
Window::create(mode, title, sf::Style::Default, state, settings);
}
////////////////////////////////////////////////////////////
void Window::create(WindowHandle handle)
{

View File

@ -102,6 +102,13 @@ void WindowBase::create(VideoMode mode, const String& title, std::uint32_t style
}
////////////////////////////////////////////////////////////
void WindowBase::create(VideoMode mode, const String& title, State state)
{
create(mode, title, Style::Default, state);
}
////////////////////////////////////////////////////////////
void WindowBase::create(WindowHandle handle)
{

View File

@ -286,7 +286,8 @@ private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
IOHIDManagerRef m_manager{}; ///< Underlying HID Manager
IOHIDManagerRef m_manager{}; ///< Underlying HID Manager
bool m_keysInitialized{}; ///< Has initializeKeyboard been called at least once?
EnumArray<Keyboard::Scancode, IOHIDElements, Keyboard::ScancodeCount> m_keys; ///< All the keys on any connected keyboard
EnumArray<Keyboard::Key, Keyboard::Scancode, Keyboard::KeyCount> m_keyToScancodeMapping{}; ///< Mapping from Key to Scancode
EnumArray<Keyboard::Scancode, Keyboard::Key, Keyboard::ScancodeCount> m_scancodeToKeyMapping{}; ///< Mapping from Scancode to Key

View File

@ -562,6 +562,13 @@ bool HIDInputManager::isKeyPressed(Keyboard::Key key)
////////////////////////////////////////////////////////////
bool HIDInputManager::isKeyPressed(Keyboard::Scancode code)
{
// Lazy load m_keys to prevent unnecessary macOS input monitoring permission requests
if (!m_keysInitialized)
{
initializeKeyboard();
m_keysInitialized = true;
}
return (code != Keyboard::Scan::Unknown) && isPressed(m_keys[code]);
}
@ -716,7 +723,6 @@ HIDInputManager::HIDInputManager()
}
// Build up our knowledge of the hardware
initializeKeyboard();
buildMappings();
// Register for notification on keyboard layout changes
@ -924,13 +930,17 @@ void HIDInputManager::freeUp()
m_manager = nil;
for (auto& key : m_keys)
if (m_keysInitialized)
{
for (IOHIDElementRef iohidElementRef : key)
CFRelease(iohidElementRef);
for (auto& key : m_keys)
{
for (IOHIDElementRef iohidElementRef : key)
CFRelease(iohidElementRef);
key.clear();
key.clear();
}
}
m_keysInitialized = false;
}

View File

@ -128,7 +128,16 @@ TEST_CASE("[Window] sf::Window", runDisplayTests())
CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default);
}
SECTION("Mode, title, style, and context settings")
SECTION("Mode, title, style, and state")
{
window.create(sf::VideoMode({240, 360}), "Window Tests", sf::Style::Resize, sf::State::Windowed);
CHECK(window.isOpen());
CHECK(window.getSize() == sf::Vector2u(240, 360));
CHECK(window.getNativeHandle() != sf::WindowHandle());
CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default);
}
SECTION("Mode, title, style, state, and context settings")
{
window.create(sf::VideoMode({240, 360}),
"Window Tests",
@ -142,5 +151,28 @@ TEST_CASE("[Window] sf::Window", runDisplayTests())
CHECK(window.getSettings().stencilBits >= 1);
CHECK(window.getSettings().antiAliasingLevel >= 1);
}
SECTION("Mode, title, and state")
{
window.create(sf::VideoMode({240, 360}), "Window Tests", sf::State::Windowed);
CHECK(window.isOpen());
CHECK(window.getSize() == sf::Vector2u(240, 360));
CHECK(window.getNativeHandle() != sf::WindowHandle());
CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default);
}
SECTION("Mode, title, state, and context settings")
{
window.create(sf::VideoMode({240, 360}),
"Window Tests",
sf::State::Windowed,
sf::ContextSettings{/* depthBits*/ 1, /* stencilBits */ 1, /* antiAliasingLevel */ 1});
CHECK(window.isOpen());
CHECK(window.getSize() == sf::Vector2u(240, 360));
CHECK(window.getNativeHandle() != sf::WindowHandle());
CHECK(window.getSettings().depthBits >= 1);
CHECK(window.getSettings().stencilBits >= 1);
CHECK(window.getSettings().antiAliasingLevel >= 1);
}
}
}

View File

@ -88,6 +88,14 @@ TEST_CASE("[Window] sf::WindowBase", runDisplayTests())
CHECK(windowBase.getNativeHandle() != sf::WindowHandle());
}
SECTION("Mode, title, and state")
{
windowBase.create(sf::VideoMode({240, 360}), "WindowBase Tests", sf::State::Windowed);
CHECK(windowBase.isOpen());
CHECK(windowBase.getSize() == sf::Vector2u(240, 360));
CHECK(windowBase.getNativeHandle() != sf::WindowHandle());
}
SECTION("Mode, title, style, and state")
{
windowBase.create(sf::VideoMode({240, 360}), "WindowBase Tests", sf::Style::Resize, sf::State::Windowed);