Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
674d4cbb1f | ||
|
|
7281ac5435 | ||
|
|
30ea08b26b | ||
|
|
77aed0eda9 | ||
|
|
9f1137b341 | ||
|
|
8e37e9adee | ||
|
|
704ac2291d | ||
|
|
8c6b37efbc | ||
|
|
c68a4fd4ec | ||
|
|
3a1a356330 | ||
|
|
969c08b532 | ||
|
|
1576bad9eb | ||
|
|
1fdd515786 | ||
|
|
ecd1d474e9 | ||
|
|
14a3aabfb4 | ||
|
|
fdc2a272e5 | ||
|
|
1d44f0d262 | ||
|
|
1e877b8ce6 | ||
|
|
102ce64676 | ||
|
|
7faee56f4a | ||
|
|
97cb31e313 | ||
|
|
f1641061fd | ||
|
|
4869fe3e06 | ||
|
|
ead56179d9 | ||
|
|
b509f3aa50 | ||
|
|
4a00047ae5 | ||
|
|
48a7b881de | ||
|
|
a45ab4e9af | ||
|
|
c43d43d69c | ||
|
|
259f2cccaf | ||
|
|
b79eed5fdb | ||
|
|
aabed00cc0 | ||
|
|
f68a361eab | ||
|
|
59f9003386 | ||
|
|
67a104366c | ||
|
|
3f8290a1e7 | ||
|
|
f800bd96ac | ||
|
|
b3d4a4679b | ||
|
|
51f5e5a719 | ||
|
|
7ebfa0765e | ||
|
|
b2dd91e019 | ||
|
|
6732d2a588 | ||
|
|
9f21a9a9c3 | ||
|
|
e4ef8bf98a | ||
|
|
32febc0bf6 | ||
|
|
3478bf6613 | ||
|
|
1940c46a70 | ||
|
|
f15291d478 | ||
|
|
f048b6a273 | ||
|
|
5a18aac261 | ||
|
|
b6d9120700 | ||
|
|
748f64e5c6 | ||
|
|
affa05921c |
@@ -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
|
||||
@@ -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 .
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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<oken=$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
|
||||
|
||||
@@ -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/
|
||||
@@ -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<oken=$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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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())>
|
||||
{
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+17
-6
@@ -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
@@ -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*, *)
|
||||
|
||||
@@ -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
|
||||
// -------------------------------------------------------
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace rkcommon {
|
||||
|
||||
void RKCOMMON_INTERFACE initTaskingSystem(int numThreads = -1,
|
||||
bool flushDenormals = false);
|
||||
void RKCOMMON_INTERFACE shutdownTaskingSystem();
|
||||
|
||||
int RKCOMMON_INTERFACE numTaskingThreads();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ¶m : params)
|
||||
if (param.first == name)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user