From ea4c448a85fc1a93c71431faf4b4f12efcfe0976 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Sun, 26 Mar 2023 14:21:21 +0200 Subject: [PATCH] Add support for installing and using the Mesa 3D library for OpenGL rendering. --- .github/workflows/ci.yml | 23 ++++++---- CMakeLists.txt | 4 ++ cmake/Macros.cmake | 7 +++ cmake/Mesa3D.cmake | 86 ++++++++++++++++++++++++++++++++++++ test/Window/Context.test.cpp | 55 +++++++++++++++++++++++ 5 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 cmake/Mesa3D.cmake diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a917ec1..5d220509 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ name: CI on: [push, pull_request] +env: + GALLIUM_DRIVER: llvmpipe # Use Mesa 3D software OpenGL renderer + jobs: build: name: ${{ matrix.platform.name }} ${{ matrix.config.name }} ${{ matrix.type.name }} @@ -11,12 +14,12 @@ jobs: fail-fast: false matrix: platform: - - { name: Windows VS2019 x86, os: windows-2019, flags: -A Win32 } - - { name: Windows VS2019 x64, os: windows-2019, flags: -A x64 } - - { name: Windows VS2022 x86, os: windows-2022, flags: -A Win32 } - - { name: Windows VS2022 x64, os: windows-2022, flags: -A x64 } - - { name: Windows VS2022 ClangCL, os: windows-2022, flags: -T ClangCL } - - { name: Windows VS2022 Clang, os: windows-2022, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -GNinja } + - { name: Windows VS2019 x86, os: windows-2019, flags: -DSFML_USE_MESA3D=TRUE -A Win32 } + - { name: Windows VS2019 x64, os: windows-2019, flags: -DSFML_USE_MESA3D=TRUE -A x64 } + - { name: Windows VS2022 x86, os: windows-2022, flags: -DSFML_USE_MESA3D=TRUE -A Win32 } + - { name: Windows VS2022 x64, os: windows-2022, flags: -DSFML_USE_MESA3D=TRUE -A x64 } + - { name: Windows VS2022 ClangCL, os: windows-2022, flags: -DSFML_USE_MESA3D=TRUE -T ClangCL } + - { name: Windows VS2022 Clang, os: windows-2022, flags: -DSFML_USE_MESA3D=TRUE -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -GNinja } - { name: Linux GCC, os: ubuntu-latest, flags: -DSFML_RUN_DISPLAY_TESTS=ON -GNinja } - { name: Linux Clang, os: ubuntu-latest, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DSFML_RUN_DISPLAY_TESTS=ON -GNinja , gcovr_options: '--gcov-executable="llvm-cov-$CLANG_VERSION gcov"' } - { name: MacOS, os: macos-11, flags: -GNinja } @@ -38,10 +41,10 @@ jobs: include: - platform: { name: Windows VS2022, os: windows-2022 } - config: { name: Unity, flags: -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON } + config: { name: Unity, flags: -DSFML_USE_MESA3D=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON } type: { name: Release } - platform: { name: Windows VS2022, os: windows-2022 } - config: { name: Unity, flags: -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON } + config: { name: Unity, flags: -DSFML_USE_MESA3D=TRUE -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON } type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug -DSFML_ENABLE_COVERAGE=TRUE } - platform: { name: MacOS, os: macos-11 } config: { name: Frameworks, flags: -GNinja -DSFML_BUILD_FRAMEWORKS=TRUE -DBUILD_SHARED_LIBS=TRUE } @@ -112,6 +115,8 @@ jobs: shell: bash command: | if [ "${{ runner.os }}" == "Windows" ]; then + # Make use of a test to print OpenGL vendor/renderer/version info to the console + find $GITHUB_WORKSPACE/build/bin -name test-sfml-window.exe -exec {} --test-case="[Window] sf::Context" --subcase="Version String" \; | grep OpenGL # Run the tests cmake --build $GITHUB_WORKSPACE/build --target runtests --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} # Coverage is already generated on Windows when running tests. @@ -124,6 +129,8 @@ jobs: fluxbox > /dev/null 2>&1 & sleep 5 fi + # Make use of a test to print OpenGL vendor/renderer/version info to the console + find $GITHUB_WORKSPACE/build/bin -name test-sfml-window -exec {} --test-case="[Window] sf::Context" --subcase="Version String" \; | grep OpenGL # Run the tests ctest --test-dir $GITHUB_WORKSPACE/build --output-on-failure --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} # Run gcovr to extract coverage information from the test run diff --git a/CMakeLists.txt b/CMakeLists.txt index cbbfb55a..5fc93ba1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,10 @@ if(SFML_OS_WINDOWS) if(BUILD_SHARED_LIBS AND SFML_USE_STATIC_STD_LIBS) message(FATAL_ERROR "BUILD_SHARED_LIBS and SFML_USE_STATIC_STD_LIBS cannot be used together") endif() + + sfml_set_option(SFML_USE_MESA3D FALSE BOOL "TRUE to use the Mesa 3D graphics library for rendering, FALSE to use the system provided library for rendering") + + include(cmake/Mesa3D.cmake) endif() # setup Mac OS X stuff diff --git a/cmake/Macros.cmake b/cmake/Macros.cmake index e6895a82..03a510fe 100644 --- a/cmake/Macros.cmake +++ b/cmake/Macros.cmake @@ -331,6 +331,9 @@ macro(sfml_add_example target) sfml_set_common_ios_properties(${target}) endif() + if(SFML_OS_WINDOWS AND SFML_USE_MESA3D) + add_dependencies(${target} "install-mesa3d") + endif() endmacro() # add a new target which is a SFML test @@ -368,6 +371,10 @@ function(sfml_add_test target SOURCES DEPENDS) endforeach() endif() + if(SFML_OS_WINDOWS AND SFML_USE_MESA3D) + add_dependencies(${target} "install-mesa3d") + endif() + # Add the test doctest_discover_tests(${target}) endfunction() diff --git a/cmake/Mesa3D.cmake b/cmake/Mesa3D.cmake new file mode 100644 index 00000000..60539ed6 --- /dev/null +++ b/cmake/Mesa3D.cmake @@ -0,0 +1,86 @@ +set(MESA3D_URL "https://github.com/pal1000/mesa-dist-win/releases/download/23.0.0/mesa3d-23.0.0-release-msvc.7z") +set(MESA3D_SHA256 "FEF8A643689414A70347AE8027D24674DEFD85E8D6428C8A9D4145BB3F44A3B0") + +get_filename_component(MESA3D_ARCHIVE "${MESA3D_URL}" NAME) +get_filename_component(MESA3D_ARCHIVE_DIRECTORY "${MESA3D_URL}" NAME_WLE) + +if(${ARCH_64BITS}) + set(MESA3D_ARCH "x64") +else() + set(MESA3D_ARCH "x86") +endif() + +set(MESA3D_ARCHIVE_PATH "${PROJECT_BINARY_DIR}/${MESA3D_ARCHIVE_DIRECTORY}/${MESA3D_ARCHIVE}") +set(MESA3D_ARCH_PATH "${PROJECT_BINARY_DIR}/${MESA3D_ARCHIVE_DIRECTORY}/${MESA3D_ARCH}") + +# we support automatically installing and uninstalling the necessary files + +# if SFML_USE_MESA3D is set and true during configuration, we add custom commands to +# automatically copy over the necessary files whenever an executable/test target is built + +# if SFML_USE_MESA3D is not set or false but the Mesa 3D directory is present, use its file list to +# remove any files that were previously copied into ${PROJECT_BINARY_DIR}/bin and subdirectories + +if(SFML_OS_WINDOWS AND SFML_USE_MESA3D) + # we are installing the files + + # if the Mesa 3D directory is not yet present, download and extract the + # files relevant for the architecture we are configured to build for + if(NOT EXISTS "${MESA3D_ARCH_PATH}") + message(STATUS "Downloading ${MESA3D_ARCHIVE}") + + file(DOWNLOAD "${MESA3D_URL}" "${MESA3D_ARCHIVE_PATH}" SHOW_PROGRESS EXPECTED_HASH SHA256=${MESA3D_SHA256}) + + if(NOT EXISTS "${MESA3D_ARCHIVE_PATH}") + message(FATAL_ERROR "Failed to download ${MESA3D_URL}") + endif() + + message(STATUS "Extracting ${MESA3D_ARCH} files from ${MESA3D_ARCHIVE}") + + execute_process(COMMAND "${CMAKE_COMMAND}" -E tar x "${MESA3D_ARCHIVE_PATH}" -- ${MESA3D_ARCH} WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/${MESA3D_ARCHIVE_DIRECTORY}") + + file(REMOVE "${MESA3D_ARCHIVE_PATH}") + endif() + + # add the files as file dependencies to a custom target that we can add as a dependency to executable/test targets + file(GLOB MESA3D_FILE_LIST "${MESA3D_ARCH_PATH}/*") + + get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + + foreach(MESA3D_FILE ${MESA3D_FILE_LIST}) + get_filename_component(MESA3D_FILE_NAME "${MESA3D_FILE}" NAME) + + list(APPEND MESA3D_INSTALLED_FILES "${PROJECT_BINARY_DIR}/bin/$,$/,>${MESA3D_FILE_NAME}") + endforeach() + + # if files are missing from the target directory of the configuration being built, copy them over + add_custom_command(OUTPUT ${MESA3D_INSTALLED_FILES} COMMAND "${CMAKE_COMMAND}" ARGS -E copy_if_different ${MESA3D_FILE_LIST} "${PROJECT_BINARY_DIR}/bin$,/$,>") + + add_custom_target(install-mesa3d DEPENDS ${MESA3D_INSTALLED_FILES}) + + set_target_properties(install-mesa3d PROPERTIES EXCLUDE_FROM_ALL TRUE) +elseif(SFML_OS_WINDOWS AND EXISTS "${MESA3D_ARCH_PATH}") + # we are removing the files + + # compile a list of file names that we have to remove + file(GLOB MESA3D_FILE_LIST "${MESA3D_ARCH_PATH}/*") + + foreach(MESA3D_FILE ${MESA3D_FILE_LIST}) + get_filename_component(MESA3D_FILE_NAME "${MESA3D_FILE}" NAME) + + list(APPEND MESA3D_FILE_NAMES "${MESA3D_FILE_NAME}") + endforeach() + + # recursively go through all files in bin and remove files that match the file name of a Mesa 3D file + file(GLOB_RECURSE BINARY_FILE_LIST "${PROJECT_BINARY_DIR}/bin/*") + + foreach(BINARY_FILE ${BINARY_FILE_LIST}) + get_filename_component(BINARY_FILE_NAME "${BINARY_FILE}" NAME) + + list(FIND MESA3D_FILE_NAMES "${BINARY_FILE_NAME}" INDEX) + + if(NOT INDEX EQUAL -1) + file(REMOVE "${BINARY_FILE}") + endif() + endforeach() +endif() diff --git a/test/Window/Context.test.cpp b/test/Window/Context.test.cpp index 57f5bee5..2cf6dfdc 100644 --- a/test/Window/Context.test.cpp +++ b/test/Window/Context.test.cpp @@ -1,8 +1,63 @@ #include +// Other 1st party headers +#include + +#include + +#include +#include #include +#if defined(SFML_SYSTEM_WINDOWS) +#define GLAPI __stdcall +#else +#define GLAPI +#endif + static_assert(!std::is_copy_constructible_v); static_assert(!std::is_copy_assignable_v); static_assert(!std::is_nothrow_move_constructible_v); static_assert(!std::is_nothrow_move_assignable_v); + +TEST_CASE("[Window] sf::Context" * doctest::skip(skipDisplayTests)) +{ + SUBCASE("Construction") + { + const sf::Context context; + + CHECK(context.getSettings().majorVersion > 0); + } + + SUBCASE("Version String") + { + sf::Context context; + + CHECK(context.setActive(true)); + + using glGetStringFuncType = const char*(GLAPI*)(unsigned int); + + auto glGetStringFunc = reinterpret_cast(sf::Context::getFunction("glGetString")); + + REQUIRE_UNARY(!!glGetStringFunc); + + constexpr unsigned int glVendor = 0x1F00; + constexpr unsigned int glRenderer = 0x1F01; + constexpr unsigned int glVersion = 0x1F02; + + const char* vendor = glGetStringFunc(glVendor); + const char* renderer = glGetStringFunc(glRenderer); + const char* version = glGetStringFunc(glVersion); + + REQUIRE(vendor != nullptr); + REQUIRE(renderer != nullptr); + REQUIRE(version != nullptr); + + MESSAGE("\nOpenGL vendor: ", + std::string(vendor), + "\nOpenGL renderer: ", + std::string(renderer), + "\nOpenGL version: ", + std::string(version)); + } +}