53 Commits
Author SHA1 Message Date
Johannes Günther 674d4cbb1f Merge branch 'devel' into 'master' for 1.15.2 release 2025-11-13 10:50:41 +01:00
Johannes Günther 7281ac5435 Version bump to 1.15.2 2025-11-13 10:50:21 +01:00
Johannes Günther 30ea08b26b Workaround clashes of constants with newer icx 2025-11-13 10:49:05 +01:00
Johannes Günther 77aed0eda9 Merge branch 'devel' into 'master' for 1.15.1 release 2025-11-10 16:34:02 +01:00
Johannes Günther 9f1137b341 Version bump to 1.15.1 2025-11-10 16:32:57 +01:00
Johannes Günther 8e37e9adee Fix missing find_dependency() with some CMake versions 2025-11-10 16:31:36 +01:00
Johannes Günther 704ac2291d CI: Bump icx to 2025.2 2025-11-10 16:31:22 +01:00
Johannes Günther 8c6b37efbc Merge branch 'devel' into 'master' for 1.15.0 release 2025-10-29 14:45:29 +01:00
Johannes Günther c68a4fd4ec Version bump to 1.15.0 2025-10-29 14:42:43 +01:00
Johannes Günther 3a1a356330 CI: Bump icx to 2025.1 2025-10-29 15:04:28 +01:00
Johannes Günther 969c08b532 Bump CMake minimum required version to 3.10, closes #15 2025-10-29 15:04:28 +01:00
Johannes Günther 1576bad9eb Optimize (orthonormal) frame() 2025-10-29 15:04:28 +01:00
Yogesh Tyagi 1fdd515786 use fully-qualified rkcommon::math::rsqrt to avoid overload ambiguity
Explicitly calling rkcommon::math::rsqrt() prevents conflict with the
standard rsqrt(double) declared in bits/mathcalls.h, fixing the build
error in Quaternion tests.

Signed-off-by: Yogesh Tyagi <yogesh.tyagi@intel.com>
2025-08-25 15:56:55 +02:00
Miroslaw Pawlowski ecd1d474e9 Merge branch 'devel' into 'master' for 1.14.2 release 2024-09-26 13:56:57 +02:00
Miroslaw Pawlowski 14a3aabfb4 Version bump to 1.14.2 2024-09-26 12:02:19 +02:00
Miroslaw Pawlowski fdc2a272e5 Synchronous 'async' and 'schedule' on internal tasking system 2024-09-26 11:43:32 +02:00
Miroslaw Pawlowski 1d44f0d262 enkiTS update to current master 2024-09-26 11:43:32 +02:00
Miroslaw Pawlowski 1e877b8ce6 A bug fix in RefCount test 2024-09-26 11:43:32 +02:00
Miroslaw Pawlowski 102ce64676 Add shutdownTaskingSystem() function call 2024-09-26 11:43:32 +02:00
Miroslaw Pawlowski 7faee56f4a CI: switch macOS testing to INTERNAL tasking 2024-09-26 11:43:32 +02:00
Krzysztof Raszkowski 97cb31e313 ci: add external linux workflow 2024-08-20 15:20:10 +02:00
Miroslaw Pawlowski f1641061fd Merge branch 'devel' into 'master' for 1.14.1 release 2024-08-06 16:30:55 +02:00
Miroslaw Pawlowski 4869fe3e06 Version bump to 1.14.1 2024-08-06 16:30:08 +02:00
Miroslaw Pawlowski ead56179d9 sse2neon: Fix Clang showing incorrect GCC version warning 2024-08-06 16:30:08 +02:00
Miroslaw Pawlowski b509f3aa50 Treat warnings as errors in CI 2024-08-06 16:30:08 +02:00
Miroslaw Pawlowski 4a00047ae5 Merge branch 'devel' into 'master' for 1.14.0 release 2024-07-17 12:19:06 +02:00
Miroslaw Pawlowski 48a7b881de CI: more platforms/OSes/compilers tested 2024-06-27 14:09:02 +02:00
Miroslaw Pawlowski a45ab4e9af Aligned malloc for Apple ARM 2024-06-27 14:09:02 +02:00
Miroslaw Pawlowski c43d43d69c sse2neon update to version 1.7.0 2024-06-27 14:09:02 +02:00
Miroslaw Pawlowski 259f2cccaf Add initTaskingSystem() call to test suite main() 2024-06-27 14:09:02 +02:00
David E. DeMarle b79eed5fdb extend string split with the ability to keep delimiters around
The new argument (defaulted to OFF) changes the behavior so that
returned substrings keep the (one character) delimiters so that
the caller can handle the delimters differently. +mayo-mustard
when ordering burgers for example could add mayonaise and hold
the mustard.
2024-06-25 16:35:03 +02:00
Johannes Günther aabed00cc0 Conversion for 16bit types 2024-06-25 12:02:41 +02:00
Miroslaw Pawlowski f68a361eab Fix for C2593 ambiguous operator error on MSVC 2022 2024-06-21 15:43:50 +02:00
Miroslaw Pawlowski 59f9003386 CI: tested OSes update 2024-06-21 15:43:50 +02:00
Miroslaw Pawlowski 67a104366c CI: switched tasking system to OpenMP on macOS 2024-06-21 15:43:50 +02:00
Miroslaw Pawlowski 3f8290a1e7 CI: changed scripts location, removed gitlab and KW scripts 2024-06-21 15:43:50 +02:00
Miroslaw Pawlowski f800bd96ac Version bump to 1.14.0, CMake min version bump to 3.5 2024-06-21 15:43:50 +02:00
Johannes Günther b3d4a4679b CI permissions 2024-03-07 10:30:19 +01:00
Johannes Günther 51f5e5a719 Fix name 2024-02-29 16:46:08 +01:00
Miroslaw Pawlowski 7ebfa0765e Merge branch 'devel' into 'master' for 1.13.0 release 2024-02-08 16:49:14 +01:00
Miroslaw Pawlowski b2dd91e019 Add DEPENDENTLOADFLAG linker parameter 2024-02-08 13:35:20 +01:00
Johannes Günther 6732d2a588 Compile fix 2023-11-15 17:59:33 +01:00
Miroslaw Pawlowski 9f21a9a9c3 Bump version to 1.13.0 2023-11-15 15:15:46 +01:00
Johannes Günther e4ef8bf98a Fix exclusive_scan_add 2023-11-15 15:15:46 +01:00
Johannes Günther 32febc0bf6 Set install RPATH 2023-11-15 15:15:46 +01:00
Miroslaw Pawlowski 3478bf6613 Fixes for Coverity found issues 2023-09-28 13:51:15 +02:00
Miroslaw Pawlowski 1940c46a70 CI: coverity fixes 2023-09-28 13:51:15 +02:00
Miroslaw Pawlowski f15291d478 Merge branch 'devel' into 'master' for 1.12.0 release 2023-09-27 11:56:55 +02:00
Miroslaw Pawlowski f048b6a273 Bump version to 1.12.0 2023-09-27 11:27:38 +02:00
Miroslaw Pawlowski 5a18aac261 Support for versioned library loading 2023-09-26 15:45:25 +02:00
Miroslaw Pawlowski b6d9120700 fscanf warning fix 2023-09-26 15:37:55 +02:00
Miroslaw Pawlowski 748f64e5c6 Use invoke_result_t with C++17 and newer 2023-09-26 15:37:55 +02:00
Johannes Günther affa05921c WA for resource deps on Windows 2023-09-26 11:17:33 +02:00
52 changed files with 10666 additions and 7347 deletions
@@ -8,8 +8,8 @@ cmake --version
cmake -L `
-G $args[0] `
-T $args[1] `
-D RKCOMMON_TASKING_SYSTEM=INTERNAL `
-D RKCOMMON_WARN_AS_ERRORS=ON `
..
cmake --build . --config Release --target ALL_BUILD
+1 -1
View File
@@ -7,6 +7,6 @@ cd build
cmake --version
cmake -DRKCOMMON_TASKING_SYSTEM=INTERNAL ..
cmake "$@" -DRKCOMMON_TASKING_SYSTEM=INTERNAL -DRKCOMMON_WARN_AS_ERRORS=ON ..
cmake --build .
+93 -29
View File
@@ -1,7 +1,7 @@
## Copyright 2022 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
name: CI Linux
name: (Internal) CI Linux
on:
push:
@@ -11,26 +11,41 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
## Build Jobs ##
build-centos7:
build-rocky8:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: centos:7
image: rockylinux:8
cmd: |
gitlab/build.sh
artifact-out: build-centos7
.github/scripts/build.sh
artifact-out: build-rocky8
artifact-path: build
build-ubuntu1804:
build-rocky8-icx:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ubuntu:18.04
image: rockylinux:8
dpcpp-version: intel/2025.2
cmd: |
gitlab/build.sh -G Ninja
artifact-out: build-ubuntu1804
export CC=icx
export CXX=icpx
export LDFLAGS="-static-intel"
.github/scripts/build.sh
artifact-out: build-rocky8-icx
artifact-path: build
build-rocky9:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: rockylinux:9
cmd: |
.github/scripts/build.sh
artifact-out: build-rocky9
artifact-path: build
build-ubuntu2004:
@@ -38,7 +53,7 @@ jobs:
with:
image: ubuntu:20.04
cmd: |
gitlab/build.sh -G Ninja
.github/scripts/build.sh -G Ninja
artifact-out: build-ubuntu2004
artifact-path: build
@@ -47,49 +62,81 @@ jobs:
with:
image: ubuntu:22.04
cmd: |
gitlab/build.sh -G Ninja
.github/scripts/build.sh -G Ninja
artifact-out: build-ubuntu2204
artifact-path: build
build-ubuntu2404:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ubuntu:24.04
cmd: |
.github/scripts/build.sh -G Ninja
artifact-out: build-ubuntu2404
artifact-path: build
build-arch:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ospray/docker-images:arch
image: archlinux:latest
cmd: |
gitlab/build.sh -G Ninja
.github/scripts/build.sh -G Ninja
artifact-out: build-arch
artifact-path: build
build-arch-clang:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ospray/docker-images:arch
image: archlinux:latest
cmd: |
export CC=clang
export CXX=clang++
gitlab/build.sh -G Ninja
.github/scripts/build.sh -G Ninja
artifact-out: build-arch-clang
artifact-path: build
build-arch-clang-sanitizer:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: archlinux:latest
cmd: |
export CC=clang
export CXX=clang++
export CFLAGS=-fsanitize=address
export CXXFLAGS=-fsanitize=address
export LDFLAGS=-fsanitize=address
.github/scripts/build.sh -G Ninja -DCMAKE_BUILD_TYPE=Debug
artifact-out: build-arch-clang-sanitizer
artifact-path: build
## Functional Test Jobs ##
test-centos7:
needs: build-centos7
test-rocky8:
needs: build-rocky8
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: centos:7
image: rockylinux:8
cmd: |
./build/rkcommon_test_suite
artifact-in: build-centos7
artifact-in: build-rocky8
test-ubuntu1804:
needs: build-ubuntu1804
test-rocky8-icx:
needs: build-rocky8-icx
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ubuntu:18.04
image: rockylinux:8
cmd: |
./build/rkcommon_test_suite
artifact-in: build-ubuntu1804
artifact-in: build-rocky8-icx
test-rocky9:
needs: build-rocky9
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: rockylinux:9
cmd: |
./build/rkcommon_test_suite
artifact-in: build-rocky9
test-ubuntu2004:
needs: build-ubuntu2004
@@ -109,11 +156,20 @@ jobs:
./build/rkcommon_test_suite
artifact-in: build-ubuntu2204
test-ubuntu2404:
needs: build-ubuntu2404
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ubuntu:24.04
cmd: |
./build/rkcommon_test_suite
artifact-in: build-ubuntu2404
test-arch:
needs: build-arch
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ospray/docker-images:arch
image: archlinux:latest
cmd: |
./build/rkcommon_test_suite
artifact-in: build-arch
@@ -122,18 +178,26 @@ jobs:
needs: build-arch-clang
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: ospray/docker-images:arch
image: archlinux:latest
cmd: |
./build/rkcommon_test_suite
artifact-in: build-arch-clang
test-arch-clang-sanitizer:
needs: build-arch-clang-sanitizer
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/docker.yml@main
with:
image: archlinux:latest
cmd: |
./build/rkcommon_test_suite
artifact-in: build-arch-clang-sanitizer
## Static Analysis ##
static-analysis:
secrets: inherit
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/static_analysis.yml@main
with:
project: rkcommon
prebuild: |
cmake -S . -B build -DRKCOMMON_TASKING_SYSTEM=INTERNAL
build: cmake --build build
project: RKCommon
prebuild: cmake -S . -B build -DRKCOMMON_TASKING_SYSTEM=INTERNAL
build: cmake --build build
+26 -3
View File
@@ -1,7 +1,7 @@
## Copyright 2022 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
name: CI MacOS
name: (Internal) CI MacOS
on:
push:
@@ -11,6 +11,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
## Build Jobs ##
@@ -18,18 +20,39 @@ jobs:
build-macos:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/macos.yml@main
with:
runs-on: '[ "macOS", "build", "x86_64" ]'
cmd: |
gitlab/build.sh
.github/scripts/build.sh
artifact-out: build-macos
artifact-path: build
build-macos-arm:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/macos.yml@main
with:
runs-on: '[ "macOS", "build", "arm" ]'
cmd: |
.github/scripts/build.sh
artifact-out: build-macos-arm
artifact-path: build
## Functional Test Jobs ##
test-macos:
needs: build-macos
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/macos.yml@main
with:
runs-on: '[ "macOS", "build", "x86_64" ]'
cmd: |
export DYLD_FALLBACK_LIBRARY_PATH=./build:$DYLD_FALLBACK_LIBRARY_PATH
./build/rkcommon_test_suite
artifact-in: build-macos
artifact-in: build-macos
test-macos-arm:
needs: build-macos-arm
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/macos.yml@main
with:
runs-on: '[ "macOS", "build", "arm" ]'
cmd: |
export DYLD_FALLBACK_LIBRARY_PATH=./build:$DYLD_FALLBACK_LIBRARY_PATH
./build/rkcommon_test_suite
artifact-in: build-macos-arm
+24 -6
View File
@@ -1,7 +1,7 @@
## Copyright 2022 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
name: CI Windows
name: (Internal) CI Windows
on:
push:
@@ -11,6 +11,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
## Build Jobs ##
@@ -19,7 +21,7 @@ jobs:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
gitlab\build.ps1 "Visual Studio 15 2017 Win64" "v141"
.github\scripts\build.ps1 "Visual Studio 15 2017 Win64"
artifact-out: build-windows-msvc15
artifact-path: build
@@ -27,10 +29,18 @@ jobs:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
gitlab\build.ps1 "Visual Studio 16 2019" "v142"
.github\scripts\build.ps1 "Visual Studio 16 2019"
artifact-out: build-windows-msvc16
artifact-path: build
build-windows-msvc17:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
.github\scripts\build.ps1 "Visual Studio 17 2022"
artifact-out: build-windows-msvc17
artifact-path: build
## Functional Test Jobs ##
test-windows-msvc15:
@@ -38,7 +48,7 @@ jobs:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
gitlab\run_tests.ps1
.github\scripts\run_tests.ps1
artifact-in: build-windows-msvc15
test-windows-msvc16:
@@ -46,5 +56,13 @@ jobs:
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
gitlab\run_tests.ps1
artifact-in: build-windows-msvc16
.github\scripts\run_tests.ps1
artifact-in: build-windows-msvc16
test-windows-msvc17:
needs: build-windows-msvc17
uses: intel-innersource/libraries.devops.renderkit.workflows/.github/workflows/windows.yml@main
with:
cmd: |
.github\scripts\run_tests.ps1
artifact-in: build-windows-msvc17
+128
View File
@@ -0,0 +1,128 @@
## Copyright 2024 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
name: CI Linux
on:
push:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
build-rocky-8:
runs-on: ubuntu-latest
container:
image: rockylinux:8
steps:
- name: Install packages
run: |
echo "Installing build dependencies..."
dnf update -y
dnf install -y git cmake tbb-devel
dnf group install -y "Development Tools"
- name: Checkout Repository
uses: actions/checkout@v4
- name: Build
run: |
mkdir build
cd build
cmake -D CMAKE_INSTALL_PREFIX=`pwd`/install ..
make -j`nproc` install
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: build-rocky-8
path: build/install
test-rocky-8:
needs: build-rocky-8
runs-on: ubuntu-latest
container:
image: rockylinux:8
steps:
- name: Install packages
run: |
echo "Installing runtime dependencies..."
dnf update -y
dnf install -y tbb
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: build-rocky-8
- name: Test
run: |
# Adding execution bit to binaries is needed since upload/download GHA is using zip compression
# and it can't preserve files permissions - https://github.com/actions/upload-artifact/issues/38
chmod +x ./bin/*
LD_LIBRARY_PATH=./lib64/ ./bin/rkcommon_test_suite -d yes
build-ubuntu-2204:
runs-on: ubuntu-latest
container:
image: ubuntu:22.04
steps:
- name: Install packages
run: |
echo "Installing build dependencies..."
apt update
apt upgrade -y
apt install -y build-essential cmake git libtbb2-dev
- name: Checkout Repository
uses: actions/checkout@v4
- name: Build
run: |
mkdir build
cd build
cmake -D CMAKE_INSTALL_PREFIX=`pwd`/install ..
make -j`nproc` install
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: build-ubuntu-2204
path: build/install
test-ubuntu-2204:
needs: build-ubuntu-2204
runs-on: ubuntu-latest
container:
image: ubuntu:22.04
steps:
- name: Install packages
run: |
echo "Installing runtime dependencies..."
apt update
apt upgrade -y
apt install -y libtbb2
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: build-ubuntu-2204
- name: Test
run: |
# Adding execution bit to binaries is needed since upload/download GHA is using zip compression
# and it can't preserve files permissions - https://github.com/actions/upload-artifact/issues/38
chmod +x ./bin/*
LD_LIBRARY_PATH=./lib/ ./bin/rkcommon_test_suite -d yes
+11 -6
View File
@@ -3,11 +3,7 @@
## Global CMake options ##
if (RKCOMMON_TASKING_SYSTEM STREQUAL "OpenMP")
cmake_minimum_required(VERSION 3.9) # NOTE(jda): rely on OpenMP targets
else()
cmake_minimum_required(VERSION 3.1)
endif()
cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -18,7 +14,7 @@ set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
## Establish project ##
project(rkcommon VERSION 1.11.0 LANGUAGES CXX)
project(rkcommon VERSION 1.15.2 LANGUAGES CXX)
include(GNUInstallDirs)
@@ -45,6 +41,15 @@ rkcommon_create_tasking_target(FALSE)
option(BUILD_SHARED_LIBS "Build rkcommon as a shared library" ON)
option(RKCOMMON_ADDRSAN "Build rkcommon with dlclose disabled for addrsan" OFF)
option(RKCOMMON_NO_SIMD "Build rkcommon not using SIMD instructions" OFF)
option(RKCOMMON_WARN_AS_ERRORS "Treat warnings as errors" OFF)
set(CMAKE_SKIP_INSTALL_RPATH OFF)
if (APPLE)
set(CMAKE_MACOSX_RPATH ON)
set(CMAKE_INSTALL_RPATH "@loader_path/")
else()
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
endif()
include(CTest)
+2 -2
View File
@@ -1,11 +1,11 @@
# rkcommon - C++/CMake infrastructure
This project represents a common set of C++ infrastructure and CMake utilities
used by various components of Intel® oneAPI Rendering Toolkit.
used by various components of Intel® Rendering Toolkit (Render Kit).
### Requirements
- CMake
- CMake v3.10 or higher
- C++11 compiler
- TBB 4.4.3 or higher (by default, other tasking system options available via
the `RKCOMMON_TASKING_SYSTEM` CMake variable)
+1 -2
View File
@@ -36,8 +36,7 @@
#
#===============================================================================
# We use INTERFACE libraries, which are only supported in 3.x
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.10)
# These two are used to automatically find the root and include directories.
set(_TBB_INCLUDE_SUBDIR "include")
+23 -41
View File
@@ -1,45 +1,6 @@
## Copyright 2009 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
# use a backported version of find_dependency(), renamed as
# find_dependency_39(), from CMake 3.9.0, which correctly supports passing
# components to find_package(). this allows us to maintain our current minimum
# CMake version of 3.1.
macro(find_dependency_39 dep)
if (NOT ${dep}_FOUND)
set(cmake_fd_quiet_arg)
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
set(cmake_fd_quiet_arg QUIET)
endif()
set(cmake_fd_required_arg)
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
set(cmake_fd_required_arg REQUIRED)
endif()
get_property(cmake_fd_alreadyTransitive GLOBAL PROPERTY
_CMAKE_${dep}_TRANSITIVE_DEPENDENCY
)
find_package(${dep} ${ARGN}
${cmake_fd_quiet_arg}
${cmake_fd_required_arg}
)
if(NOT DEFINED cmake_fd_alreadyTransitive OR cmake_fd_alreadyTransitive)
set_property(GLOBAL PROPERTY _CMAKE_${dep}_TRANSITIVE_DEPENDENCY TRUE)
endif()
if (NOT ${dep}_FOUND)
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency ${dep} could not be found.")
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
return()
endif()
set(cmake_fd_required_arg)
set(cmake_fd_quiet_arg)
set(cmake_fd_exact_arg)
endif()
endmacro()
## Macro for printing CMake variables ##
macro(print var)
message("${var} = ${${var}}")
@@ -125,6 +86,25 @@ macro(rkcommon_configure_compiler)
message(FATAL_ERROR
"Unsupported compiler specified: '${CMAKE_CXX_COMPILER_ID}'")
endif()
if (WIN32 AND NOT RKCOMMON_COMPILER_MSVC)
# workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/18311
set(CMAKE_NINJA_CMCLDEPS_RC OFF)
endif()
# setting DEPENDENTLOADFLAG:LOAD_LIBRARY_SAFE_CURRENT_DIRS on rkcommon DLL
if(WIN32)
if(RKCOMMON_COMPILER_MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEPENDENTLOADFLAG:0x2000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEPENDENTLOADFLAG:0x2000")
elseif(RKCOMMON_COMPILER_DPCPP)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /Qoption,link,/DEPENDENTLOADFLAG:0x2000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /Qoption,link,/DEPENDENTLOADFLAG:0x2000")
else()
message(WARNING "Unrecognized WIN32 compiler, DEPENDENTLOADFLAG can't be set")
endif()
endif()
endmacro()
## Tasking System macros ##
@@ -162,6 +142,8 @@ macro(rkcommon_configure_tasking_system)
endif()
endmacro()
include(CMakeFindDependencyMacro)
macro(rkcommon_create_tasking_target FROM_INSTALL)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
@@ -191,7 +173,7 @@ macro(rkcommon_create_tasking_target FROM_INSTALL)
# If not found try getting older TBB via module (FindTBB.cmake)
unset(TBB_DIR CACHE)
if (${FROM_INSTALL})
find_dependency_39(TBB 4.4 REQUIRED tbb tbbmalloc)
find_dependency(TBB 4.4 REQUIRED tbb tbbmalloc)
else()
find_package(TBB 4.4 REQUIRED tbb tbbmalloc)
endif()
@@ -201,7 +183,7 @@ macro(rkcommon_create_tasking_target FROM_INSTALL)
endif()
endif()
elseif(RKCOMMON_TASKING_OPENMP)
find_dependency_39(OpenMP)
find_dependency(OpenMP)
if (OPENMP_FOUND)
list(APPEND RKCOMMON_TASKING_LIBS OpenMP::OpenMP_CXX)
set(RKCOMMON_TASKING_DEFINITIONS RKCOMMON_TASKING_OMP)
-176
View File
@@ -1,176 +0,0 @@
variables:
GIT_DEPTH: "15"
ErrorActionPreference: STOP
stages:
- build
- test
- build-kw
- test-kw
- test-external
## Job Templates ##
.job_template: &base_build_job
stage: build
script:
- gitlab/build.sh -G Ninja
artifacts:
paths:
- build
.job_template: &build_job_docker
<<: *base_build_job
tags: [docker]
.job_template: &base_functional_test_job
type: test
script:
- ./build/rkcommon_test_suite
.job_template: &test_functional_job_docker
<<: *base_functional_test_job
tags: [docker]
## Build Jobs ##
build-centos7:
<<: *build_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:centos7
script:
- gitlab/build.sh
build-ubuntu18.04:
<<: *build_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:ubuntu18.04
build-ubuntu20.04:
<<: *build_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:ubuntu20.04
build-arch:
<<: *build_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:arch
build-arch-clang:
<<: *build_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:arch
script:
- export CC=clang
- export CXX=clang++
- gitlab/build.sh -G Ninja
build-macOS:
<<: *base_build_job
stage: build
script:
- gitlab/build.sh
tags:
- osx
- clang
build-windows-msvc15:
stage: build
script:
- gitlab\build.ps1 "Visual Studio 15 2017 Win64" "v141"
tags:
- msvc15
- win
artifacts:
paths:
- build
expire_in: 3 day
## Functional Test Jobs ##
test-centos7:
<<: *test_functional_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:centos7
dependencies: [build-centos7]
test-ubuntu18.04:
<<: *test_functional_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:ubuntu18.04
dependencies: [build-ubuntu18.04]
test-ubuntu20.04:
<<: *test_functional_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:ubuntu20.04
dependencies: [build-ubuntu20.04]
test-arch:
<<: *test_functional_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:arch
dependencies: [build-arch]
test-arch-clang:
<<: *test_functional_job_docker
image: $DOCKER_REGISTRY/ospray/docker-images:arch
dependencies: [build-arch-clang]
test-macOS:
<<: *base_functional_test_job
dependencies: [build-macOS]
script:
- export DYLD_FALLBACK_LIBRARY_PATH=./build
- ./build/rkcommon_test_suite
tags: [osx,clang]
test-windows-msvc15:
<<: *base_functional_test_job
script:
- gitlab\run_tests.ps1
dependencies: [build-windows-msvc15]
tags: [msvc15,win]
## Klocwork Jobs ##
.job_template: &base_kw_job
image: $DOCKER_REGISTRY/ospray/docker-images:centos7
tags: [docker]
build-kw:
<<: *base_kw_job
stage: build-kw
script:
- gitlab/build-kw.sh
- test -s $CI_PROJECT_DIR/klocwork/build_name
artifacts:
paths:
- $CI_PROJECT_DIR/klocwork/*
needs: []
check-kw-issues:
<<: *base_kw_job
stage: test-kw
script:
- gitlab/test-kw.sh
needs: [build-kw]
allow_failure: true
gen-kw-report:
<<: *base_kw_job
stage: test-kw
needs: [build-kw]
script:
- gitlab/kw-gen-report.sh
- gitlab/store-files.sh $CI_PROJECT_NAME $CI_PIPELINE_ID klocwork "klocwork/report.log"
artifacts:
paths:
- ./klocwork/*
## External Jobs ##
test-openvkl:
variables:
RKCOMMON_BRANCH_NAME: $CI_COMMIT_REF_NAME
RKCOMMON_PROJECT_PATH: $CI_PROJECT_PATH
RKCOMMON_GIT_SHA: $CI_COMMIT_SHA
stage: test-external
trigger:
project: renderkit/OpenVKL
branch: devel
strategy: depend
-25
View File
@@ -1,25 +0,0 @@
#!/bin/bash
## Copyright 2020 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
set -e
KW_SERVER_PATH=$KW_PATH/server
KW_CLIENT_PATH=$KW_PATH/client
export KLOCWORK_LTOKEN=/tmp/ltoken
echo "$KW_SERVER_IP;$KW_SERVER_PORT;$KW_USER;$KW_LTOKEN" > $KLOCWORK_LTOKEN
mkdir -p $CI_PROJECT_DIR/klocwork
log_file=$CI_PROJECT_DIR/klocwork/build.log
mkdir build
cd build
cmake --version
cmake -DRKCOMMON_TASKING_SYSTEM=INTERNAL ..
$KW_CLIENT_PATH/bin/kwinject make -j `nproc` | tee -a $log_file
$KW_SERVER_PATH/bin/kwbuildproject --classic --url http://$KW_SERVER_IP:$KW_SERVER_PORT/$KW_PROJECT_NAME --tables-directory $CI_PROJECT_DIR/kw_tables kwinject.out | tee -a $log_file
$KW_SERVER_PATH/bin/kwadmin --url http://$KW_SERVER_IP:$KW_SERVER_PORT/ load --force --name build-$CI_JOB_ID $KW_PROJECT_NAME $CI_PROJECT_DIR/kw_tables | tee -a $log_file
echo "build-$CI_JOB_ID" > $CI_PROJECT_DIR/klocwork/build_name
-40
View File
@@ -1,40 +0,0 @@
#!/bin/bash -xe
## Copyright 2020 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
KW_SERVER_API_URL=http://$KW_SERVER_IP:$KW_SERVER_PORT/review/api
KW_BUILD_NAME=$(cat $CI_PROJECT_DIR/klocwork/build_name)
KW_BUILD_LOG_FILE=$CI_PROJECT_DIR/klocwork/build.log
export PATH="$SHARED_TOOLS_PATH:$PATH"
[ -f $KW_BUILD_LOG_FILE ] || (echo "Build log file not found. Expected to be in: $KW_BUILD_LOG_FILE." ; exit 1;)
mkdir -p $CI_PROJECT_DIR/klocwork
report_file=$CI_PROJECT_DIR/klocwork/report.log
echo "------------------" >> $report_file
echo "Report generated at: "$(date '+%d/%m/%Y %H:%M:%S') >> $report_file
echo "Project source code url: $CI_PROJECT_URL" >> $report_file
echo "Project source code sha: $CI_COMMIT_SHA" >> $report_file
echo "Klocwork server: http://$KW_SERVER_IP:$KW_SERVER_PORT" >> $report_file
echo "------------------" >> $report_file
echo -e "\n\n\n" >> $report_file
# Get all issues list and put to report file
column_list=".id, .code, .severity, .state, .status, .taxonomyName, .owner, .url, .file, .line"
echo "------------------" >> $report_file
echo "Issues list:" >> $report_file
echo "------------------" >> $report_file
echo $column_list | sed 's/\\t/ ,/g' | column -t -s, >> $report_file
echo "------------------" >> $report_file
curl -f --data "action=search&project=$KW_PROJECT_NAME&query=build:'$KW_BUILD_NAME'&user=$KW_USER&ltoken=$KW_LTOKEN" $KW_SERVER_API_URL | jq-linux64 "[${column_list}] | @tsv" | sed 's/\\t/|/g' | column -t -s'|' | cut -d'"' -f2 >> $report_file
echo -e "\n\n\n" >> $report_file
# Attach build log to report file
echo "------------------" >> $report_file
echo "Build & scan log:" >> $report_file
echo "------------------" >> $report_file
cat $KW_BUILD_LOG_FILE >> $report_file
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash -xe
## Copyright 2019 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
project_name=$1
build_id=$2
group_name=$3
files=$4
STORAGE_DIR=$STORAGE_PATH/$project_name/$build_id/$group_name/
mkdir -p $STORAGE_DIR
cp $files $STORAGE_DIR/
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash -xe
## Copyright 2020 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
KW_ISSUES_FILE=/tmp/issues
KW_SERVER_API_URL=http://$KW_SERVER_IP:$KW_SERVER_PORT/review/api
KW_BUILD_NAME=$(cat $CI_PROJECT_DIR/klocwork/build_name)
echo "Checking for issues in $KW_BUILD_NAME ..."
curl -f --data "action=search&project=$KW_PROJECT_NAME&query=build:'$KW_BUILD_NAME'%20status:Analyze,Fix,Fix%20in%20Next%20Release,Fix%20in%20Later%20Release,Defer,Filter&user=$KW_USER&ltoken=$KW_LTOKEN" $KW_SERVER_API_URL -o $KW_ISSUES_FILE
getCriticalCount() {
cat $KW_ISSUES_FILE | wc -l
}
if [ -f $KW_ISSUES_FILE ]; then
echo "Issues found - $(getCriticalCount) in $KW_BUILD_NAME";
while IFS= read -r line; do echo $line | python -m json.tool; done < $KW_ISSUES_FILE
exit 1;
else
echo "Found no issues in $KW_BUILD_NAME"
fi
+13 -1
View File
@@ -51,6 +51,10 @@ target_include_directories(${PROJECT_NAME}
${CMAKE_CURRENT_LIST_DIR}
)
if (RKCOMMON_TASKING_INTERNAL)
target_compile_definitions(${PROJECT_NAME} PRIVATE -DENKITS_BUILD_DLL)
endif()
if (RKCOMMON_ADDRSAN)
target_compile_definitions(${PROJECT_NAME} PUBLIC -DRKCOMMON_ADDRSAN)
endif()
@@ -59,7 +63,15 @@ if (RKCOMMON_NO_SIMD)
target_compile_definitions(${PROJECT_NAME} PUBLIC -DRKCOMMON_NO_SIMD)
endif()
set_property(TARGET rkcommon PROPERTY POSITION_INDEPENDENT_CODE ON)
if (RKCOMMON_WARN_AS_ERRORS)
if(MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /WX)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -Werror)
endif()
endif()
set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)
## Install library + targets ##################################################
+3 -2
View File
@@ -14,9 +14,10 @@ namespace rkcommon {
ac -= howMany;
}
void loadLibrary(const std::string &name, const void *anchorAddress)
void loadLibrary(const void *anchorAddress, const std::string &name,
const std::vector<int> &version)
{
LibraryRepository::getInstance()->add(name, anchorAddress);
LibraryRepository::getInstance()->add(anchorAddress, name, version);
}
void unloadLibrary(const std::string &name)
+2 -1
View File
@@ -52,7 +52,8 @@ namespace rkcommon {
// anchorAddress = nullptr will disable anchored loads
RKCOMMON_INTERFACE void loadLibrary(
const std::string &name, const void *anchorAddress);
const void *anchorAddress, const std::string &name,
const std::vector<int> &version = {});
RKCOMMON_INTERFACE void unloadLibrary(const std::string &name);
+9 -4
View File
@@ -438,10 +438,15 @@ namespace rkcommon {
template <typename T>
inline LinearSpace3<T> frame(const T &N)
{
const T dx0 = cross(T(one, zero, zero), N);
const T dx1 = cross(T(zero, one, zero), N);
const T dx = normalize(dot(dx0, dx0) > dot(dx1, dx1) ? dx0 : dx1);
const T dy = normalize(cross(N, dx));
// Duff et al., "Building an Orthonormal Basis, Revisited", JCGT 2017
const typename T::Scalar sgn = N.z < (typename T::Scalar)zero
? -(typename T::Scalar)one
: (typename T::Scalar)one;
const typename T::Scalar a = -(typename T::Scalar)one / (sgn + N.z);
const typename T::Scalar b = N.x * N.y * a;
const T dx =
T((typename T::Scalar)one + sgn * N.x * N.x * a, sgn * b, -sgn * N.x);
const T dy = T(b, sgn + N.y * N.y * a, -N.y);
return LinearSpace3<T>(dx, dy, N);
}
+7 -4
View File
@@ -187,10 +187,13 @@ __define_comp_ops();
*/ \
inline univary LinearSpace3f frame(const univary vec3f N) \
{ \
const univary vec3f dx0 = make_vec3f(0.0f, N.z, -N.y); \
const univary vec3f dx1 = make_vec3f(-N.z, 0.0f, N.x); \
const univary vec3f dx = normalize(abs(N.x) < abs(N.y) ? dx0 : dx1); \
const univary vec3f dy = cross(N, dx); \
/* Duff et al., "Building an Orthonormal Basis, Revisited", JCGT 2017 */ \
const univary float sgn = N.z < 0.0f ? -1.0f : 1.0f; \
const univary float a = -1.0f / (sgn + N.z); \
const univary float b = N.x * N.y * a; \
const univary vec3f dx = \
make_vec3f(1.0f + sgn * N.x * N.x * a, sgn * b, -sgn * N.x); \
const univary vec3f dy = make_vec3f(b, sgn + N.y * N.y * a, -N.y); \
return make_LinearSpace3f(dx, dy, N); \
} \
inline univary LinearSpace3f rcp(const univary LinearSpace3f l) \
+4 -2
View File
@@ -72,7 +72,7 @@ namespace rkcommon {
}
template <typename T, typename U,
typename = traits::is_not_same_t<T, U>>
typename = traits::is_not_same_and_arithmetic_t<T, U>>
inline auto operator*(const T &a, const QuaternionT<U> &b)
-> QuaternionT<decltype(T() * U())>
{
@@ -81,7 +81,9 @@ namespace rkcommon {
return quaternion_t(scalar_t(a) * quaternion_t(b));
}
template <typename T, typename U, typename = traits::is_not_same_t<T, U>>
template <typename T,
typename U,
typename = traits::is_not_same_and_arithmetic_t<T, U>>
inline auto operator*(const QuaternionT<T> &a, const U &b)
-> QuaternionT<decltype(T() * U())>
{
-38
View File
@@ -3,48 +3,10 @@
#pragma once
/* Make precision match SSE, at the cost of some performance */
#if !defined(__aarch64__)
# define SSE2NEON_PRECISE_DIV 1
# define SSE2NEON_PRECISE_SQRT 1
#endif
#include "sse2neon.h"
__forceinline __m128 _mm_fmsub_ps(__m128 a, __m128 b, __m128 c) {
__m128 neg_c = vreinterpretq_m128_f32(vnegq_f32(vreinterpretq_f32_m128(c)));
return _mm_fmadd_ps(a, b, neg_c);
}
__forceinline __m128 _mm_fnmadd_ps(__m128 a, __m128 b, __m128 c) {
#if defined(__aarch64__)
return vreinterpretq_m128_f32(vfmsq_f32(vreinterpretq_f32_m128(c),
vreinterpretq_f32_m128(b),
vreinterpretq_f32_m128(a)));
#else
return _mm_sub_ps(c, _mm_mul_ps(a, b));
#endif
}
__forceinline __m128 _mm_fnmsub_ps(__m128 a, __m128 b, __m128 c) {
return vreinterpretq_m128_f32(vnegq_f32(vreinterpretq_f32_m128(_mm_fmadd_ps(a,b,c))));
}
/* Dummy defines for floating point control */
#define _MM_MASK_MASK 0x1f80
#define _MM_MASK_DIV_ZERO 0x200
#define _MM_FLUSH_ZERO_ON 0x8000
#define _MM_MASK_DENORM 0x100
#define _MM_SET_EXCEPTION_MASK(x)
#define _MM_SET_FLUSH_ZERO_MODE(x)
__forceinline int _mm_getcsr()
{
return 0;
}
__forceinline void _mm_mfence()
{
__sync_synchronize();
}
+8435 -6195
View File
File diff suppressed because it is too large Load Diff
+17 -6
View File
@@ -27,10 +27,21 @@ using int8 = int8_t;
// Constants
// ------------------------------------------------------------------
#ifdef ISPC
#define inf floatbits(0x7F800000)
#define pos_inf floatbits(0x7F800000)
#define neg_inf floatbits(0xFF800000)
#define nan floatbits(0x7FBFFFFF)
#else
constexpr float floatbits(unsigned int a)
{
return sycl::bit_cast<float>(a);
}
constexpr float inf = floatbits(0x7F800000);
constexpr float pos_inf = floatbits(0x7F800000);
constexpr float neg_inf = floatbits(0xFF800000);
constexpr float nan = floatbits(0x7FBFFFFF);
#endif
// smallest positive normal number 2^-126 ~ 1.17549435e-38
#define flt_min 0x1.0p-126f
@@ -137,10 +148,6 @@ inline float nextafter(const float a, const float b)
{
return sycl::nextafter(a, b);
}
inline float floatbits(unsigned int a)
{
return sycl::bit_cast<float>(a);
}
inline unsigned int intbits(float a)
{
return sycl::bit_cast<unsigned int>(a);
@@ -169,9 +176,9 @@ inline T extract(const T &t, int)
}
template <typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
inline T exclusive_scan_add(const T &t)
inline T exclusive_scan_add(const T &)
{
return t;
return 0;
}
template <typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
@@ -191,6 +198,10 @@ inline T reduce_min(const T &t)
{
return t;
}
inline float half_to_float(sycl::half x)
{
return x;
}
#endif
#define __define_functions(univary) \
+4 -4
View File
@@ -489,7 +489,7 @@ namespace rkcommon {
typename U, \
int N, \
bool A, \
typename = traits::is_not_same_t<T, U>> \
typename = traits::is_not_same_and_arithmetic_t<T, U>> \
inline auto name(const vec_t<T, N, A> &a, const vec_t<U, N, A> &b) \
->vec_t<decltype(T() op U()), N, A> \
{ \
@@ -521,7 +521,7 @@ namespace rkcommon {
typename U, \
int N, \
bool A, \
typename = traits::is_not_same_t<T, U>> \
typename = traits::is_not_same_and_arithmetic_t<T, U>> \
inline auto name(const vec_t<T, N, A> &a, const U &b) \
->vec_t<decltype(T() op U()), N, A> \
{ \
@@ -554,7 +554,7 @@ namespace rkcommon {
typename U, \
int N, \
bool A, \
typename = traits::is_not_same_t<T, U>> \
typename = traits::is_not_same_and_arithmetic_t<T, U>> \
inline auto name(const T &a, const vec_t<U, N, A> &b) \
->vec_t<decltype(T() op U()), N, A> \
{ \
@@ -563,7 +563,7 @@ namespace rkcommon {
return vector_t(scalar_t(a) op vector_t(b)); \
}
// clang-format off
// clang-format off
binary_operator(operator+, +)
binary_operator(operator-, -)
binary_operator(operator*, *)
+6 -1
View File
@@ -851,7 +851,7 @@ inline vec4f pow(const vec4f &a, const float b)
* round to nearest, see "2.3.5 Fixed-Point Data Conversions" of OpenGL 4.6 */
inline uint32 cvt_uint32(const float f)
{
return (uint32)roundf(255.f * clamp(f, 0.f, 1.f));
return (uint32)roundf(255.f * clamp(f));
}
inline uint32 cvt_uint32(const vec4f &v)
@@ -866,6 +866,11 @@ inline uint32 cvt_uint32(const vec3f &v)
| (cvt_uint32(v.z) << 16);
}
inline uint32 cvt_uint16(const float f)
{
return (uint32)roundf(65535.f * clamp(f));
}
// -------------------------------------------------------
// sRGB conversion functions
// -------------------------------------------------------
+8 -1
View File
@@ -9,6 +9,8 @@
#else
#ifdef _WIN32
#include <malloc.h>
#elif defined(__APPLE__) && (defined(__aarch64__) || defined(__arm64__))
#include <stdlib.h>
#else
#include <xmmintrin.h>
#endif
@@ -25,6 +27,9 @@ namespace rkcommon {
#else
#ifdef _WIN32
return _aligned_malloc(size, align);
#elif defined(__APPLE__) && (defined(__aarch64__) || defined(__arm64__))
void *ptr = nullptr;
return (posix_memalign(&ptr, align, size) == 0) ? ptr : nullptr;
#else // __UNIX__
return _mm_malloc(size, align);
#endif
@@ -37,7 +42,9 @@ namespace rkcommon {
scalable_aligned_free(ptr);
#else
#ifdef _WIN32
return _aligned_free(ptr);
_aligned_free(ptr);
#elif defined(__APPLE__) && (defined(__aarch64__) || defined(__arm64__))
free(ptr);
#else // __UNIX__
_mm_free(ptr);
#endif
+18 -13
View File
@@ -11,12 +11,6 @@
#include <sys/times.h>
#endif
#if defined(__MACOSX__) || defined(__APPLE__)
#define RKCOMMON_LIB_EXT ".dylib"
#else
#define RKCOMMON_LIB_EXT ".so"
#endif
namespace {
std::string directory_from_path(const std::string &path)
@@ -124,8 +118,8 @@ namespace {
namespace rkcommon {
Library::Library(
const std::string &name, const void *anchorAddress)
: libraryName(name)
const void *anchorAddress, const std::string &name, const Version &version)
: libraryName(name), libraryVersion(version)
{
bool success = false;
@@ -177,8 +171,18 @@ namespace rkcommon {
LocalFree(lpMsgBuf);
}
#else
std::string fullName = libLocation + "lib" + file + RKCOMMON_LIB_EXT;
lib = dlopen(fullName.c_str(), RTLD_LAZY | RTLD_LOCAL);
std::string versionStr;
for (int i: libraryVersion)
versionStr += "." + std::to_string(i);
std::string fullName = libLocation + "lib" + file;
#if defined(__MACOSX__) || defined(__APPLE__)
fullName += versionStr + ".dylib";
#else
fullName += ".so" + versionStr;
#endif
lib = dlopen(fullName.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (lib == nullptr)
errorMsg = dlerror();
#endif
@@ -241,13 +245,14 @@ namespace rkcommon {
}
}
void LibraryRepository::add(
const std::string &name, const void *anchorAddress)
void LibraryRepository::add(const void *anchorAddress,
const std::string &name, const Library::Version &version)
{
if (libraryExists(name))
return; // lib already loaded.
repo.push_back(rkcommon::make_unique<Library>(name, anchorAddress));
repo.push_back(rkcommon::make_unique<Library>(
anchorAddress, name, version));
}
void LibraryRepository::remove(const std::string &name)
+10 -6
View File
@@ -12,12 +12,14 @@ namespace rkcommon {
class RKCOMMON_INTERFACE Library
{
public:
/* opens a shared library; anchorAddress = nullptr will disable anchored
* loads */
Library(const std::string &name, const void *anchorAddress);
using Version = std::vector<int>;
// Opens a shared library; anchorAddress = nullptr will disable anchored loads
Library(const void *anchorAddress,
const std::string &name, const Version &version);
~Library();
/* returns address of a symbol from the library */
// Returns address of a symbol from the library
void *getSymbol(const std::string &sym) const;
private:
@@ -26,6 +28,7 @@ namespace rkcommon {
bool loadLibrary(const void *anchorAddress);
std::string libraryName;
Version libraryVersion;
std::string errorMessage;
void *lib{nullptr};
bool freeLibOnDelete{true};
@@ -48,10 +51,11 @@ namespace rkcommon {
LibraryRepository &operator=(const LibraryRepository &) = delete;
// add/remove a library to/from the repo
void add(const std::string &name, const void *anchorAddress);
void add(const void *anchorAddress,
const std::string &name, const Library::Version &version = {});
void remove(const std::string &name);
/* returns address of a symbol from any library in the repo */
// Returns address of a symbol from any library in the repo
void *getSymbol(const std::string &sym) const;
bool libraryExists(const std::string &name) const;
+4
View File
@@ -12,7 +12,11 @@ namespace rkcommon {
namespace tasking {
template <typename TASK_T>
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
using operator_return_t = std::invoke_result_t<TASK_T>;
#else
using operator_return_t = typename std::result_of<TASK_T()>::type;
#endif
// NOTE(jda) - This abstraction takes a lambda which should take captured
// variables by *value* to ensure no captured references race
+5 -2
View File
@@ -4,8 +4,6 @@
#include "TaskSys.h"
// ospray
#include "../../platform.h"
// stl
#include <thread>
namespace rkcommon {
namespace tasking {
@@ -25,6 +23,11 @@ namespace rkcommon {
g_ts->Initialize(nThreads);
}
void shutdownTaskSystemInternal()
{
g_ts.reset();
}
int numThreadsTaskSystemInternal()
{
return g_ts->GetNumTaskThreads();
+3 -27
View File
@@ -15,12 +15,11 @@ namespace rkcommon {
using Task = enki::ITaskSet;
void RKCOMMON_INTERFACE initTaskSystemInternal(int numThreads = -1);
int RKCOMMON_INTERFACE numThreadsTaskSystemInternal();
void initTaskSystemInternal(int numThreads = -1);
void shutdownTaskSystemInternal();
int numThreadsTaskSystemInternal();
void RKCOMMON_INTERFACE scheduleTaskInternal(Task *task);
void RKCOMMON_INTERFACE waitInternal(Task *task);
template <typename TASK_T>
@@ -48,29 +47,6 @@ namespace rkcommon {
scheduleTaskInternal(&task);
waitInternal(&task);
}
template <typename TASK_T>
inline void schedule_internal(TASK_T &&fcn)
{
struct LocalTask : public Task
{
TASK_T t;
LocalTask(TASK_T &&fcn) : Task(1), t(std::forward<TASK_T>(fcn)) {}
~LocalTask() override = default;
void ExecuteRange(enki::TaskSetPartition, uint32_t) override
{
t();
delete this;
}
};
auto *task = new LocalTask(std::forward<TASK_T>(fcn));
scheduleTaskInternal(task);
}
} // namespace detail
} // namespace tasking
} // namespace rkcommon
-97
View File
@@ -1,97 +0,0 @@
// Copyright (c) 2013 Doug Binks
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#pragma once
#include <stdint.h>
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#undef GetObject
#include <intrin.h>
extern "C" void _ReadWriteBarrier();
#pragma intrinsic(_ReadWriteBarrier)
#pragma intrinsic(_InterlockedCompareExchange)
#pragma intrinsic(_InterlockedExchangeAdd)
// Memory Barriers to prevent CPU and Compiler re-ordering
#define BASE_MEMORYBARRIER_ACQUIRE() _ReadWriteBarrier()
#define BASE_MEMORYBARRIER_RELEASE() _ReadWriteBarrier()
#define BASE_ALIGN(x) __declspec( align( x ) )
#else
#define BASE_MEMORYBARRIER_ACQUIRE() __asm__ __volatile__("": : :"memory")
#define BASE_MEMORYBARRIER_RELEASE() __asm__ __volatile__("": : :"memory")
#define BASE_ALIGN(x) __attribute__ ((aligned( x )))
#endif
namespace enki
{
// Atomically performs: if( *pDest == compareWith ) { *pDest = swapTo; }
// returns old *pDest (so if successfull, returns compareWith)
inline uint32_t AtomicCompareAndSwap( volatile uint32_t* pDest, uint32_t swapTo, uint32_t compareWith )
{
#ifdef _WIN32
return _InterlockedCompareExchange( (volatile long*)pDest,swapTo, compareWith );
#else
return __sync_val_compare_and_swap( pDest, compareWith, swapTo );
#endif
}
inline uint64_t AtomicCompareAndSwap( volatile uint64_t* pDest, uint64_t swapTo, uint64_t compareWith )
{
#ifdef _WIN32
return _InterlockedCompareExchange64( (__int64 volatile*)pDest, swapTo, compareWith );
#else
return __sync_val_compare_and_swap( pDest, compareWith, swapTo );
#endif
}
inline void* AtomicCompareAndSwapPtr( void* volatile* pDest, void* swapTo, void* compareWith )
{
#ifdef _WIN32
return _InterlockedCompareExchangePointer( pDest, swapTo, compareWith );
#else
return __sync_val_compare_and_swap( pDest, compareWith, swapTo );
#endif
}
// exchange ptr and return previous value
inline void* AtomicExchangePtr( void* volatile* pDest, void* swapTo )
{
#ifdef _WIN32
return _InterlockedExchangePointer( pDest, swapTo );
#else
return __sync_lock_test_and_set( pDest, swapTo );
#endif
}
// Atomically performs: tmp = *pDest; *pDest += value; return tmp;
inline int32_t AtomicAdd( volatile int32_t* pDest, int32_t value )
{
#ifdef _WIN32
return _InterlockedExchangeAdd( (long*)pDest, value );
#else
return __sync_fetch_and_add( pDest, value );
#endif
}
}
@@ -1,13 +1,13 @@
// Copyright (c) 2013 Doug Binks
//
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
@@ -19,11 +19,13 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include "Atomics.h"
#include <atomic>
#include <string.h>
#ifndef ENKI_ASSERT
#include <assert.h>
#define ENKI_ASSERT(x) assert(x)
#endif
namespace enki
{
@@ -56,7 +58,7 @@ namespace enki
// Should only be used very prudently.
bool IsPipeEmpty() const
{
return 0 == m_WriteIndex - m_ReadCount;
return 0 == m_WriteIndex.load( std::memory_order_relaxed ) - m_ReadCount.load( std::memory_order_relaxed );
}
void Clear()
@@ -77,11 +79,11 @@ namespace enki
T m_Buffer[ ms_cSize ];
// read and write indexes allow fast access to the pipe, but actual access
// controlled by the access flags.
volatile uint32_t BASE_ALIGN(4) m_WriteIndex;
volatile uint32_t BASE_ALIGN(4) m_ReadCount;
volatile uint32_t m_Flags[ ms_cSize ];
volatile uint32_t BASE_ALIGN(4) m_ReadIndex;
// controlled by the access flags.
std::atomic<uint32_t> m_WriteIndex;
std::atomic<uint32_t> m_ReadCount;
std::atomic<uint32_t> m_Flags[ ms_cSize ];
std::atomic<uint32_t> m_ReadIndex;
};
template<uint8_t cSizeLog2, typename T> inline
@@ -90,7 +92,7 @@ namespace enki
, m_ReadCount(0)
, m_ReadIndex(0)
{
assert( cSizeLog2 < 32 );
ENKI_ASSERT( cSizeLog2 < 32 );
memset( (void*)m_Flags, 0, sizeof( m_Flags ) );
}
@@ -99,18 +101,15 @@ namespace enki
{
uint32_t actualReadIndex;
uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed );
uint32_t readCount = m_ReadCount;
// We get hold of read index for consistency,
// We get hold of read index for consistency
// and do first pass starting at read count
uint32_t readIndexToUse = readCount;
while(true)
{
uint32_t writeIndex = m_WriteIndex;
uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed );
// power of two sizes ensures we can use a simple calc without modulus
uint32_t numInPipe = writeIndex - readCount;
if( 0 == numInPipe )
@@ -119,37 +118,35 @@ namespace enki
}
if( readIndexToUse >= writeIndex )
{
// move back to start
readIndexToUse = m_ReadIndex;
readIndexToUse = m_ReadIndex.load( std::memory_order_relaxed );
}
// power of two sizes ensures we can perform AND for a modulus
actualReadIndex = readIndexToUse & ms_cIndexMask;
// Multiple potential readers mean we should check if the data is valid,
// using an atomic compare exchange
uint32_t previous = AtomicCompareAndSwap( &m_Flags[ actualReadIndex ], FLAG_INVALID, FLAG_CAN_READ );
if( FLAG_CAN_READ == previous )
uint32_t previous = FLAG_CAN_READ;
bool bSuccess = m_Flags[ actualReadIndex ].compare_exchange_strong( previous, FLAG_INVALID, std::memory_order_acq_rel, std::memory_order_relaxed );
if( bSuccess )
{
break;
}
++readIndexToUse;
//update known readcount
readCount = m_ReadCount;
// Update read count
readCount = m_ReadCount.load( std::memory_order_relaxed );
}
// we update the read index using an atomic add, as we've only read one piece of data.
// this ensure consistency of the read index, and the above loop ensures readers
// only read from unread data
AtomicAdd( (volatile int32_t*)&m_ReadCount, 1 );
m_ReadCount.fetch_add(1, std::memory_order_relaxed );
BASE_MEMORYBARRIER_ACQUIRE();
// now read data, ensuring we do so after above reads & CAS
*pOut = m_Buffer[ actualReadIndex ];
m_Flags[ actualReadIndex ] = FLAG_CAN_WRITE;
m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_release );
return true;
}
@@ -157,32 +154,31 @@ namespace enki
template<uint8_t cSizeLog2, typename T> inline
bool LockLessMultiReadPipe<cSizeLog2,T>::WriterTryReadFront( T* pOut )
{
uint32_t writeIndex = m_WriteIndex;
uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed );
uint32_t frontReadIndex = writeIndex;
// Multiple potential readers mean we should check if the data is valid,
// using an atomic compare exchange - which acts as a form of lock (so not quite lockless really).
uint32_t previous = FLAG_INVALID;
uint32_t actualReadIndex = 0;
while( true )
uint32_t actualReadIndex = 0;
while(true)
{
uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed );
// power of two sizes ensures we can use a simple calc without modulus
uint32_t readCount = m_ReadCount;
uint32_t numInPipe = writeIndex - readCount;
if( 0 == numInPipe || 0 == frontReadIndex )
if( 0 == numInPipe )
{
// frontReadIndex can get to 0 here if that item was just being read by another thread.
m_ReadIndex = readCount;
m_ReadIndex.store( readCount, std::memory_order_release );
return false;
}
--frontReadIndex;
actualReadIndex = frontReadIndex & ms_cIndexMask;
previous = AtomicCompareAndSwap( &m_Flags[ actualReadIndex ], FLAG_INVALID, FLAG_CAN_READ );
if( FLAG_CAN_READ == previous )
actualReadIndex = frontReadIndex & ms_cIndexMask;
uint32_t previous = FLAG_CAN_READ;
bool success = m_Flags[ actualReadIndex ].compare_exchange_strong( previous, FLAG_INVALID, std::memory_order_acq_rel, std::memory_order_relaxed );
if( success )
{
break;
}
else if( m_ReadIndex >= frontReadIndex )
else if( m_ReadIndex.load( std::memory_order_acquire ) >= frontReadIndex )
{
return false;
}
@@ -191,13 +187,9 @@ namespace enki
// now read data, ensuring we do so after above reads & CAS
*pOut = m_Buffer[ actualReadIndex ];
m_Flags[ actualReadIndex ] = FLAG_CAN_WRITE;
m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_relaxed );
BASE_MEMORYBARRIER_RELEASE();
// 32-bit aligned stores are atomic, and writer owns the write index
// we only move one back as this is as many as we have read, not where we have read from.
--m_WriteIndex;
m_WriteIndex.store(writeIndex-1, std::memory_order_relaxed);
return true;
}
@@ -211,29 +203,21 @@ namespace enki
// impacting more than one access
uint32_t writeIndex = m_WriteIndex;
// power of two sizes ensures we can perform AND for a modulus
uint32_t actualWriteIndex = writeIndex & ms_cIndexMask;
// a reader may still be reading this item, as there are multiple readers
if( m_Flags[ actualWriteIndex ] != FLAG_CAN_WRITE )
if( m_Flags[ actualWriteIndex ].load(std::memory_order_acquire) != FLAG_CAN_WRITE )
{
return false; // still being read, so have caught up with tail.
return false; // still being read, so have caught up with tail.
}
// as we are the only writer we can update the data without atomics
// whilst the write index has not been updated
m_Buffer[ actualWriteIndex ] = in;
m_Flags[ actualWriteIndex ] = FLAG_CAN_READ;
m_Flags[ actualWriteIndex ].store( FLAG_CAN_READ, std::memory_order_release );
// We need to ensure the above writes occur prior to updating the write index,
// otherwise another thread might read before it's finished
BASE_MEMORYBARRIER_RELEASE();
// 32-bit aligned stores are atomic, and the writer controls the write index
++writeIndex;
m_WriteIndex = writeIndex;
m_WriteIndex.fetch_add(1, std::memory_order_relaxed);
return true;
}
@@ -243,8 +227,8 @@ namespace enki
template<typename T> class LocklessMultiWriteIntrusiveList
{
T* volatile pHead;
T tail;
std::atomic<T*> pHead;
T tail;
public:
LocklessMultiWriteIntrusiveList() : pHead( &tail )
{
@@ -259,9 +243,9 @@ namespace enki
// Add - safe to perform from any thread
void WriterWriteFront( T* pNode_ )
{
assert( pNode_ );
ENKI_ASSERT( pNode_ );
pNode_->pNext = NULL;
T* pPrev = (T*)AtomicExchangePtr( (void* volatile*)&pHead, (void*)pNode_ );
T* pPrev = pHead.exchange( pNode_ );
pPrev->pNext = pNode_;
}
@@ -279,14 +263,16 @@ namespace enki
}
else
{
// pTailPlus1 is the head, attempt swap with tail
tail.pNext = NULL;
T* pPrev = (T*)AtomicCompareAndSwapPtr( (void* volatile*)&pHead, (void*)&tail, (void*)pTailPlus1 );
if( pPrev != pTailPlus1 )
T* pCompare = pTailPlus1; // we need preserve pTailPlus1 as compare will alter it on failure
// pTailPlus1 is the head, attempt swap with tail
if( !pHead.compare_exchange_strong( pCompare, &tail ) )
{
// pCompare receives the revised pHead on failure.
// pTailPlus1 is no longer the head, so pTailPlus1->pNext should be non NULL
assert( pTailPlus1->pNext );
tail.pNext = pTailPlus1->pNext;
while( (T*)NULL == pTailPlus1->pNext ) {;} // wait for pNext to be updated as head may have just changed.
tail.pNext = pTailPlus1->pNext.load();
pTailPlus1->pNext = NULL;
}
}
}
File diff suppressed because it is too large Load Diff
+435 -84
View File
@@ -18,10 +18,19 @@
#pragma once
#include <atomic>
#include <thread>
#include <stdint.h>
#include "Threads.h"
#include "Atomics.h"
#include <functional>
#include <initializer_list>
// ENKITS_TASK_PRIORITIES_NUM can be set from 1 to 5.
// 1 corresponds to effectively no priorities.
#ifndef ENKITS_TASK_PRIORITIES_NUM
#define ENKITS_TASK_PRIORITIES_NUM 3
#endif
#ifndef ENKITS_API
#if defined(_WIN32) && defined(ENKITS_BUILD_DLL)
// Building enkiTS as a DLL
#define ENKITS_API __declspec(dllexport)
@@ -34,10 +43,29 @@
#else
#define ENKITS_API
#endif
#endif
// Define ENKI_CUSTOM_ALLOC_FILE_AND_LINE (at project level) to get file and line report in custom allocators,
// this is default in Debug - to turn off define ENKI_CUSTOM_ALLOC_NO_FILE_AND_LINE
#ifndef ENKI_CUSTOM_ALLOC_FILE_AND_LINE
#if defined(_DEBUG ) && !defined(ENKI_CUSTOM_ALLOC_NO_FILE_AND_LINE)
#define ENKI_CUSTOM_ALLOC_FILE_AND_LINE
#endif
#endif
#ifndef ENKI_ASSERT
#include <assert.h>
#define ENKI_ASSERT(x) assert(x)
#endif
#if (!defined(_MSVC_LANG) && __cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
#define ENKI_DEPRECATED [[deprecated]]
#else
#define ENKI_DEPRECATED
#endif
namespace enki
{
struct TaskSetPartition
{
uint32_t start;
@@ -47,23 +75,70 @@ namespace enki
class TaskScheduler;
class TaskPipe;
class PinnedTaskList;
class Dependency;
struct ThreadArgs;
struct ThreadDataStore;
struct SubTaskSet;
struct semaphoreid_t;
static constexpr uint32_t NO_THREAD_NUM = 0xFFFFFFFF;
ENKITS_API uint32_t GetNumHardwareThreads();
enum TaskPriority
{
TASK_PRIORITY_HIGH = 0,
#if ( ENKITS_TASK_PRIORITIES_NUM > 3 )
TASK_PRIORITY_MED_HI,
#endif
#if ( ENKITS_TASK_PRIORITIES_NUM > 2 )
TASK_PRIORITY_MED,
#endif
#if ( ENKITS_TASK_PRIORITIES_NUM > 4 )
TASK_PRIORITY_MED_LO,
#endif
#if ( ENKITS_TASK_PRIORITIES_NUM > 1 )
TASK_PRIORITY_LOW,
#endif
TASK_PRIORITY_NUM
};
// ICompletable is a base class used to check for completion.
// Do not use this class directly, instead derive from ITaskSet or IPinnedTask.
// Can be used with dependencies to wait for their completion.
// Derive from ITaskSet or IPinnedTask for running parallel tasks.
class ICompletable
{
public:
ICompletable() : m_RunningCount(0) {}
virtual ~ICompletable() = default;
bool GetIsComplete() {
bool bRet = ( 0 == m_RunningCount );
BASE_MEMORYBARRIER_ACQUIRE();
return bRet; }
bool GetIsComplete() const {
return 0 == m_RunningCount.load( std::memory_order_acquire );
}
virtual ~ICompletable();
// Dependency helpers, see Dependencies.cpp
void SetDependency( Dependency& dependency_, const ICompletable* pDependencyTask_ );
template<typename D, typename T, int SIZE> void SetDependenciesArr( D& dependencyArray_ , const T(&taskArray_)[SIZE] );
template<typename D, typename T> void SetDependenciesArr( D& dependencyArray_, std::initializer_list<T*> taskpList_ );
template<typename D, typename T, int SIZE> void SetDependenciesArr( D(&dependencyArray_)[SIZE], const T(&taskArray_)[SIZE] );
template<typename D, typename T, int SIZE> void SetDependenciesArr( D(&dependencyArray_)[SIZE], std::initializer_list<T*> taskpList_ );
template<typename D, typename T, int SIZE> void SetDependenciesVec( D& dependencyVec_, const T(&taskArray_)[SIZE] );
template<typename D, typename T> void SetDependenciesVec( D& dependencyVec_, std::initializer_list<T*> taskpList_ );
TaskPriority m_Priority = TASK_PRIORITY_HIGH;
protected:
// Deriving from an ICompletable and overriding OnDependenciesComplete is advanced use.
// If you do override OnDependenciesComplete() call:
// ICompletable::OnDependenciesComplete( pTaskScheduler_, threadNum_ );
// in your implementation.
virtual void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ );
private:
friend class TaskScheduler;
volatile int32_t m_RunningCount;
friend class TaskScheduler;
friend class Dependency;
std::atomic<int32_t> m_RunningCount = {0};
std::atomic<int32_t> m_DependenciesCompletedCount = {0};
int32_t m_DependenciesCount = 0;
mutable std::atomic<int32_t> m_WaitingForTaskCount = {0};
mutable Dependency* m_pDependents = NULL;
};
// Subclass ITaskSet to create tasks.
@@ -71,16 +146,9 @@ namespace enki
class ITaskSet : public ICompletable
{
public:
ITaskSet()
: m_SetSize(1)
, m_MinRange(1)
, m_RangeToRun(1)
{}
ITaskSet() = default;
ITaskSet( uint32_t setSize_ )
: m_SetSize( setSize_ )
, m_MinRange(1)
, m_RangeToRun(1)
{}
ITaskSet( uint32_t setSize_, uint32_t minRange_ )
@@ -89,49 +157,95 @@ namespace enki
, m_RangeToRun(minRange_)
{}
virtual ~ITaskSet() override = default;
// Execute range should be overloaded to process tasks. It will be called with a
// range_ where range.start >= 0; range.start < range.end; and range.end < m_SetSize;
// The range values should be mapped so that linearly processing them in order is cache friendly
// i.e. neighbouring values should be close together.
// threadnum should not be used for changing processing of data, it's intended purpose
// threadnum_ should not be used for changing processing of data, its intended purpose
// is to allow per-thread data buckets for output.
virtual void ExecuteRange( TaskSetPartition range, uint32_t threadnum ) = 0;
virtual void ExecuteRange( TaskSetPartition range_, uint32_t threadnum_ ) = 0;
// Size of set - usually the number of data items to be processed, see ExecuteRange. Defaults to 1
uint32_t m_SetSize;
// Set Size - usually the number of data items to be processed, see ExecuteRange. Defaults to 1
uint32_t m_SetSize = 1;
// Minimum size of of TaskSetPartition range when splitting a task set into partitions.
// Min Range - Minimum size of TaskSetPartition range when splitting a task set into partitions.
// Designed for reducing scheduling overhead by preventing set being
// divided up too small. Ranges passed to ExecuteRange will *not* be a multiple of this,
// only attempts to deliver range sizes larger than this most of the time.
// This should be set to a value which results in computation effort of at least 10k
// clock cycles to minimize tast scheduler overhead.
// clock cycles to minimize task scheduler overhead.
// NOTE: The last partition will be smaller than m_MinRange if m_SetSize is not a multiple
// of m_MinRange.
// Also known as grain size in literature.
uint32_t m_MinRange;
uint32_t m_MinRange = 1;
private:
friend class TaskScheduler;
uint32_t m_RangeToRun;
friend class TaskScheduler;
void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) final;
uint32_t m_RangeToRun = 1;
};
// Subclass IPinnedTask to create tasks which cab be run on a given thread only.
// Subclass IPinnedTask to create tasks which can be run on a given thread only.
class IPinnedTask : public ICompletable
{
public:
IPinnedTask() : threadNum(0), pNext(NULL) {} // default is to run a task on main thread
IPinnedTask( uint32_t threadNum_ ) : threadNum(threadNum_), pNext(NULL) {} // default is to run a task on main thread
IPinnedTask() = default;
IPinnedTask( uint32_t threadNum_ ) : threadNum(threadNum_) {} // default is to run a task on main thread
virtual ~IPinnedTask() override = default;
// IPinnedTask needs to be non-abstract for intrusive list functionality.
// Should never be called as is, should be overridden.
virtual void Execute() { ENKI_ASSERT(false); }
uint32_t threadNum = 0; // thread to run this pinned task on
std::atomic<IPinnedTask*> pNext = {NULL};
private:
void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) final;
};
// IPinnedTask needs to be non abstract for intrusive list functionality.
// Should never be called as should be overridden.
virtual void Execute() { assert(false); }
// TaskSet - a utility task set for creating tasks based on std::function.
typedef std::function<void (TaskSetPartition range, uint32_t threadnum )> TaskSetFunction;
class TaskSet : public ITaskSet
{
public:
TaskSet() = default;
TaskSet( TaskSetFunction func_ ) : m_Function( std::move(func_) ) {}
TaskSet( uint32_t setSize_, TaskSetFunction func_ ) : ITaskSet( setSize_ ), m_Function( std::move(func_) ) {}
void ExecuteRange( TaskSetPartition range_, uint32_t threadnum_ ) override { m_Function( range_, threadnum_ ); }
TaskSetFunction m_Function;
};
uint32_t threadNum; // thread to run this pinned task on
IPinnedTask* volatile pNext; // Do not use. For intrusive list only.
// LambdaPinnedTask - a utility pinned task for creating tasks based on std::func.
typedef std::function<void ()> PinnedTaskFunction;
class LambdaPinnedTask : public IPinnedTask
{
public:
LambdaPinnedTask() = default;
LambdaPinnedTask( PinnedTaskFunction func_ ) : m_Function( std::move(func_) ) {}
LambdaPinnedTask( uint32_t threadNum_, PinnedTaskFunction func_ ) : IPinnedTask( threadNum_ ), m_Function( std::move(func_) ) {}
void Execute() override { m_Function(); }
PinnedTaskFunction m_Function;
};
class Dependency
{
public:
Dependency() = default;
Dependency( const Dependency& ) = delete;
ENKITS_API Dependency( Dependency&& ) noexcept;
ENKITS_API Dependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ );
ENKITS_API ~Dependency();
ENKITS_API void SetDependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ );
ENKITS_API void ClearDependency();
ICompletable* GetTaskToRunOnCompletion() { return pTaskToRunOnCompletion; }
const ICompletable* GetDependencyTask() { return pDependencyTask; }
private:
friend class TaskScheduler; friend class ICompletable;
ICompletable* pTaskToRunOnCompletion = NULL;
const ICompletable* pDependencyTask = NULL;
Dependency* pNext = NULL;
};
// TaskScheduler implements several callbacks intended for profilers
@@ -140,8 +254,43 @@ namespace enki
{
ProfilerCallbackFunc threadStart;
ProfilerCallbackFunc threadStop;
ProfilerCallbackFunc waitStart;
ProfilerCallbackFunc waitStop;
ProfilerCallbackFunc waitForNewTaskSuspendStart; // thread suspended waiting for new tasks
ProfilerCallbackFunc waitForNewTaskSuspendStop; // thread unsuspended
ProfilerCallbackFunc waitForTaskCompleteStart; // thread waiting for task completion
ProfilerCallbackFunc waitForTaskCompleteStop; // thread stopped waiting
ProfilerCallbackFunc waitForTaskCompleteSuspendStart; // thread suspended waiting task completion
ProfilerCallbackFunc waitForTaskCompleteSuspendStop; // thread unsuspended
};
// Custom allocator, set in TaskSchedulerConfig. Also see ENKI_CUSTOM_ALLOC_FILE_AND_LINE for file_ and line_
typedef void* (*AllocFunc)( size_t align_, size_t size_, void* userData_, const char* file_, int line_ );
typedef void (*FreeFunc)( void* ptr_, size_t size_, void* userData_, const char* file_, int line_ );
ENKITS_API void* DefaultAllocFunc( size_t align_, size_t size_, void* userData_, const char* file_, int line_ );
ENKITS_API void DefaultFreeFunc( void* ptr_, size_t size_, void* userData_, const char* file_, int line_ );
struct CustomAllocator
{
AllocFunc alloc = DefaultAllocFunc;
FreeFunc free = DefaultFreeFunc;
void* userData = nullptr;
};
// TaskSchedulerConfig - configuration struct for advanced Initialize
struct TaskSchedulerConfig
{
// numTaskThreadsToCreate - Number of tasking threads the task scheduler will create. Must be > 0.
// Defaults to GetNumHardwareThreads()-1 threads as thread which calls initialize is thread 0.
uint32_t numTaskThreadsToCreate = GetNumHardwareThreads()-1;
// numExternalTaskThreads - Advanced use. Number of external threads which need to use TaskScheduler API.
// See TaskScheduler::RegisterExternalTaskThread() for usage.
// Defaults to 0. The thread used to initialize the TaskScheduler can also use the TaskScheduler API.
// Thus there are (numTaskThreadsToCreate + numExternalTaskThreads + 1) able to use the API, with this
// defaulting to the number of hardware threads available to the system.
uint32_t numExternalTaskThreads = 0;
ProfilerCallbacks profilerCallbacks = {};
CustomAllocator customAllocator;
};
class TaskScheduler
@@ -150,83 +299,285 @@ namespace enki
ENKITS_API TaskScheduler();
ENKITS_API ~TaskScheduler();
// Call either Initialize() or Initialize( numThreads_ ) before adding tasks.
// Call an Initialize function before adding tasks.
// Initialize() will create GetNumHardwareThreads()-1 threads, which is
// Initialize() will create GetNumHardwareThreads()-1 tasking threads, which is
// sufficient to fill the system when including the main thread.
// Initialize can be called multiple times - it will wait for completion
// before re-initializing.
ENKITS_API void Initialize();
// Initialize( numThreads_ ) - numThreads_ (must be > 0)
// will create numThreads_-1 threads, as thread 0 is
// Initialize( numThreadsTotal_ )
// will create numThreadsTotal_-1 threads, as thread 0 is
// the thread on which the initialize was called.
ENKITS_API void Initialize( uint32_t numThreads_ );
// numThreadsTotal_ must be > 0
ENKITS_API void Initialize( uint32_t numThreadsTotal_ );
// Initialize with advanced TaskSchedulerConfig settings. See TaskSchedulerConfig.
ENKITS_API void Initialize( TaskSchedulerConfig config_ );
// Get config. Can be called before Initialize to get the defaults.
ENKITS_API TaskSchedulerConfig GetConfig() const;
// while( !GetIsShutdownRequested() ) {} can be used in tasks which loop, to check if enkiTS has been requested to shutdown.
// If GetIsShutdownRequested() returns true should then exit. Not required for finite tasks
// Safe to use with WaitforAllAndShutdown() and ShutdownNow() where this will be set
// Not safe to use with WaitforAll(), use GetIsWaitforAllCalled() instead.
inline bool GetIsShutdownRequested() const { return m_bShutdownRequested.load( std::memory_order_acquire ); }
// while( !GetIsWaitforAllCalled() ) {} can be used in tasks which loop, to check if WaitforAll() has been called.
// If GetIsWaitforAllCalled() returns false should then exit. Not required for finite tasks
// This is intended to be used with code which calls WaitforAll().
// This is also set when the task manager is shutting down, so no need to have an additional check for GetIsShutdownRequested()
inline bool GetIsWaitforAllCalled() const { return m_bWaitforAllCalled.load( std::memory_order_acquire ); }
// Adds the TaskSet to pipe and returns if the pipe is not full.
// If the pipe is full, pTaskSet is run.
// should only be called from main thread, or within a task
ENKITS_API void AddTaskSetToPipe( ITaskSet* pTaskSet );
ENKITS_API void AddTaskSetToPipe( ITaskSet* pTaskSet_ );
// Thread 0 is main thread, otherwise use threadNum
// Pinned tasks can be added from any thread
ENKITS_API void AddPinnedTask( IPinnedTask* pTask_ );
// This function will run any IPinnedTask* for current thread, but not run other
// Main thread should call this or use a wait to ensure it's tasks are run.
// Main thread should call this or use a wait to ensure its tasks are run.
ENKITS_API void RunPinnedTasks();
// Runs the TaskSets in pipe until true == pTaskSet->GetIsComplete();
// should only be called from thread which created the taskscheduler , or within a task
// if called with 0 it will try to run tasks, and return if none available.
ENKITS_API void WaitforTask( const ICompletable* pCompletable_ );
// WaitforTaskSet, deprecated interface use WaitforTask
inline void WaitforTaskSet( const ICompletable* pCompletable_ ) { WaitforTask( pCompletable_ ); }
// Should only be called from thread which created the task scheduler, or within a task.
// If called with 0 it will try to run tasks, and return if none are available.
// To run only a subset of tasks, set priorityOfLowestToRun_ to a high priority.
// Default is lowest priority available.
// Only wait for child tasks of the current task otherwise a deadlock could occur.
// WaitforTask will exit if ShutdownNow() is called even if pCompletable_ is not complete.
ENKITS_API void WaitforTask( const ICompletable* pCompletable_, enki::TaskPriority priorityOfLowestToRun_ = TaskPriority(TASK_PRIORITY_NUM - 1) );
// Waits for all task sets to complete - not guaranteed to work unless we know we
// are in a situation where tasks aren't being continuosly added.
// are in a situation where tasks aren't being continuously added.
// If you are running tasks which loop, make sure to check GetIsWaitforAllCalled() and exit
// WaitforAll will exit if ShutdownNow() is called even if there are still tasks to run or currently running
ENKITS_API void WaitforAll();
// Waits for all task sets to complete and shutdown threads - not guaranteed to work unless we know we
// are in a situation where tasks aren't being continuosly added.
// are in a situation where tasks aren't being continuously added.
// This function can be safely called even if TaskScheduler::Initialize() has not been called.
ENKITS_API void WaitforAllAndShutdown();
// Returns the number of threads created for running tasks + 1
// to account for the main thread.
// Shutdown threads without waiting for all tasks to complete.
// Intended to be used to exit an application quickly.
// This function can be safely called even if TaskScheduler::Initialize() has not been called.
// This function will still wait for any running tasks to exit before the task threads exit.
// ShutdownNow will cause tasks which have been added to the scheduler but not completed
// to be in an undefined state in which should not be re-launched.
ENKITS_API void ShutdownNow();
// Waits for the current thread to receive a PinnedTask.
// Will not run any tasks - use with RunPinnedTasks().
// Can be used with both ExternalTaskThreads or with an enkiTS tasking thread to create
// a thread which only runs pinned tasks. If enkiTS threads are used can create
// extra enkiTS task threads to handle non-blocking computation via normal tasks.
ENKITS_API void WaitForNewPinnedTasks();
// Returns the number of threads created for running tasks + number of external threads
// plus 1 to account for the thread used to initialize the task scheduler.
// Equivalent to config values: numTaskThreadsToCreate + numExternalTaskThreads + 1.
// It is guaranteed that GetThreadNum() < GetNumTaskThreads()
ENKITS_API uint32_t GetNumTaskThreads() const;
// Returns the current task threadNum.
// Will return 0 for thread which initialized the task scheduler,
// and NO_THREAD_NUM for all other non-enkiTS threads which have not been registered ( see RegisterExternalTaskThread() ),
// and < GetNumTaskThreads() for all registered and internal enkiTS threads.
// It is guaranteed that GetThreadNum() < GetNumTaskThreads() unless it is NO_THREAD_NUM
ENKITS_API uint32_t GetThreadNum() const;
// Call on a thread to register the thread to use the TaskScheduling API.
// This is implicitly done for the thread which initializes the TaskScheduler
// Intended for developers who have threads who need to call the TaskScheduler API
// Returns true if successful, false if not.
// Can only have numExternalTaskThreads registered at any one time, which must be set
// at initialization time.
ENKITS_API bool RegisterExternalTaskThread();
// As RegisterExternalTaskThread() but explicitly requests a given thread number.
// threadNumToRegister_ must be >= GetNumFirstExternalTaskThread()
// and < ( GetNumFirstExternalTaskThread() + numExternalTaskThreads ).
ENKITS_API bool RegisterExternalTaskThread( uint32_t threadNumToRegister_ );
// Call on a thread on which RegisterExternalTaskThread has been called to deregister that thread.
ENKITS_API void DeRegisterExternalTaskThread();
// Get the number of registered external task threads.
ENKITS_API uint32_t GetNumRegisteredExternalTaskThreads();
// Get the thread number of the first external task thread. This thread
// is not guaranteed to be registered, but threads are registered in order
// from GetNumFirstExternalTaskThread() up to ( GetNumFirstExternalTaskThread() + numExternalTaskThreads )
// Note that if numExternalTaskThreads == 0 a for loop using this will be valid:
// for( uint32_t externalThreadNum = GetNumFirstExternalTaskThread();
// externalThreadNum < ( GetNumFirstExternalTaskThread() + numExternalTaskThreads
// ++externalThreadNum ) { // do something with externalThreadNum }
inline static constexpr uint32_t GetNumFirstExternalTaskThread() { return 1; }
// ------------- Start DEPRECATED Functions -------------
// DEPRECATED: use GetIsShutdownRequested() instead of GetIsRunning() in external code
// while( GetIsRunning() ) {} can be used in tasks which loop, to check if enkiTS has been shutdown.
// If GetIsRunning() returns false should then exit. Not required for finite tasks.
ENKI_DEPRECATED inline bool GetIsRunning() const { return !GetIsShutdownRequested(); }
// DEPRECATED - WaitforTaskSet, deprecated interface use WaitforTask.
ENKI_DEPRECATED inline void WaitforTaskSet( const ICompletable* pCompletable_ ) { WaitforTask( pCompletable_ ); }
// DEPRECATED - GetProfilerCallbacks. Use TaskSchedulerConfig instead.
// Returns the ProfilerCallbacks structure so that it can be modified to
// set the callbacks.
ENKITS_API ProfilerCallbacks* GetProfilerCallbacks();
// set the callbacks. Should be set prior to initialization.
ENKI_DEPRECATED inline ProfilerCallbacks* GetProfilerCallbacks() { return &m_Config.profilerCallbacks; }
// ------------- End DEPRECATED Functions -------------
private:
static THREADFUNC_DECL TaskingThreadFunction( void* pArgs );
void WaitForTasks( uint32_t threadNum );
void RunPinnedTasks( uint32_t threadNum );
bool TryRunTask( uint32_t threadNum, uint32_t& hintPipeToCheck_io_ );
void StartThreads();
void StopThreads( bool bWait_ );
void SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ );
void WakeThreads( int32_t maxToWake_ = 0 );
friend class ICompletable; friend class ITaskSet; friend class IPinnedTask;
static void TaskingThreadFunction( const ThreadArgs& args_ );
bool HaveTasks( uint32_t threadNum_ );
void WaitForNewTasks( uint32_t threadNum_ );
void WaitForTaskCompletion( const ICompletable* pCompletable_, uint32_t threadNum_ );
void RunPinnedTasks( uint32_t threadNum_, uint32_t priority_ );
bool TryRunTask( uint32_t threadNum_, uint32_t& hintPipeToCheck_io_ );
bool TryRunTask( uint32_t threadNum_, uint32_t priority_, uint32_t& hintPipeToCheck_io_ );
void StartThreads();
void StopThreads( bool bWait_ );
void SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ );
void WakeThreadsForNewTasks();
void WakeThreadsForTaskCompletion();
bool WakeSuspendedThreadsWithPinnedTasks( uint32_t threadNum_ );
void InitDependencies( ICompletable* pCompletable_ );
inline bool GetIsRunningInt() const { return m_bRunning.load( std::memory_order_acquire ); }
TaskPipe* m_pPipesPerThread;
PinnedTaskList* m_pPinnedTaskListPerThread;
ENKITS_API void TaskComplete( ICompletable* pTask_, bool bWakeThreads_, uint32_t threadNum_ );
ENKITS_API void AddTaskSetToPipeInt( ITaskSet* pTaskSet_, uint32_t threadNum_ );
ENKITS_API void AddPinnedTaskInt( IPinnedTask* pTask_ );
uint32_t m_NumThreads;
ThreadArgs* m_pThreadArgStore;
threadid_t* m_pThreadIDs;
volatile bool m_bRunning;
volatile int32_t m_NumThreadsRunning;
volatile int32_t m_NumThreadsWaiting;
uint32_t m_NumPartitions;
uint32_t m_NumInitialPartitions;
semaphoreid_t m_NewTaskSemaphore;
bool m_bHaveThreads;
ProfilerCallbacks m_ProfilerCallbacks;
template< typename T > T* NewArray( size_t num_, const char* file_, int line_ );
template< typename T > void DeleteArray( T* p_, size_t num_, const char* file_, int line_ );
template<class T, class... Args> T* New( const char* file_, int line_, Args&&... args_ );
template< typename T > void Delete( T* p_, const char* file_, int line_ );
template< typename T > T* Alloc( const char* file_, int line_ );
template< typename T > void Free( T* p_, const char* file_, int line_ );
semaphoreid_t* SemaphoreNew();
void SemaphoreDelete( semaphoreid_t* pSemaphore_ );
TaskScheduler( const TaskScheduler& nocopy );
TaskScheduler& operator=( const TaskScheduler& nocopy );
TaskPipe* m_pPipesPerThread[ TASK_PRIORITY_NUM ];
PinnedTaskList* m_pPinnedTaskListPerThread[ TASK_PRIORITY_NUM ];
uint32_t m_NumThreads;
ThreadDataStore* m_pThreadDataStore;
std::thread* m_pThreads;
std::atomic<bool> m_bRunning;
std::atomic<bool> m_bShutdownRequested;
std::atomic<bool> m_bWaitforAllCalled;
std::atomic<int32_t> m_NumInternalTaskThreadsRunning;
std::atomic<int32_t> m_NumThreadsWaitingForNewTasks;
std::atomic<int32_t> m_NumThreadsWaitingForTaskCompletion;
uint32_t m_NumPartitions;
semaphoreid_t* m_pNewTaskSemaphore;
semaphoreid_t* m_pTaskCompleteSemaphore;
uint32_t m_NumInitialPartitions;
bool m_bHaveThreads;
TaskSchedulerConfig m_Config;
std::atomic<int32_t> m_NumExternalTaskThreadsRegistered;
TaskScheduler( const TaskScheduler& nocopy_ );
TaskScheduler& operator=( const TaskScheduler& nocopy_ );
protected:
void SetCustomAllocator( CustomAllocator customAllocator_ ); // for C interface
};
inline void ICompletable::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ )
{
m_RunningCount.fetch_sub( 1, std::memory_order_acq_rel );
pTaskScheduler_->TaskComplete( this, true, threadNum_ );
}
inline void ITaskSet::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ )
{
pTaskScheduler_->AddTaskSetToPipeInt( this, threadNum_ );
}
inline void IPinnedTask::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ )
{
(void)threadNum_;
pTaskScheduler_->AddPinnedTaskInt( this );
}
inline ICompletable::~ICompletable()
{
ENKI_ASSERT( GetIsComplete() ); // this task is still waiting to run
Dependency* pDependency = m_pDependents;
while( pDependency )
{
Dependency* pNext = pDependency->pNext;
pDependency->pDependencyTask = NULL;
pDependency->pNext = NULL;
pDependency = pNext;
}
}
inline void ICompletable::SetDependency( Dependency& dependency_, const ICompletable* pDependencyTask_ )
{
ENKI_ASSERT( pDependencyTask_ != this );
dependency_.SetDependency( pDependencyTask_, this );
}
template<typename D, typename T, int SIZE>
void ICompletable::SetDependenciesArr( D& dependencyArray_ , const T(&taskArray_)[SIZE] ) {
static_assert( std::tuple_size<D>::value >= SIZE, "Size of dependency array too small" );
for( int i = 0; i < SIZE; ++i )
{
dependencyArray_[i].SetDependency( &taskArray_[i], this );
}
}
template<typename D, typename T>
void ICompletable::SetDependenciesArr( D& dependencyArray_, std::initializer_list<T*> taskpList_ ) {
ENKI_ASSERT( std::tuple_size<D>::value >= taskpList_.size() );
int i = 0;
for( auto pTask : taskpList_ )
{
dependencyArray_[i++].SetDependency( pTask, this );
}
}
template<typename D, typename T, int SIZE>
void ICompletable::SetDependenciesArr( D(&dependencyArray_)[SIZE], const T(&taskArray_)[SIZE] ) {
for( int i = 0; i < SIZE; ++i )
{
dependencyArray_[i].SetDependency( &taskArray_[i], this );
}
}
template<typename D, typename T, int SIZE>
void ICompletable::SetDependenciesArr( D(&dependencyArray_)[SIZE], std::initializer_list<T*> taskpList_ ) {
ENKI_ASSERT( SIZE >= taskpList_.size() );
int i = 0;
for( auto pTask : taskpList_ )
{
dependencyArray_[i++].SetDependency( pTask, this );
}
}
template<typename D, typename T, int SIZE>
void ICompletable::SetDependenciesVec( D& dependencyVec_, const T(&taskArray_)[SIZE] ) {
dependencyVec_.resize( SIZE );
for( int i = 0; i < SIZE; ++i )
{
dependencyVec_[i].SetDependency( &taskArray_[i], this );
}
}
template<typename D, typename T>
void ICompletable::SetDependenciesVec( D& dependencyVec_, std::initializer_list<T*> taskpList_ ) {
dependencyVec_.resize( taskpList_.size() );
int i = 0;
for( auto pTask : taskpList_ )
{
dependencyVec_[i++].SetDependency( pTask, this );
}
}
}
-210
View File
@@ -1,210 +0,0 @@
// Copyright (c) 2013 Doug Binks
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#pragma once
#include <stdint.h>
#include <assert.h>
#ifdef _WIN32
#include "Atomics.h"
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#define THREADFUNC_DECL DWORD WINAPI
#define THREAD_LOCAL __declspec( thread )
namespace enki
{
typedef HANDLE threadid_t;
// declare the thread start function as:
// THREADFUNC_DECL MyThreadStart( void* pArg );
inline bool ThreadCreate( threadid_t* returnid, DWORD ( WINAPI *StartFunc) (void* ), void* pArg )
{
// posix equiv pthread_create
DWORD threadid;
*returnid = CreateThread( 0, 0, StartFunc, pArg, 0, &threadid );
return *returnid != NULL;
}
inline bool ThreadTerminate( threadid_t threadid )
{
// posix equiv pthread_cancel
return CloseHandle( threadid ) == 0;
}
inline uint32_t GetNumHardwareThreads()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return sysInfo.dwNumberOfProcessors;
}
}
#else // posix
#include <pthread.h>
#include <unistd.h>
#define THREADFUNC_DECL void*
#define THREAD_LOCAL __thread
namespace enki
{
typedef pthread_t threadid_t;
// declare the thread start function as:
// THREADFUNC_DECL MyThreadStart( void* pArg );
inline bool ThreadCreate( threadid_t* returnid, void* ( *StartFunc) (void* ), void* pArg )
{
// posix equiv pthread_create
int32_t retval = pthread_create( returnid, NULL, StartFunc, pArg );
return retval == 0;
}
inline bool ThreadTerminate( threadid_t threadid )
{
// posix equiv pthread_cancel
pthread_cancel( threadid );
return pthread_detach( threadid ) == 0;
}
inline uint32_t GetNumHardwareThreads()
{
return (uint32_t)sysconf( _SC_NPROCESSORS_ONLN );
}
}
#endif // posix
// Semaphore implementation
#ifdef _WIN32
namespace enki
{
struct semaphoreid_t
{
HANDLE sem;
};
inline void SemaphoreCreate( semaphoreid_t& semaphoreid )
{
semaphoreid.sem = CreateSemaphore(NULL, 0, MAXLONG, NULL );
}
inline void SemaphoreClose( semaphoreid_t& semaphoreid )
{
CloseHandle( semaphoreid.sem );
}
inline void SemaphoreWait( semaphoreid_t& semaphoreid )
{
DWORD retval = WaitForSingleObject( semaphoreid.sem, INFINITE );
assert( retval != WAIT_FAILED );
}
inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting )
{
if( countWaiting )
{
ReleaseSemaphore( semaphoreid.sem, countWaiting, NULL );
}
}
}
#elif defined(__MACH__)
// OS X does not have POSIX semaphores
// see https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KernelProgramming/synchronization/synchronization.html
#include <mach/mach.h>
namespace enki
{
struct semaphoreid_t
{
semaphore_t sem;
};
inline void SemaphoreCreate( semaphoreid_t& semaphoreid )
{
semaphore_create( mach_task_self(), &semaphoreid.sem, SYNC_POLICY_FIFO, 0 );
}
inline void SemaphoreClose( semaphoreid_t& semaphoreid )
{
semaphore_destroy( mach_task_self(), semaphoreid.sem );
}
inline void SemaphoreWait( semaphoreid_t& semaphoreid )
{
semaphore_wait( semaphoreid.sem );
}
inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting )
{
while( countWaiting-- > 0 )
{
semaphore_signal( semaphoreid.sem );
}
}
}
#else // POSIX
#include <semaphore.h>
namespace enki
{
struct semaphoreid_t
{
sem_t sem;
};
inline void SemaphoreCreate( semaphoreid_t& semaphoreid )
{
sem_init( &semaphoreid.sem, 0, 0 );
}
inline void SemaphoreClose( semaphoreid_t& semaphoreid )
{
sem_destroy( &semaphoreid.sem );
}
inline void SemaphoreWait( semaphoreid_t& semaphoreid )
{
sem_wait( &semaphoreid.sem );
}
inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting )
{
while( countWaiting-- > 0 )
{
sem_post( &semaphoreid.sem );
}
}
}
#endif
+1 -3
View File
@@ -28,9 +28,7 @@ namespace rkcommon {
#elif defined(RKCOMMON_TASKING_OMP)
std::thread thread(fcn);
thread.detach();
#elif defined(RKCOMMON_TASKING_INTERNAL)
detail::schedule_internal(std::move(fcn));
#else// Debug --> synchronous!
#else// Internal & Debug --> synchronous!
fcn();
#endif
}
@@ -66,6 +66,15 @@ namespace rkcommon {
#endif
}
~tasking_system_handle()
{
#if defined(RKCOMMON_TASKING_TBB)
tbb_gc.reset();
#elif defined(RKCOMMON_TASKING_INTERNAL)
detail::shutdownTaskSystemInternal();
#endif
}
int num_threads()
{
#if defined(RKCOMMON_TASKING_TBB)
@@ -98,6 +107,11 @@ namespace rkcommon {
g_tasking_handle = make_unique<tasking_system_handle>(numThreads);
}
void shutdownTaskingSystem()
{
g_tasking_handle.reset();
}
int numTaskingThreads()
{
if (!g_tasking_handle.get())
+1
View File
@@ -10,6 +10,7 @@ namespace rkcommon {
void RKCOMMON_INTERFACE initTaskingSystem(int numThreads = -1,
bool flushDenormals = false);
void RKCOMMON_INTERFACE shutdownTaskingSystem();
int RKCOMMON_INTERFACE numTaskingThreads();
+5 -4
View File
@@ -326,10 +326,11 @@ void getProcMemUse(uint64_t &virtMem, uint64_t &resMem)
FILE *file = std::fopen("/proc/self/statm", "r");
if (file) {
// These values are measured in pages
std::fscanf(file, "%lu %lu", &virtMem, &resMem);
const int pageSize = getpagesize();
virtMem *= pageSize;
resMem *= pageSize;
if (std::fscanf(file, "%lu %lu", &virtMem, &resMem) == 2) {
const int pageSize = getpagesize();
virtMem *= pageSize;
resMem *= pageSize;
}
std::fclose(file);
}
#endif
+6
View File
@@ -137,5 +137,11 @@ namespace rkcommon {
template <typename T>
using is_arithmetic_t = enable_if_t<std::is_arithmetic<T>::value>;
// type 'T1' and 'T2' are not the same and arithmetic /////////////////////
template <typename T1, typename T2>
using is_not_same_and_arithmetic_t = enable_if_t<!std::is_same<T1, T2>::value
&& std::is_arithmetic<T1>::value && std::is_arithmetic<T2>::value>;
} // namespace traits
} // namespace rkcommon
+1 -1
View File
@@ -114,7 +114,7 @@ namespace rkcommon {
/*! check if the given parameter was specified */
bool PseudoURL::hasParam(const std::string &name)
{
for (auto param : params)
for (auto &param : params)
if (param.first == name)
return true;
return false;
+4 -1
View File
@@ -47,7 +47,8 @@ namespace rkcommon {
/* split a string on a set of delimiters */
inline std::vector<std::string> split(const std::string &input,
const std::string &delim)
const std::string &delim,
const bool keepDelim = false)
{
std::vector<std::string> tokens;
size_t pos = 0;
@@ -56,6 +57,8 @@ namespace rkcommon {
if (begin == input.npos)
return tokens;
size_t end = input.find_first_of(delim, begin);
if (keepDelim && begin != 0)
begin--;
tokens.push_back(input.substr(
begin, (end == input.npos) ? input.npos : (end - begin)));
pos = end;
+15 -1
View File
@@ -1,5 +1,19 @@
// Copyright 2009 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
#include "rkcommon/tasking/tasking_system_init.h"
int main(int argc, char* argv[]) {
// Spawn workers threads
rkcommon::tasking::initTaskingSystem();
// Run all tests
auto result = Catch::Session().run(argc, argv);
// Terminate worker threads
rkcommon::tasking::shutdownTaskingSystem();
return result;
}
+1 -1
View File
@@ -187,7 +187,7 @@ template <typename T>
inline void test_slerp()
{
typename T::Scalar two = 2;
REQUIRE(CmpT(slerp(.5f, T(1, 0, 0, 0), T(0, 1, 0, 0)), T(rsqrt(two), rsqrt(two), 0, 0)));
REQUIRE(CmpT(slerp(.5f, T(1, 0, 0, 0), T(0, 1, 0, 0)), T(rkcommon::math::rsqrt(two), rkcommon::math::rsqrt(two), 0, 0)));
}
TEST_CASE("Quaternion functions", "[quat]")
+4 -2
View File
@@ -129,8 +129,10 @@ inline void test_intersectRayBox()
using V = vec_t<T, N>;
using X = box_t<T, N>;
range_t<T> r = intersectRayBox(V(0), normalize(V(1)), X(V(1), V(2)));
REQUIRE(abs(r.lower - length(V(1))) <= T(ulp));
REQUIRE(abs(r.upper - length(V(2))) <= 2 * T(ulp)); // rcp is multiplied by box.upper
// Tolerance 2x higher because of sse2neon
REQUIRE(abs(r.lower - length(V(1))) <= 2 * T(ulp));
REQUIRE(abs(r.upper - length(V(2))) <= 4 * T(ulp)); // rcp is multiplied by box.upper
}
TEST_CASE("box intersectRayBox function", "[box]")
+2 -2
View File
@@ -49,8 +49,8 @@ TEST_CASE("rkmath rcp_safe function", "[rkmath]")
template <typename T>
inline void test_rsqrt()
{
REQUIRE(CmpT<T>(rsqrt(T(1)), T(1)));
REQUIRE(CmpT<T>(rsqrt(T(4)), T(.5)));
REQUIRE(CmpT<T>(rkcommon::math::rsqrt(T(1)), T(1)));
REQUIRE(CmpT<T>(rkcommon::math::rsqrt(T(4)), T(.5)));
}
TEST_CASE("rkmath rsqrt function", "[rkmath]")
+5
View File
@@ -18,6 +18,11 @@ SCENARIO("Ref interface tests", "[Ref]")
};
const Ref<A> a(new A);
// Reference count is 2 at this moment we need to decrease it by 1,
// this would be not needed once we initialize reference count with 0
// instead of 1
a->refDec();
WHEN("setting values via pointer or reference")
{
A &aRef = *a;
+1 -1
View File
@@ -42,7 +42,7 @@ inline void verify_N(const ArrayView<T> &v, int N)
template <typename T, int SIZE>
inline std::array<T, SIZE> make_test_array()
{
std::array<T, SIZE> a;
std::array<T, SIZE> a = {};
std::iota(a.begin(), a.end(), 0);
return a;
}
+2
View File
@@ -96,6 +96,7 @@ SCENARIO("Optional<> construction", "[Optional]")
WHEN("Used to move construct from Optional<> with value")
{
Optional<int> value_opt(5);
Optional<int> copy_opt(std::move(value_opt));
THEN("The new Optional<> should have the same value")
{
@@ -105,6 +106,7 @@ SCENARIO("Optional<> construction", "[Optional]")
WHEN("Used to move construct from Optional<> with value and different type")
{
Optional<int> value_opt(5);
Optional<float> copy_opt(std::move(value_opt));
THEN("The new Optional<> should have the right value")
{