From e4d781346bea5cb16a6f4331dca5810cc4084fe8 Mon Sep 17 00:00:00 2001 From: Willy Scheibel Date: Tue, 6 Mar 2018 14:30:52 +0100 Subject: [PATCH] Initial release --- .gitignore | 41 +++ AUTHORS | 2 + CMakeLists.txt | 173 +++++++++ LICENSE | 20 ++ README.md | 1 + cmake/CompileOptions.cmake | 148 ++++++++ cmake/ComponentInstall.cmake | 6 + cmake/Custom.cmake | 46 +++ cmake/GetGitRevisionDescription.cmake | 125 +++++++ cmake/GetGitRevisionDescription.cmake.in | 38 ++ cmake/RuntimeDependencies.cmake | 19 + configure | 126 +++++++ data/unifiedmemory/shaders/average.frag | 10 + data/unifiedmemory/shaders/average.geom | 19 + data/unifiedmemory/shaders/average.vert | 35 ++ .../shaders/averagecomputation.comp | 93 +++++ .../shaders/linedatatransfer.comp | 32 ++ data/unifiedmemory/shaders/plot.frag | 10 + data/unifiedmemory/shaders/plot.vert | 37 ++ .../unifiedmemory/shaders/sumcomputation.comp | 93 +++++ deploy/CMakeLists.txt | 30 ++ deploy/packages/pack-unifiedmemory.cmake | 259 ++++++++++++++ docs/CMakeLists.txt | 17 + docs/manual/.gitignore | 5 + docs/manual/CMakeLists.txt | 57 +++ docs/manual/unifiedmemory.tex | 34 ++ source/CMakeLists.txt | 28 ++ source/sensor/CMakeLists.txt | 113 ++++++ source/sensor/main.cpp | 104 ++++++ source/unifiedmemory/CMakeLists.txt | 144 ++++++++ source/unifiedmemory/Computation.cpp | 311 +++++++++++++++++ source/unifiedmemory/Computation.h | 63 ++++ source/unifiedmemory/Rendering.cpp | 328 ++++++++++++++++++ source/unifiedmemory/Rendering.h | 66 ++++ source/unifiedmemory/common.cpp | 167 +++++++++ source/unifiedmemory/common.h | 30 ++ source/unifiedmemory/main.cpp | 232 +++++++++++++ source/unifiedmemory/unified_allocator.h | 92 +++++ source/unifiedmemory/unified_allocator.inl | 62 ++++ source/unifiedmemory/unified_vector.h | 84 +++++ source/unifiedmemory/unified_vector.inl | 127 +++++++ source/version.h.in | 15 + 42 files changed, 3442 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/CompileOptions.cmake create mode 100644 cmake/ComponentInstall.cmake create mode 100644 cmake/Custom.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in create mode 100644 cmake/RuntimeDependencies.cmake create mode 100755 configure create mode 100644 data/unifiedmemory/shaders/average.frag create mode 100644 data/unifiedmemory/shaders/average.geom create mode 100644 data/unifiedmemory/shaders/average.vert create mode 100644 data/unifiedmemory/shaders/averagecomputation.comp create mode 100644 data/unifiedmemory/shaders/linedatatransfer.comp create mode 100644 data/unifiedmemory/shaders/plot.frag create mode 100644 data/unifiedmemory/shaders/plot.vert create mode 100644 data/unifiedmemory/shaders/sumcomputation.comp create mode 100644 deploy/CMakeLists.txt create mode 100644 deploy/packages/pack-unifiedmemory.cmake create mode 100644 docs/CMakeLists.txt create mode 100644 docs/manual/.gitignore create mode 100644 docs/manual/CMakeLists.txt create mode 100644 docs/manual/unifiedmemory.tex create mode 100644 source/CMakeLists.txt create mode 100644 source/sensor/CMakeLists.txt create mode 100644 source/sensor/main.cpp create mode 100644 source/unifiedmemory/CMakeLists.txt create mode 100644 source/unifiedmemory/Computation.cpp create mode 100644 source/unifiedmemory/Computation.h create mode 100644 source/unifiedmemory/Rendering.cpp create mode 100644 source/unifiedmemory/Rendering.h create mode 100644 source/unifiedmemory/common.cpp create mode 100644 source/unifiedmemory/common.h create mode 100644 source/unifiedmemory/main.cpp create mode 100644 source/unifiedmemory/unified_allocator.h create mode 100644 source/unifiedmemory/unified_allocator.inl create mode 100644 source/unifiedmemory/unified_vector.h create mode 100644 source/unifiedmemory/unified_vector.inl create mode 100644 source/version.h.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1019507 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a + +# Build dir +build* +debug_build +release_build +/bin +/lib +/install + +# Qt cache +CMakeLists.txt.user +CMakeLists.txt.user.* + +# IDE project files +*.sublime-project +*.sublime-workspace + +# Local config windows +_configure.bat +_open-project.bat +_start-cmake-gui.bat +_start-cmd.bat + +# Local config unix +.localconfig + +# Taken screenshots +screenshots/* diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..4e73c74 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ + +Willy Scheibel diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a65db53 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,173 @@ + +# +# CMake options +# + +# CMake version +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + +# Include cmake modules + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set(WriterCompilerDetectionHeaderFound NOTFOUND) + +include(GenerateExportHeader) +include(ExternalProject) + +# This module is only available with CMake >=3.1, so check whether it could be found +include(WriteCompilerDetectionHeader OPTIONAL RESULT_VARIABLE WriterCompilerDetectionHeaderFound) + +include(cmake/GetGitRevisionDescription.cmake) +include(cmake/Custom.cmake) + +# Set policies +set_policy(CMP0028 NEW) # ENABLE CMP0028: Double colon in target name means ALIAS or IMPORTED target. +set_policy(CMP0054 NEW) # ENABLE CMP0054: Only interpret if() arguments as variables or keywords when unquoted. +set_policy(CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by default. +set_policy(CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for all target types. + + +# +# Project description and (meta) information +# + +# Get git revision +get_git_head_revision(GIT_REFSPEC GIT_SHA1) +string(SUBSTRING "${GIT_SHA1}" 0 12 GIT_REV) + +# Meta information about the project +set(META_PROJECT_NAME "unifiedmemory") +set(META_PROJECT_DESCRIPTION "Unified Memory Heterogenous Computing Showcase") +set(META_AUTHOR_ORGANIZATION "CG Internals") +set(META_AUTHOR_DOMAIN "git@code.sbusch.net:willy.scheibel/unifiedmemory.git") +set(META_AUTHOR_MAINTAINER "opensource@cginternals.com") +set(META_VERSION_MAJOR "0") +set(META_VERSION_MINOR "0") +set(META_VERSION_PATCH "0") +set(META_VERSION_REVISION "${GIT_REV}") +set(META_VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}") +set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})") + + +# +# Project configuration options +# + +# Project options +option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) +option(OPTION_SELF_CONTAINED "Create a self-contained install with all dependencies." OFF) +option(OPTION_BUILD_DOCS "Build documentation." OFF) + + +# +# Declare project +# + +# Generate folders for IDE targets (e.g., VisualStudio solutions) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(IDE_FOLDER "") + +# Declare project +project(${META_PROJECT_NAME} C CXX) + +# Set output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +# Create version file +file(WRITE "${PROJECT_BINARY_DIR}/VERSION" "${META_NAME_VERSION}") + + +# +# Compiler settings and options +# + +include(cmake/CompileOptions.cmake) + + +# +# Deployment/installation setup +# + +# Get project name +set(project ${META_PROJECT_NAME}) + +# Check for system dir install +set(SYSTEM_DIR_INSTALL FALSE) +if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr" OR "${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") + set(SYSTEM_DIR_INSTALL TRUE) +endif() + +# Installation paths +if(UNIX AND SYSTEM_DIR_INSTALL) + # Install into the system (/usr/bin or /usr/local/bin) + set(INSTALL_ROOT "share/${project}") # /usr/[local]/share/ + set(INSTALL_CMAKE "share/${project}/cmake") # /usr/[local]/share//cmake + set(INSTALL_EXAMPLES "share/${project}") # /usr/[local]/share/ + set(INSTALL_DATA "share/${project}") # /usr/[local]/share/ + set(INSTALL_BIN "bin") # /usr/[local]/bin + set(INSTALL_SHARED "lib") # /usr/[local]/lib + set(INSTALL_LIB "lib") # /usr/[local]/lib + set(INSTALL_INCLUDE "include") # /usr/[local]/include + set(INSTALL_DOC "share/doc/${project}") # /usr/[local]/share/doc/ + set(INSTALL_SHORTCUTS "share/applications") # /usr/[local]/share/applications + set(INSTALL_ICONS "share/pixmaps") # /usr/[local]/share/pixmaps + set(INSTALL_INIT "/etc/init") # /etc/init (upstart init scripts) +else() + # Install into local directory + set(INSTALL_ROOT ".") # ./ + set(INSTALL_CMAKE "cmake") # ./cmake + set(INSTALL_EXAMPLES ".") # ./ + set(INSTALL_DATA ".") # ./ + set(INSTALL_BIN ".") # ./ + set(INSTALL_SHARED "lib") # ./lib + set(INSTALL_LIB "lib") # ./lib + set(INSTALL_INCLUDE "include") # ./include + set(INSTALL_DOC "doc") # ./doc + set(INSTALL_SHORTCUTS "misc") # ./misc + set(INSTALL_ICONS "misc") # ./misc + set(INSTALL_INIT "misc") # ./misc +endif() + +# Set runtime path +set(CMAKE_SKIP_BUILD_RPATH FALSE) # Add absolute path to all dependencies for BUILD +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # Use CMAKE_INSTALL_RPATH for INSTALL +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # Do NOT add path to dependencies for INSTALL + +if(NOT SYSTEM_DIR_INSTALL) + # Find libraries relative to binary + if(APPLE) + set(CMAKE_INSTALL_RPATH "@loader_path/../../../${INSTALL_LIB}") + else() + set(CMAKE_INSTALL_RPATH "$ORIGIN/${INSTALL_LIB}") + endif() +endif() + + +# +# Project modules +# + +add_subdirectory(source) +add_subdirectory(docs) +add_subdirectory(deploy) + + +# +# Deployment (global project files) +# + +# Install version file +install(FILES "${PROJECT_BINARY_DIR}/VERSION" DESTINATION ${INSTALL_ROOT} COMPONENT runtime) + +# Install cmake find script for the project +install(FILES ${META_PROJECT_NAME}-config.cmake DESTINATION ${INSTALL_ROOT} COMPONENT dev) + +# Install the project meta files +install(FILES AUTHORS DESTINATION ${INSTALL_ROOT} COMPONENT runtime) +install(FILES LICENSE DESTINATION ${INSTALL_ROOT} COMPONENT runtime) +install(FILES README.md DESTINATION ${INSTALL_ROOT} COMPONENT runtime) + +# Install runtime data +install(DIRECTORY ${PROJECT_SOURCE_DIR}/data DESTINATION ${INSTALL_DATA} COMPONENT runtime) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ec9c07c --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ + +Copyright (c) 2017-2018 CG Internals + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..77c7672 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Unified Memory Heterogenous Computing Showcase \ No newline at end of file diff --git a/cmake/CompileOptions.cmake b/cmake/CompileOptions.cmake new file mode 100644 index 0000000..6e6fe83 --- /dev/null +++ b/cmake/CompileOptions.cmake @@ -0,0 +1,148 @@ + +# +# Platform and architecture setup +# + +# Get upper case system name +string(TOUPPER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME_UPPER) + +# Determine architecture (32/64 bit) +set(X64 OFF) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(X64 ON) +endif() + + +# +# Project options +# + +set(DEFAULT_PROJECT_OPTIONS + DEBUG_POSTFIX "d" + CXX_STANDARD 14 # Not available before CMake 3.1; see below for manual command line argument addition + LINKER_LANGUAGE "CXX" + POSITION_INDEPENDENT_CODE ON + CXX_VISIBILITY_PRESET "hidden" +) + + +# +# Include directories +# + +set(DEFAULT_INCLUDE_DIRECTORIES) + + +# +# Libraries +# + +set(DEFAULT_LIBRARIES) + + +# +# Compile definitions +# + +set(DEFAULT_COMPILE_DEFINITIONS + SYSTEM_${SYSTEM_NAME_UPPER} +) + +# MSVC compiler options +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + set(DEFAULT_COMPILE_DEFINITIONS ${DEFAULT_COMPILE_DEFINITIONS} + _SCL_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the Standard C++ Library + _CRT_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the CRT Library + ) +endif () + + +# +# Compile options +# + +set(DEFAULT_COMPILE_OPTIONS) + +# MSVC compiler options +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} + /MP # -> build with multiple processes + /W4 # -> warning level 4 + # /WX # -> treat warnings as errors + + /wd4251 # -> disable warning: 'identifier': class 'type' needs to have dll-interface to be used by clients of class 'type2' + /wd4592 # -> disable warning: 'identifier': symbol will be dynamically initialized (implementation limitation) + # /wd4201 # -> disable warning: nonstandard extension used: nameless struct/union (caused by GLM) + # /wd4127 # -> disable warning: conditional expression is constant (caused by Qt) + + #$<$: + #/RTCc # -> value is assigned to a smaller data type and results in a data loss + #> + + $<$: + /Gw # -> whole program global optimization + /GS- # -> buffer security check: no + /GL # -> whole program optimization: enable link-time code generation (disables Zi) + /GF # -> enable string pooling + > + + # No manual c++11 enable for MSVC as all supported MSVC versions for cmake-init have C++11 implicitly enabled (MSVC >=2013) + ) +endif () + +# GCC and Clang compiler options +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} + -Wall + -Wextra + -Wunused + + -Wreorder + -Wignored-qualifiers + -Wmissing-braces + -Wreturn-type + -Wswitch + -Wswitch-default + -Wuninitialized + -Wmissing-field-initializers + + $<$: + -Wmaybe-uninitialized + + $<$,4.8>: + -Wpedantic + + -Wreturn-local-addr + > + > + + $<$: + -Wpedantic + + -Wreturn-stack-address + > + + $<$: + -pthread + > + + # Required for CMake < 3.1; should be removed if minimum required CMake version is raised. + $<$: + -std=c++14 + > + ) +endif () + + +# +# Linker options +# + +set(DEFAULT_LINKER_OPTIONS) + +# Use pthreads on mingw and linux +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + set(DEFAULT_LINKER_OPTIONS + -pthread + ) +endif() diff --git a/cmake/ComponentInstall.cmake b/cmake/ComponentInstall.cmake new file mode 100644 index 0000000..8606060 --- /dev/null +++ b/cmake/ComponentInstall.cmake @@ -0,0 +1,6 @@ + +# Execute cmake_install.cmake wrapper that allows to pass both DESTDIR and COMPONENT environment variable + +execute_process( + COMMAND ${CMAKE_COMMAND} -DCOMPONENT=$ENV{COMPONENT} -P cmake_install.cmake +) diff --git a/cmake/Custom.cmake b/cmake/Custom.cmake new file mode 100644 index 0000000..8b1ff5e --- /dev/null +++ b/cmake/Custom.cmake @@ -0,0 +1,46 @@ + +# Set policy if policy is available +function(set_policy POL VAL) + + if(POLICY ${POL}) + cmake_policy(SET ${POL} ${VAL}) + endif() + +endfunction(set_policy) + + +# Define function "source_group_by_path with three mandatory arguments (PARENT_PATH, REGEX, GROUP, ...) +# to group source files in folders (e.g. for MSVC solutions). +# +# Example: +# source_group_by_path("${CMAKE_CURRENT_SOURCE_DIR}/src" "\\\\.h$|\\\\.hpp$|\\\\.cpp$|\\\\.c$|\\\\.ui$|\\\\.qrc$" "Source Files" ${sources}) +function(source_group_by_path PARENT_PATH REGEX GROUP) + + foreach (FILENAME ${ARGN}) + + get_filename_component(FILEPATH "${FILENAME}" REALPATH) + file(RELATIVE_PATH FILEPATH ${PARENT_PATH} ${FILEPATH}) + get_filename_component(FILEPATH "${FILEPATH}" DIRECTORY) + + string(REPLACE "/" "\\" FILEPATH "${FILEPATH}") + + source_group("${GROUP}\\${FILEPATH}" REGULAR_EXPRESSION "${REGEX}" FILES ${FILENAME}) + + endforeach() + +endfunction(source_group_by_path) + + +# Function that extract entries matching a given regex from a list. +# ${OUTPUT} will store the list of matching filenames. +function(list_extract OUTPUT REGEX) + + foreach(FILENAME ${ARGN}) + if(${FILENAME} MATCHES "${REGEX}") + list(APPEND ${OUTPUT} ${FILENAME}) + endif() + endforeach() + + set(${OUTPUT} ${${OUTPUT}} PARENT_SCOPE) + +endfunction(list_extract) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..72426cb --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,125 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision(; ; [; ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe(; [ [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}") + + if(NOT EXISTS "${GIT_PARENT_DIR}/.git") + # .git dir not found + set(${_refspecvar} "000000000000" PARENT_SCOPE) + set(${_hashvar} "000000000000" PARENT_SCOPE) + return() + endif() + + if (IS_DIRECTORY "${GIT_PARENT_DIR}/.git") + # common case + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + else() + # submodule case + file(STRINGS "${GIT_PARENT_DIR}/.git" contents LIMIT_COUNT 1) + message(STATUS "${contents}") + string(SUBSTRING "${contents}" 8 -1 SUBMODULE_GIT_DIR) + message(STATUS "${SUBMODULE_GIT_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/${SUBMODULE_GIT_DIR}") + message(STATUS "${GIT_DIR}") + endif() + + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..f52760a --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,38 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") + configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + set(HEAD_HASH "${HEAD_REF}") + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() \ No newline at end of file diff --git a/cmake/RuntimeDependencies.cmake b/cmake/RuntimeDependencies.cmake new file mode 100644 index 0000000..7568b27 --- /dev/null +++ b/cmake/RuntimeDependencies.cmake @@ -0,0 +1,19 @@ + +# +# Default dependencies for the runtime-package +# + +# Install 3rd-party runtime dependencies into runtime-component +# install(FILES ... COMPONENT runtime) + + +# +# Full dependencies for self-contained packages +# + +if(OPTION_SELF_CONTAINED) + + # Install 3rd-party runtime dependencies into runtime-component + # install(FILES ... COMPONENT runtime) + +endif() diff --git a/configure b/configure new file mode 100755 index 0000000..515e256 --- /dev/null +++ b/configure @@ -0,0 +1,126 @@ +#!/bin/bash + +# Default options +BUILD_DIR="build" +CMAKE_GENERATOR="Unix Makefiles" +BUILD_TYPE="Release" +CMAKE_OPTIONS="" + +# Create default configs +if [ ! -d "./.localconfig" ] +then + mkdir ".localconfig" + + touch ".localconfig/default" + echo "#!/bin/bash" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Default configuration for configure (is always sourced)" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# CMake generator" >> ".localconfig/default" + echo "CMAKE_GENERATOR=\"Unix Makefiles\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Build directory and build type" >> ".localconfig/default" + echo "BUILD_DIR=\"build\"" >> ".localconfig/default" + echo "BUILD_TYPE=\"Release\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Installation directory" >> ".localconfig/default" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=../install\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Build static libraries" >> ".localconfig/default" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DBUILD_SHARED_LIBS:BOOL=OFF\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Enable examples" >> ".localconfig/default" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_EXAMPLES:BOOL=ON\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Enable documentation" >> ".localconfig/default" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_DOCS:BOOL=ON\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Disable tests" >> ".localconfig/default" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_TESTS:BOOL=OFF\"" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# CMake and environment variables (e.g., search paths for external libraries)" >> ".localconfig/default" + echo "" >> ".localconfig/default" + echo "# Qt" >> ".localconfig/default" + echo "#export CMAKE_PREFIX_PATH=\"\${CMAKE_PREFIX_PATH}:/opt/Qt5.2.1/5.2.1/gcc_64/\"" >> ".localconfig/default" + + touch ".localconfig/debug" + echo "#!/bin/bash" >> ".localconfig/debug" + echo "" >> ".localconfig/debug" + echo "# Configuration for debug builds" >> ".localconfig/debug" + echo "" >> ".localconfig/debug" + echo "# Build directory and build type" >> ".localconfig/debug" + echo "BUILD_DIR=\"\${BUILD_DIR}-debug\"" >> ".localconfig/debug" + echo "BUILD_TYPE=\"Debug\"" >> ".localconfig/debug" + + touch ".localconfig/pack" + echo "#!/bin/bash" >> ".localconfig/pack" + echo "" >> ".localconfig/pack" + echo "# Configuration for creating packages" >> ".localconfig/pack" + echo "" >> ".localconfig/pack" + echo "# Installation directory" >> ".localconfig/pack" + echo "CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=/usr\"" >> ".localconfig/pack" + echo "" >> ".localconfig/pack" + echo "# Enable self-contained installation" >> ".localconfig/pack" + echo "#CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_SELF_CONTAINED:BOOL=ON\"" >> ".localconfig/pack" + echo "" >> ".localconfig/pack" + echo "# Enable all components for the package" >> ".localconfig/pack" + echo "CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_EXAMPLES:BOOL=ON\"" >> ".localconfig/pack" + echo "CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_DOCS:BOOL=ON\"" >> ".localconfig/pack" + echo "CMAKE_OPTIONS=\"\${CMAKE_OPTIONS} -DOPTION_BUILD_TESTS:BOOL=OFF\"" >> ".localconfig/pack" + + echo "Default configuration has been written to .localconfig" + echo "Please review and adjust the configuration, then run again" + echo "" + echo " ./configure $@" + + exit +fi + +# Read local default options +if [ -f "./.localconfig/default" ] +then + . ./.localconfig/default +fi + +# Parse command line arguments +for ARG in "$@" +do + # Read in configuration for that command-line argument + CONFIGFILE="./.localconfig/$ARG" + if [ -f "./.localconfig/$ARG" ] + then + . "./.localconfig/$ARG" + elif [ -f "$HOME/.localconfig/$ARG" ] + then + . "$HOME/.localconfig/$ARG" + else + echo "Configuration \"$ARG\" not found (searched in ./.localconfig and ~/.localconfig)" + fi +done + +# Configure build +echo "Configuring ..." +echo "" + +# Create build directory +if [ ! -d "./$BUILD_DIR" ] +then + mkdir $BUILD_DIR +fi + +# Configure project +cd $BUILD_DIR +cmake -G "$CMAKE_GENERATOR" "-DCMAKE_BUILD_TYPE=$BUILD_TYPE" $CMAKE_OPTIONS .. +if [ $? == 0 ] +then + echo "" + echo "Project configured. To build the project, use"; + echo "" + echo " cmake --build $BUILD_DIR" +else + echo "" + echo "Configuration failed."; +fi + +cd .. diff --git a/data/unifiedmemory/shaders/average.frag b/data/unifiedmemory/shaders/average.frag new file mode 100644 index 0000000..598df2e --- /dev/null +++ b/data/unifiedmemory/shaders/average.frag @@ -0,0 +1,10 @@ +#version 450 + +uniform vec3 u_color; + +layout (location=0) out vec3 out_color; + +void main() +{ + out_color = mix(u_color, vec3(1.0, 1.0, 1.0), 0.7); +} diff --git a/data/unifiedmemory/shaders/average.geom b/data/unifiedmemory/shaders/average.geom new file mode 100644 index 0000000..22c2d05 --- /dev/null +++ b/data/unifiedmemory/shaders/average.geom @@ -0,0 +1,19 @@ +#version 450 + +layout (points) in; +layout (line_strip, max_vertices = 2) out; + +void main() +{ + vec4 origin = gl_in[0].gl_Position; + + gl_Position = vec4(-1.0, origin.yzw); + + EmitVertex(); + + gl_Position = vec4(1.0, origin.yzw); + + EmitVertex(); + + EndPrimitive(); +} diff --git a/data/unifiedmemory/shaders/average.vert b/data/unifiedmemory/shaders/average.vert new file mode 100644 index 0000000..edbc04d --- /dev/null +++ b/data/unifiedmemory/shaders/average.vert @@ -0,0 +1,35 @@ +#version 450 + +layout (location=0) in float in_value; + +uniform int u_sensorCount; +uniform int u_sensor; + +layout (std430, binding = 0) buffer Minimums +{ + float minimums[]; +}; + +layout (std430, binding = 1) buffer Maximums +{ + float maximums[]; +}; + +const float plotAreaFactor = 0.95; +const float plotAreaOffset = (1.0 - plotAreaFactor) / 0.5; + +void main() +{ + float minValue = minimums[u_sensor]; + float maxValue = maximums[u_sensor]; + float top = float(u_sensor+1.0-plotAreaOffset) / float(u_sensorCount); + float bottom = float(u_sensor+plotAreaOffset) / float(u_sensorCount); + + float x = 0.0; + float y = bottom + (top - bottom) * (in_value - minValue) / (maxValue - minValue); + + gl_Position = vec4( + x * 2.0 - 1.0, + y * 2.0 - 1.0, + 0.5, 1.0); +} diff --git a/data/unifiedmemory/shaders/averagecomputation.comp b/data/unifiedmemory/shaders/averagecomputation.comp new file mode 100644 index 0000000..4dbd6f0 --- /dev/null +++ b/data/unifiedmemory/shaders/averagecomputation.comp @@ -0,0 +1,93 @@ +#version 450 + +#extension GL_ARB_compute_variable_group_size : require + +layout (local_size_variable) in; + +layout(location=0) uniform int u_count; +layout(location=1) uniform int u_stride; +layout(location=2) uniform int u_availablePoints; + +layout (std430, binding = 0) buffer SubAverages +{ + vec4 subaverages[]; +}; + +layout (std430, binding = 1) buffer SubMinimums +{ + vec4 subminimums[]; +}; + +layout (std430, binding = 2) buffer SubMaximums +{ + vec4 submaximums[]; +}; + +layout (std430, binding = 3) buffer Averages +{ + float averages[]; +}; + +layout (std430, binding = 4) buffer Minimums +{ + float minimums[]; +}; + +layout (std430, binding = 5) buffer Maximums +{ + float maximums[]; +}; + +float average(in vec4 sum) +{ + return (sum.x + sum.y + sum.z + sum.w) / u_count; +} + +float minimum(in vec4 subminimum) +{ + switch (u_availablePoints) + { + case 1: + return subminimum.x; + case 2: + return min(subminimum.x, subminimum.y); + case 3: + return min(min(subminimum.x, subminimum.y), subminimum.z); + case 4: + return min(min(subminimum.x, subminimum.y), min(subminimum.z, subminimum.w)); + } +} + +float maximum(in vec4 submaximum) +{ + /*switch (u_availablePoints) + { + case 1: + return submaximum.x; + case 2: + return max(submaximum.x, submaximum.y); + case 3: + return max(max(submaximum.x, submaximum.y), submaximum.z); + case 4: + return max(max(submaximum.x, submaximum.y), max(submaximum.z, submaximum.w)); + }*/ + return max(max(submaximum.x, submaximum.y), max(submaximum.z, submaximum.w)); +} + +void main() +{ + uint sensorIndex = gl_GlobalInvocationID.x; + + if (sensorIndex >= u_stride) + { + return; + } + + vec4 subaverage = subaverages[sensorIndex]; + vec4 subminimum = subminimums[sensorIndex]; + vec4 submaximum = submaximums[sensorIndex]; + + averages[sensorIndex] = average(subaverage); + minimums[sensorIndex] = minimum(subminimum); + maximums[sensorIndex] = maximum(submaximum); +} diff --git a/data/unifiedmemory/shaders/linedatatransfer.comp b/data/unifiedmemory/shaders/linedatatransfer.comp new file mode 100644 index 0000000..5d6e562 --- /dev/null +++ b/data/unifiedmemory/shaders/linedatatransfer.comp @@ -0,0 +1,32 @@ +#version 450 + +#extension GL_ARB_compute_variable_group_size : require + +layout (local_size_variable) in; + +layout(location=0) uniform int u_count; +layout(location=1) uniform int u_stride; + +layout (std430, binding = 0) buffer Values +{ + float values[]; +}; + +layout (std430, binding = 1) buffer Buffer +{ + float data[]; +}; + +void main() +{ + uint sensorIndex = gl_GlobalInvocationID.x; + + if (sensorIndex >= data.length()) + { + return; + } + + float value = data[sensorIndex]; + + values[u_stride * sensorIndex + u_count] = value; +} diff --git a/data/unifiedmemory/shaders/plot.frag b/data/unifiedmemory/shaders/plot.frag new file mode 100644 index 0000000..7babcdc --- /dev/null +++ b/data/unifiedmemory/shaders/plot.frag @@ -0,0 +1,10 @@ +#version 450 + +uniform vec3 u_color; + +layout (location=0) out vec3 out_color; + +void main() +{ + out_color = u_color; +} diff --git a/data/unifiedmemory/shaders/plot.vert b/data/unifiedmemory/shaders/plot.vert new file mode 100644 index 0000000..f1c7890 --- /dev/null +++ b/data/unifiedmemory/shaders/plot.vert @@ -0,0 +1,37 @@ +#version 450 + +layout (location=0) in float in_value; + +uniform int u_vertexOffset; +uniform int u_count; +uniform int u_sensorCount; +uniform int u_sensor; + +layout (std430, binding = 0) buffer Minimums +{ + float minimums[]; +}; + +layout (std430, binding = 1) buffer Maximums +{ + float maximums[]; +}; + +const float plotAreaFactor = 0.95; +const float plotAreaOffset = (1.0 - plotAreaFactor) / 0.5; + +void main() +{ + float minValue = minimums[u_sensor]; + float maxValue = maximums[u_sensor]; + float top = float(u_sensor+1.0-plotAreaOffset) / float(u_sensorCount); + float bottom = float(u_sensor+plotAreaOffset) / float(u_sensorCount); + + float x = float((gl_VertexID + u_vertexOffset) % u_count) / float(u_count-1); + float y = bottom + (top - bottom) * (in_value - minValue) / (maxValue - minValue); + + gl_Position = vec4( + x * 2.0 - 1.0, + y * 2.0 - 1.0, + 0.5, 1.0); +} diff --git a/data/unifiedmemory/shaders/sumcomputation.comp b/data/unifiedmemory/shaders/sumcomputation.comp new file mode 100644 index 0000000..4e5bcf9 --- /dev/null +++ b/data/unifiedmemory/shaders/sumcomputation.comp @@ -0,0 +1,93 @@ +#version 450 + +#extension GL_ARB_compute_variable_group_size : require + +layout (local_size_variable) in; + +layout(location=0) uniform int u_count; +layout(location=1) uniform int u_stride; +layout(location=2) uniform int u_availablePoints; + +layout (std430, binding = 0) buffer Averages +{ + vec4 averages[]; +}; + +layout (std430, binding = 1) buffer Minimums +{ + vec4 minimums[]; +}; + +layout (std430, binding = 2) buffer Maximums +{ + vec4 maximums[]; +}; + +layout (std430, binding = 3) buffer SubAverages +{ + vec4 subaverages[]; +}; + +layout (std430, binding = 4) buffer SubMinimums +{ + vec4 subminimums[]; +}; + +layout (std430, binding = 5) buffer SubMaximums +{ + vec4 submaximums[]; +}; + +vec4 sum(in vec4 avg1, in vec4 avg2, in uint min2Index) +{ + return avg1 + avg2; +} + +vec4 minimum(in vec4 min1, in vec4 min2, in uint min2Index) +{ + return vec4( + min(min1.x, mix(min1.x, min2.x, float(min2Index+0 < u_availablePoints))), + min(min1.y, mix(min1.x, min2.y, float(min2Index+1 < u_availablePoints))), + min(min1.z, mix(min1.x, min2.z, float(min2Index+2 < u_availablePoints))), + min(min1.w, mix(min1.x, min2.w, float(min2Index+3 < u_availablePoints))) + ); +} + +vec4 maximum(in vec4 max1, in vec4 max2, in uint max2Index) +{ + /*return vec4( + max(max1.x, mix(max1.x, max2.x, float(max2Index+0 < u_availablePoints))), + max(max1.y, mix(max1.y, max2.y, float(max2Index+1 < u_availablePoints))), + max(max1.z, mix(max1.z, max2.z, float(max2Index+2 < u_availablePoints))), + max(max1.w, mix(max1.w, max2.w, float(max2Index+3 < u_availablePoints))) + );*/ + return max(max1, max2); +} + +void main() +{ + uint dataIndex = gl_GlobalInvocationID.x; + uint sensorIndex = gl_GlobalInvocationID.y; + uint stride = uint(u_count); + uint doubleStride = uint(2 * stride); + + if (dataIndex >= u_count || sensorIndex >= u_stride) + { + return; + } + + uint firstReadIndex = doubleStride * sensorIndex + dataIndex; + uint secondReadIndex = doubleStride * sensorIndex + (stride + dataIndex); + uint writeIndex = stride * sensorIndex + dataIndex; + + vec4 avg1 = averages[firstReadIndex]; + vec4 avg2 = averages[secondReadIndex]; + vec4 min1 = minimums[firstReadIndex]; + vec4 min2 = minimums[secondReadIndex]; + vec4 max1 = maximums[firstReadIndex]; + vec4 max2 = maximums[secondReadIndex]; + + subaverages[writeIndex] = sum(avg1, avg2, 4 * (stride + dataIndex)); + subminimums[writeIndex] = minimum(min1, min2, 4 * (stride + dataIndex)); + submaximums[writeIndex] = maximum(max1, max2, 4 * (stride + dataIndex)); +} diff --git a/deploy/CMakeLists.txt b/deploy/CMakeLists.txt new file mode 100644 index 0000000..6c74a55 --- /dev/null +++ b/deploy/CMakeLists.txt @@ -0,0 +1,30 @@ + +# +# Target 'pack' +# + +add_custom_target(pack) +set_target_properties(pack PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1) + + +# Install additional runtime dependencies +include(${PROJECT_SOURCE_DIR}/cmake/RuntimeDependencies.cmake) + + +# +# Packages +# + +include(packages/pack-unifiedmemory.cmake) + + +# +# Target 'component_install' +# + +add_custom_target( + component_install + COMMAND make preinstall + COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/ComponentInstall.cmake + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} +) diff --git a/deploy/packages/pack-unifiedmemory.cmake b/deploy/packages/pack-unifiedmemory.cmake new file mode 100644 index 0000000..f98332c --- /dev/null +++ b/deploy/packages/pack-unifiedmemory.cmake @@ -0,0 +1,259 @@ + +# +# Check if cpack is available +# + +if(NOT EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + return() +endif() + + +# +# Output packages +# + +if("${CMAKE_SYSTEM_NAME}" MATCHES "Windows") + # Windows installer + set(OPTION_PACK_GENERATOR "NSIS;ZIP" CACHE STRING "Package targets") + set(PACK_COMPONENT_INSTALL ON) + set(PACK_INCLUDE_TOPDIR OFF) +elseif(UNIX AND SYSTEM_DIR_INSTALL) + # System installation packages for unix systems + if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + set(OPTION_PACK_GENERATOR "TGZ;DEB;RPM" CACHE STRING "Package targets") + set(PACK_COMPONENT_INSTALL ON) + set(PACK_INCLUDE_TOPDIR OFF) + else() + set(OPTION_PACK_GENERATOR "TGZ" CACHE STRING "Package targets") + set(PACK_COMPONENT_INSTALL OFF) + set(PACK_INCLUDE_TOPDIR OFF) + endif() +#elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + # MacOS X disk image + # At the moment, DMG generator and CPACK_INCLUDE_TOPLEVEL_DIRECTORY=ON do not work together. + # Therefore, we disable dmg images for MacOS until we've found a solution +# set(OPTION_PACK_GENERATOR "DragNDrop" CACHE STRING "Package targets") +# set(PACK_COMPONENT_INSTALL OFF) +# set(PACK_INCLUDE_TOPDIR ON) +else() + # Default (portable package for any platform) + set(OPTION_PACK_GENERATOR "ZIP;TGZ" CACHE STRING "Package targets") + set(PACK_COMPONENT_INSTALL OFF) + set(PACK_INCLUDE_TOPDIR ON) +endif() + + +# +# Package components +# + +set(CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${META_PROJECT_NAME} library") +set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "Runtime components for ${META_PROJECT_NAME} library") + +set(CPACK_COMPONENT_DEV_DISPLAY_NAME "C/C++ development files") +set(CPACK_COMPONENT_DEV_DESCRIPTION "Development files for ${META_PROJECT_NAME} library") +set(CPACK_COMPONENT_DEV_DEPENDS runtime) + +set(CPACK_COMPONENTS_ALL runtime dev) + +if (OPTION_BUILD_EXAMPLES) + set(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Example applications") + set(CPACK_COMPONENT_EXAMPLES_DESCRIPTION "Example applications for ${META_PROJECT_NAME} library") + set(CPACK_COMPONENT_EXAMPLES_DEPENDS runtime) + + set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} examples) +endif() + +if (OPTION_BUILD_DOCS) + set(CPACK_COMPONENT_DOCS_DISPLAY_NAME "Documentation") + set(CPACK_COMPONENT_DOCS_DESCRIPTION "Documentation of ${META_PROJECT_NAME} library") + + set(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} docs) +endif() + + +# +# Initialize CPack +# + +# Reset CPack configuration +if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + set(CPACK_IGNORE_FILES "") + set(CPACK_INSTALLED_DIRECTORIES "") + set(CPACK_SOURCE_IGNORE_FILES "") + set(CPACK_SOURCE_INSTALLED_DIRECTORIES "") + set(CPACK_STRIP_FILES "") + set(CPACK_SOURCE_TOPLEVEL_TAG "") + set(CPACK_SOURCE_PACKAGE_FILE_NAME "") + set(CPACK_PACKAGE_RELOCATABLE OFF) + set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY ${PACK_INCLUDE_TOPDIR}) + set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY ${PACK_INCLUDE_TOPDIR}) +endif() + +# Find cpack executable +get_filename_component(CPACK_PATH ${CMAKE_COMMAND} PATH) +set(CPACK_COMMAND "${CPACK_PATH}/cpack") + +# Set install prefix +if(SYSTEM_DIR_INSTALL) + set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") +else() + set(CPACK_PACKAGING_INSTALL_PREFIX "") +endif() + +# Package project +set(project_name ${META_PROJECT_NAME}) # Name of package project +set(project_root ${META_PROJECT_NAME}) # Name of root project that is to be installed + +# Package information +string(TOLOWER ${META_PROJECT_NAME} package_name) +set(package_description ${META_PROJECT_DESCRIPTION}) +set(package_vendor ${META_AUTHOR_ORGANIZATION}) +set(package_maintainer ${META_AUTHOR_MAINTAINER}) + +# Package specific options +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/deploy/packages/${project_name}) + + +# +# Package information +# + +set(CPACK_PACKAGE_NAME "${package_name}") +set(CPACK_PACKAGE_VENDOR "${package_vendor}") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${package_description}") +set(CPACK_PACKAGE_VERSION "${META_VERSION}") +set(CPACK_PACKAGE_VERSION_MAJOR "${META_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${META_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${META_VERSION_PATCH}") +set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md") +set(CPACK_RESOURCE_FILE_WELCOME "${PROJECT_SOURCE_DIR}/README.md") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md") +set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/deploy/images/logo.bmp") +set(CPACK_PACKAGE_FILE_NAME "${package_name}-${CPACK_PACKAGE_VERSION}") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${package_name}") +set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${package_name}") + + +# +# NSIS package +# + +# Fix icon path +if("${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND CPACK_PACKAGE_ICON) + # NOTE: for using MUI (UN)WELCOME images we suggest to replace nsis defaults, + # since there is currently no way to do so without manipulating the installer template (which we won't). + # http://public.kitware.com/pipermail/cmake-developers/2013-January/006243.html + + # SO the following only works for the installer icon, not for the welcome image. + + # NSIS requires "\\" - escaped backslash to work properly. We probably won't rely on this feature, + # so just replacing / with \\ manually. + + #file(TO_NATIVE_PATH "${CPACK_PACKAGE_ICON}" CPACK_PACKAGE_ICON) + string(REGEX REPLACE "/" "\\\\\\\\" CPACK_PACKAGE_ICON "${CPACK_PACKAGE_ICON}") +endif() + +# Fix installation path for x64 builds +if(X64) + # http://public.kitware.com/Bug/view.php?id=9094 + set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") +endif() + +# Package options +#set(CPACK_NSIS_DISPLAY_NAME "${package_name}-${META_VERSION}") +set(CPACK_NSIS_MUI_ICON "${PROJECT_SOURCE_DIR}/deploy/images/logo.ico") +set(CPACK_NSIS_MUI_UNIICON "${PROJECT_SOURCE_DIR}/deploy/images/logo.ico") + +# Optional Preliminaries (i.e., silent Visual Studio Redistributable install) +if(NOT INSTALL_MSVC_REDIST_FILEPATH) + set(INSTALL_MSVC_REDIST_FILEPATH "" CACHE FILEPATH "Visual C++ Redistributable Installer (note: manual match the selected generator)" FORCE) +endif() + +if(EXISTS ${INSTALL_MSVC_REDIST_FILEPATH}) + get_filename_component(MSVC_REDIST_NAME ${INSTALL_MSVC_REDIST_FILEPATH} NAME) + string(REGEX REPLACE "/" "\\\\\\\\" INSTALL_MSVC_REDIST_FILEPATH ${INSTALL_MSVC_REDIST_FILEPATH}) + list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS " + SetOutPath \\\"$TEMP\\\" + File \\\"${INSTALL_MSVC_REDIST_FILEPATH}\\\" + ExecWait '\\\"$TEMP\\\\${MSVC_REDIST_NAME} /quiet\\\"' + Delete \\\"$TEMP\\\\${MSVC_REDIST_NAME}\\\" + ") +endif() + + +# +# Debian package +# + +set(CPACK_DEBIAN_PACKAGE_NAME "${package_name}") +set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all") +#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.1-6), libgcc1 (>= 1:3.4.2-12)") +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${package_maintainer}") +set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") +set(CPACK_DEBIAN_PACKAGE_SECTION "devel") +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +#set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "") +#set(CPACK_DEBIAN_PACKAGE_SUGGESTS "") +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "") +set(CPACK_DEB_COMPONENT_INSTALL ${PACK_COMPONENT_INSTALL}) + + +# +# RPM package +# + +set(CPACK_RPM_PACKAGE_NAME "${package_name}") +set(CPACK_RPM_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") +set(CPACK_RPM_PACKAGE_RELEASE 1) +set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") +set(CPACK_RPM_PACKAGE_REQUIRES "") +set(CPACK_RPM_PACKAGE_PROVIDES "") +set(CPACK_RPM_PACKAGE_VENDOR "${package_vendor}") +set(CPACK_RPM_PACKAGE_LICENSE "MIT") +set(CPACK_RPM_PACKAGE_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") +set(CPACK_RPM_PACKAGE_DESCRIPTION "") +set(CPACK_RPM_PACKAGE_GROUP "unknown") +#set(CPACK_RPM_SPEC_INSTALL_POST "") +#set(CPACK_RPM_SPEC_MORE_DEFINE "") +#set(CPACK_RPM_USER_BINARY_SPECFILE "") +#set(CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE "") +#set(CPACK_RPM__INSTALL_SCRIPT_FILE "") +#set(CPACK_RPM_PACKAGE_DEBUG 1) +set(CPACK_RPM_PACKAGE_RELOCATABLE OFF) +set(CPACK_RPM_COMPONENT_INSTALL ${PACK_COMPONENT_INSTALL}) + + +# +# Archives (zip, tgz, ...) +# + +set(CPACK_ARCHIVE_COMPONENT_INSTALL ${PACK_COMPONENT_INSTALL}) + + +# +# Execute CPack +# + +set(CPACK_OUTPUT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CPackConfig-${project_name}.cmake") +set(CPACK_GENERATOR "${OPTION_PACK_GENERATOR}") +set(CPack_CMake_INCLUDED FALSE) +include(CPack) + + +# +# Package target +# + +# Create target +add_custom_target( + pack-${project_name} + COMMAND ${CPACK_COMMAND} --config ${PROJECT_BINARY_DIR}/CPackConfig-${project_name}.cmake + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} +) +set_target_properties(pack-${project_name} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1) + +# Set dependencies +add_dependencies(pack pack-${project_name}) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000..d7de373 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,17 @@ + +# +# Target 'docs' +# + +if(NOT OPTION_BUILD_DOCS) + return() +endif() + +add_custom_target(docs) + + +# +# Documentation +# + +add_subdirectory(manual) diff --git a/docs/manual/.gitignore b/docs/manual/.gitignore new file mode 100644 index 0000000..f2e31fe --- /dev/null +++ b/docs/manual/.gitignore @@ -0,0 +1,5 @@ +*.aux +*.log +*.out +*.pdf +*.synctex.gz diff --git a/docs/manual/CMakeLists.txt b/docs/manual/CMakeLists.txt new file mode 100644 index 0000000..c66c7ca --- /dev/null +++ b/docs/manual/CMakeLists.txt @@ -0,0 +1,57 @@ + +# +# Find LaTeX +# + +find_package(LATEX) +if(NOT LATEX_FOUND) + message(STATUS "Disabled generation of documentation (missing LaTeX).") + return() +endif() + + +# +# Target name +# + +set(target docs-manual) +message(STATUS "Doc ${target}") + + +# +# Input and output files +# + +set(source "${CMAKE_CURRENT_SOURCE_DIR}/unifiedmemory.tex") +set(pdf "${CMAKE_CURRENT_BINARY_DIR}/unifiedmemory.pdf") + + +# +# Create documentation +# + +# Invoke LaTeX +add_custom_command( + OUTPUT ${pdf} + DEPENDS ${source} + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND ${PDFLATEX_COMPILER} \"${source}\" + COMMAND ${PDFLATEX_COMPILER} \"${source}\" + COMMAND ${PDFLATEX_COMPILER} \"${source}\" + COMMENT "Creating LaTeX documentation." +) + +# Declare target +add_custom_target(${target} ALL DEPENDS ${pdf}) +add_dependencies(docs ${target}) + + +# +# Deployment +# + +# PDF file +install(FILES ${pdf} + DESTINATION "${INSTALL_DOC}" + COMPONENT docs +) diff --git a/docs/manual/unifiedmemory.tex b/docs/manual/unifiedmemory.tex new file mode 100644 index 0000000..0b93c21 --- /dev/null +++ b/docs/manual/unifiedmemory.tex @@ -0,0 +1,34 @@ + +\documentclass{article} + +\usepackage[pdfborder={0 0 0}]{hyperref} +\usepackage{tikz} + +\begin{document} + +\title{Unified Memory Heterogenous Computing Showcase} + +\maketitle + +\section{Frame Execution Overview} + +{ + \scriptsize + \begin{tikzpicture}[scale=1.12] + \node[text width=1.3cm] at (-0.5, 1.25) {GPU}; + \node[text width=1.3cm] at (-0.5, 2.25) {CPU Main Thread}; + \node[text width=1.3cm] at (-0.5, 3.25) {Disk Thread}; + \draw[black] (0, 1) rectangle node[text width=1.3cm] {Load image} (1, 2.5); + \draw[black] (1, 2) rectangle node[text width=1.3cm] {Store image} (2, 3.5); + \draw[black] (2, 2) rectangle node[text width=1.3cm] {Handle window events} (3, 2.5); + \draw[black] (3, 2) rectangle node[text width=1.3cm] {Process sensor changes} (4, 2.5); + \draw[black] (4, 1) rectangle node[text width=1.3cm] {Update sensor data} (5, 2.5); + \draw[black] (5, 1) rectangle node[text width=1.3cm] {Compute average} (6, 1.5); + \draw[black] (6, 1) rectangle node[text width=1.3cm] {Transfer Average} (7, 2.5); + \draw[black] (7, 2) rectangle node[text width=1.3cm] {Write average to log} (8, 2.5); + \draw[black] (8, 1) rectangle node[text width=1.3cm] {Render image} (9, 1.5); + \draw[black] (9, 2) rectangle node[text width=1.3cm] {Swap buffers} (10, 2.5); + \end{tikzpicture} +} + +\end{document} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 0000000..9bbc908 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,28 @@ + +# +# Configuration for all sub-projects +# + +# Generate version-header +string(TOUPPER ${META_PROJECT_NAME} META_PROJECT_NAME_UPPER) +configure_file(version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/${META_PROJECT_NAME}/${META_PROJECT_NAME}-version.h) + + +# +# Sub-projects +# + +# Libraries +set(IDE_FOLDER "") + +# Examples +set(IDE_FOLDER "Examples") +add_subdirectory(sensor) +add_subdirectory(unifiedmemory) + +# +# Deployment +# + +# Deploy generated headers +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/${META_PROJECT_NAME} DESTINATION include COMPONENT dev) diff --git a/source/sensor/CMakeLists.txt b/source/sensor/CMakeLists.txt new file mode 100644 index 0000000..62283f3 --- /dev/null +++ b/source/sensor/CMakeLists.txt @@ -0,0 +1,113 @@ + +# +# External dependencies +# + +#find_package(OpenMP QUIET) + + +# +# Executable name and options +# + +# Target name +set(target sensor) + +# Exit here if required dependencies are not met +message(STATUS "Example ${target}") + + +# +# Sources +# + +set(sources + main.cpp +) + + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + + $ +) + + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} +) + + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + + +# +# Deployment +# + +# Executable +install(TARGETS ${target} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT examples + BUNDLE DESTINATION ${INSTALL_BIN} COMPONENT examples +) diff --git a/source/sensor/main.cpp b/source/sensor/main.cpp new file mode 100644 index 0000000..7135cda --- /dev/null +++ b/source/sensor/main.cpp @@ -0,0 +1,104 @@ + +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace +{ + + +volatile auto sensorEmitting = true; + + +void sigint(int signo) +{ + if (signo != SIGINT) + { + return; + } + + sensorEmitting = false; +} + + +template +void pairwise_for_each(iterator b1, iterator e1, iterator b2, iterator e2, callback c) +{ + for (auto it1 = b1, it2 = b2; it1 != e1 && it2 != e2; ++it1, ++it2) + { + c(*it1, *it2); + } +} + +} // namespace + + +int main(int argc, char * argv[]) +{ + using namespace std::chrono_literals; + + std::ios_base::sync_with_stdio(false); + std::cin.tie(nullptr); + + const auto numSensors = argc > 1 ? std::atoi(argv[1]) : 1; + const auto sleepTime = argc > 2 ? std::chrono::microseconds(std::atoi(argv[2])) : 10000us; + + if (signal(SIGINT, sigint) == SIG_ERR) + { + std::cerr << "Couldn't register SIGINT handler" << std::endl; + } + + std::mt19937_64 generator; + std::normal_distribution distribution(100.0,5.0); + + const auto startTimestamp = std::chrono::high_resolution_clock::now(); + + auto currentValues = std::vector(numSensors); + auto nextValues = std::vector(numSensors); + + pairwise_for_each(currentValues.begin(), currentValues.end(), nextValues.begin(), nextValues.end(), [&](double & currentValue, double & nextValue) { + currentValue = distribution(generator); + nextValue = distribution(generator); + }); + + auto t = 0.0; + while (sensorEmitting) + { + const auto currentTimestamp = std::chrono::high_resolution_clock::now(); + const auto duration = std::chrono::duration_cast(currentTimestamp - startTimestamp).count(); + + std::cout << duration; + + pairwise_for_each(currentValues.begin(), currentValues.end(), nextValues.begin(), nextValues.end(), [&](double & currentValue, double & nextValue) { + const auto value = t * nextValue + (1.0 - t) * currentValue; + + std::cout << ';' << value; + }); + + std::cout << '\n'; + + t += 0.1; + + if (t > 1.0) + { + t -= 1.0; + + pairwise_for_each(currentValues.begin(), currentValues.end(), nextValues.begin(), nextValues.end(), [&](double & currentValue, double & nextValue) { + currentValue = std::exchange(nextValue, distribution(generator)); + }); + } + + std::cout.flush(); + + std::this_thread::sleep_for(sleepTime); + } + + return 0; +} diff --git a/source/unifiedmemory/CMakeLists.txt b/source/unifiedmemory/CMakeLists.txt new file mode 100644 index 0000000..2bb4e0a --- /dev/null +++ b/source/unifiedmemory/CMakeLists.txt @@ -0,0 +1,144 @@ + +# +# External dependencies +# + +find_package(glfw3 REQUIRED) +find_package(glm REQUIRED) +find_package(glbinding REQUIRED) +find_package(cpplocate REQUIRED) + +find_package(OpenMP QUIET) + + +# +# Executable name and options +# + +# Target name +set(target unifiedmemory) + +# Exit here if required dependencies are not met +message(STATUS "Example ${target}") + +if (NOT OPENMP_FOUND) + message("Loop parallelization in ${target} skipped: OpenMP not found") +endif() + + +# +# Sources +# + +set(sources + main.cpp + + common.h + common.cpp + + unified_allocator.h + unified_allocator.inl + unified_vector.h + unified_vector.inl + + Computation.h + Computation.cpp + Rendering.h + Rendering.cpp +) + + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + + $ +) + + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + glfw + glm + glbinding::glbinding + cpplocate::cpplocate +) + + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} + $<$:USE_OPENMP> + GLFW_INCLUDE_NONE +) + + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} + $<$:${OpenMP_CXX_FLAGS}> +) + + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + $<$>:$<$:${OpenMP_CXX_FLAGS}>> + ${DEFAULT_LINKER_OPTIONS} +) + + +# +# Deployment +# + +# Executable +install(TARGETS ${target} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT examples + BUNDLE DESTINATION ${INSTALL_BIN} COMPONENT examples +) diff --git a/source/unifiedmemory/Computation.cpp b/source/unifiedmemory/Computation.cpp new file mode 100644 index 0000000..7149938 --- /dev/null +++ b/source/unifiedmemory/Computation.cpp @@ -0,0 +1,311 @@ + +#include "Computation.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" + + +namespace +{ + + +const auto summationGroupSize = 16; +const auto averagingGroupSize = 16; +const auto transferGroupSize = 16; +const auto numSimdComponents = 4; + + +} + + +Computation::Computation() +: m_headerRead(false) +, m_numSensors(0) +, m_currentTime(0) +, m_stream(nullptr) +, m_dataBuffer(nullptr) +, m_averageBuffer(nullptr) +, m_minBuffer(nullptr) +, m_maxBuffer(nullptr) +, m_numIterations(0) +, m_sumProgram(0) +, m_sumShader(0) +, m_averageProgram(0) +, m_averageShader(0) +, m_strideLocation(0) +, m_countLocation(0) + +, m_currentIndex(0) +, m_availablePoints(0) +, m_currentPingPongBuffer(0) +{ +} + +std::uint32_t Computation::numSensors() const +{ + return m_numSensors; +} + +bool Computation::dataValid() const +{ + return m_headerRead && m_numSensors > 0; +} + +std::uint64_t Computation::currentTime() const +{ + return m_currentTime; +} + +std::uint32_t Computation::availablePoints() const +{ + return m_availablePoints; +} + +std::uint32_t Computation::currentIndex() const +{ + return m_currentIndex; +} + +void Computation::initialize(std::istream * stream, unshared_gpu_vector * dataBuffer, unified_read_vector * averageBuffer, unshared_gpu_vector * minBuffer, unshared_gpu_vector * maxBuffer, std::size_t numIterations) +{ + m_stream = stream; + m_dataBuffer = dataBuffer; + m_averageBuffer = averageBuffer; + m_minBuffer = minBuffer; + m_maxBuffer = maxBuffer; + m_numIterations = numIterations; + + m_sumShader = gl::glCreateShader(gl::GL_COMPUTE_SHADER); + m_sumProgram = gl::glCreateProgram(); + m_averageShader = gl::glCreateShader(gl::GL_COMPUTE_SHADER); + m_averageProgram = gl::glCreateProgram(); + m_transferShader = gl::glCreateShader(gl::GL_COMPUTE_SHADER); + m_transferProgram = gl::glCreateProgram(); + + gl::glAttachShader(m_sumProgram, m_sumShader); + gl::glAttachShader(m_averageProgram, m_averageShader); + gl::glAttachShader(m_transferProgram, m_transferShader); + + setShaderSourceAndCompile(m_sumShader, dataPath() + "/unifiedmemory/shaders/sumcomputation.comp"); + auto success = checkForCompilationError(m_sumShader, "sum shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_averageShader, dataPath() + "/unifiedmemory/shaders/averagecomputation.comp"); + success &= checkForCompilationError(m_averageShader, "average shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_transferShader, dataPath() + "/unifiedmemory/shaders/linedatatransfer.comp"); + success &= checkForCompilationError(m_transferShader, "transfer shader"); + + if (!success) + { + return; + } + + gl::glLinkProgram(m_sumProgram); + + success &= checkForLinkerError(m_sumProgram, "sum program"); + + if (!success) + { + return; + } + + gl::glLinkProgram(m_averageProgram); + + success &= checkForLinkerError(m_averageProgram, "average program"); + + if (!success) + { + return; + } + + gl::glLinkProgram(m_transferProgram); + + success &= checkForLinkerError(m_transferProgram, "transfer program"); + + if (!success) + { + return; + } + + m_countLocation = gl::glGetUniformLocation(m_sumProgram, "u_count"); + m_strideLocation = gl::glGetUniformLocation(m_sumProgram, "u_stride"); + m_availablePointsLocation = gl::glGetUniformLocation(m_sumProgram, "u_availablePoints"); + + assert(m_countLocation == gl::glGetUniformLocation(m_averageProgram, "u_count")); + assert(m_strideLocation == gl::glGetUniformLocation(m_averageProgram, "u_stride")); + assert(m_availablePointsLocation == gl::glGetUniformLocation(m_averageProgram, "u_availablePoints")); + + assert(m_countLocation == gl::glGetUniformLocation(m_transferProgram, "u_count")); + assert(m_strideLocation == gl::glGetUniformLocation(m_transferProgram, "u_stride")); +} + +void Computation::uninitialize() +{ + gl::glDeleteShader(m_sumShader); + gl::glDeleteShader(m_averageShader); + gl::glDeleteShader(m_transferShader); + gl::glDeleteProgram(m_sumProgram); + gl::glDeleteProgram(m_averageProgram); + gl::glDeleteProgram(m_transferProgram); +} + +std::uint32_t Computation::processEvents() +{ + auto numEventsProcessed = std::uint32_t(0); + + std::string line; + while (*m_stream && m_stream->rdbuf()->in_avail()) + { + std::getline(*m_stream, line); + + processLine(line); + ++numEventsProcessed; + } + + return numEventsProcessed; +} + +void Computation::processLine(const std::string & line) +{ + if (!m_headerRead) + { + // First field is point in time + m_numSensors = std::count(line.begin(), line.end(), ';'); + + m_dataBuffer->resize(m_numIterations * m_numSensors); + m_averageBuffer->resize(m_numSensors); + m_minBuffer->resize(m_numSensors); + m_maxBuffer->resize(m_numSensors); + m_subaverageBuffer.resize(m_numIterations * m_numSensors / 2); + m_subminBuffer.resize(m_numIterations * m_numSensors / 2); + m_submaxBuffer.resize(m_numIterations * m_numSensors / 2); + for (auto & buffer : m_pingPongBuffer) + { + buffer.resize(m_numSensors); + } + + m_headerRead = true; + } + + auto current = const_cast(line.data()); + + m_currentTime = std::strtoll(current, ¤t, 10); + + for (auto i = std::uint32_t(0); i < m_numSensors; ++i) + { + assert(current - const_cast(line.data()) < static_cast(line.size())); + + const auto value = std::strtof(current+1, ¤t); + const auto index = i; + m_pingPongBuffer[m_currentPingPongBuffer].at(index) = value; + } + + m_pingPongBuffer[m_currentPingPongBuffer].flush(); + transferLine(); + + m_availablePoints = std::max(m_availablePoints, m_currentIndex+1); + m_currentIndex = (m_currentIndex + 1) % m_numIterations; +} + +void Computation::computeAverage() +{ + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, m_dataBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, m_dataBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 2, m_dataBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 3, m_subaverageBuffer.gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 4, m_subminBuffer.gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 5, m_submaxBuffer.gpuIdentifier()); + + gl::glUseProgram(m_sumProgram); + + gl::glUniform1i(m_strideLocation, m_numSensors); + gl::glUniform1i(m_availablePointsLocation, m_availablePoints); + + const auto numSensorGroups = float(m_numSensors) / averagingGroupSize; + auto iterations = m_numIterations / 2 / numSimdComponents; + while (iterations > 0) + { + gl::glUniform1i(m_countLocation, iterations); + + const auto numDataPointGroups = float(iterations) / summationGroupSize; + gl::glDispatchComputeGroupSizeARB(std::ceil(numDataPointGroups), std::ceil(numSensorGroups), 1, summationGroupSize, averagingGroupSize, 1); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, m_subaverageBuffer.gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, m_subminBuffer.gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 2, m_submaxBuffer.gpuIdentifier()); + + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + iterations /= 2; + } + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 3, m_averageBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 4, m_minBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 5, m_maxBuffer->gpuIdentifier()); + + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + gl::glUseProgram(m_averageProgram); + + gl::glUniform1i(m_strideLocation, m_numSensors); + gl::glUniform1i(m_countLocation, m_availablePoints); + gl::glUniform1i(m_availablePointsLocation, static_cast(std::ceil(m_availablePoints / (m_numIterations / 4.0f)))); + + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + gl::glDispatchComputeGroupSizeARB(std::ceil(numSensorGroups), 1, 1, averagingGroupSize, 1, 1); + + gl::glUseProgram(0); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, 0); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, 0); + + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + m_averageBuffer->startWait(); +} + +void Computation::transferLine() +{ + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, m_dataBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, m_pingPongBuffer[m_currentPingPongBuffer].gpuIdentifier()); + + m_currentPingPongBuffer = (m_currentPingPongBuffer + 1) % m_pingPongBuffer.size(); + + gl::glUseProgram(m_transferProgram); + + gl::glUniform1i(m_strideLocation, m_numIterations); + gl::glUniform1i(m_countLocation, m_currentIndex); + + const auto numSensorGroups = float(m_numSensors) / transferGroupSize; + gl::glDispatchComputeGroupSizeARB(std::ceil(numSensorGroups), 1, 1, transferGroupSize, 1, 1); + + gl::glUseProgram(0); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, 0); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, 0); + + gl::glMemoryBarrier(gl::GL_SHADER_STORAGE_BARRIER_BIT); +} diff --git a/source/unifiedmemory/Computation.h b/source/unifiedmemory/Computation.h new file mode 100644 index 0000000..9ab13d4 --- /dev/null +++ b/source/unifiedmemory/Computation.h @@ -0,0 +1,63 @@ + +#pragma once + + +#include +#include + +#include + +#include "unified_vector.h" + + +class Computation +{ +public: + Computation(); + + void initialize(std::istream * stream, unshared_gpu_vector * dataBuffer, unified_read_vector * averageBuffer, unshared_gpu_vector * minBuffer, unshared_gpu_vector * maxBuffer, std::size_t numIterations); + void uninitialize(); + + std::uint32_t processEvents(); + void computeAverage(); + + bool dataValid() const; + + std::uint32_t numSensors() const; + std::uint64_t currentTime() const; + std::uint32_t availablePoints() const; + std::uint32_t currentIndex() const; + +protected: + bool m_headerRead; + std::uint32_t m_numSensors; + std::uint64_t m_currentTime; + std::istream * m_stream; + unshared_gpu_vector * m_dataBuffer; + unified_read_vector * m_averageBuffer; + unshared_gpu_vector * m_minBuffer; + unshared_gpu_vector * m_maxBuffer; + std::size_t m_numIterations; + gl::GLuint m_sumProgram; + gl::GLuint m_sumShader; + gl::GLuint m_averageProgram; + gl::GLuint m_averageShader; + gl::GLuint m_transferProgram; + gl::GLuint m_transferShader; + gl::GLint m_strideLocation; + gl::GLint m_countLocation; + gl::GLint m_availablePointsLocation; + +protected: + void processLine(const std::string & line); + void transferLine(); + +private: + std::size_t m_currentIndex; + std::size_t m_availablePoints; + std::size_t m_currentPingPongBuffer; + unshared_gpu_vector m_subaverageBuffer; + unshared_gpu_vector m_subminBuffer; + unshared_gpu_vector m_submaxBuffer; + std::array, 32> m_pingPongBuffer; +}; diff --git a/source/unifiedmemory/Rendering.cpp b/source/unifiedmemory/Rendering.cpp new file mode 100644 index 0000000..fd2de32 --- /dev/null +++ b/source/unifiedmemory/Rendering.cpp @@ -0,0 +1,328 @@ + +#include "Rendering.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include "common.h" + + +namespace +{ + + +const auto numColorComponents = 3; + + +} + + +Rendering::Rendering() +: m_fbo(0) + +, m_plotVAO(0) +, m_averageVAO(0) + +, m_plotProgram(0) +, m_plotVertexShader(0) +, m_plotFragmentShader(0) + +, m_averageProgram(0) +, m_averageVertexShader(0) +, m_averageGeometryShader(0) +, m_averageFragmentShader(0) + +, m_plotVertexOffsetLocation(0) +, m_plotSensorCountLocation(0) +, m_plotCountLocation(0) +, m_plotColorLocation(0) +, m_plotCurrentSensorLocation(0) + +, m_renderingInitialized(false) + +, m_stride(0) +, m_sensorCount(0) +, m_pointCount(0) +, m_currentIndex(0) + +, m_width(0) +, m_height(0) + +, m_dataBuffer(nullptr) +, m_averageBuffer(nullptr) +, m_minBuffer(nullptr) +, m_maxBuffer(nullptr) +, m_pixelBuffer(nullptr) +{ +} + +Rendering::~Rendering() +{ +} + +void Rendering::initialize(unshared_gpu_vector * dataBuffer, unified_read_vector * averageBuffer, unshared_gpu_vector * minBuffer, unshared_gpu_vector * maxBuffer, unified_read_vector * pixelBuffer) +{ + m_dataBuffer = dataBuffer; + m_averageBuffer = averageBuffer; + m_minBuffer = minBuffer; + m_maxBuffer = maxBuffer; + m_pixelBuffer = pixelBuffer; + + gl::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + + gl::glCreateVertexArrays(1, &m_plotVAO); + gl::glCreateVertexArrays(1, &m_averageVAO); + + m_plotProgram = gl::glCreateProgram(); + m_plotVertexShader = gl::glCreateShader(gl::GL_VERTEX_SHADER); + m_plotFragmentShader = gl::glCreateShader(gl::GL_FRAGMENT_SHADER); + + m_averageProgram = gl::glCreateProgram(); + m_averageVertexShader = gl::glCreateShader(gl::GL_VERTEX_SHADER); + m_averageGeometryShader = gl::glCreateShader(gl::GL_GEOMETRY_SHADER); + m_averageFragmentShader = gl::glCreateShader(gl::GL_FRAGMENT_SHADER); + + gl::glAttachShader(m_plotProgram, m_plotVertexShader); + gl::glAttachShader(m_plotProgram, m_plotFragmentShader); + + gl::glAttachShader(m_averageProgram, m_averageVertexShader); + gl::glAttachShader(m_averageProgram, m_averageGeometryShader); + gl::glAttachShader(m_averageProgram, m_averageFragmentShader); + + setShaderSourceAndCompile(m_plotVertexShader, dataPath() + "/unifiedmemory/shaders/plot.vert"); + auto success = checkForCompilationError(m_plotVertexShader, "plot vertex shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_plotFragmentShader, dataPath() + "/unifiedmemory/shaders/plot.frag"); + success &= checkForCompilationError(m_plotFragmentShader, "plot fragment shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_averageVertexShader, dataPath() + "/unifiedmemory/shaders/average.vert"); + success = checkForCompilationError(m_averageVertexShader, "average vertex shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_averageGeometryShader, dataPath() + "/unifiedmemory/shaders/average.geom"); + success = checkForCompilationError(m_averageGeometryShader, "average geometry shader"); + + if (!success) + { + return; + } + + setShaderSourceAndCompile(m_averageFragmentShader, dataPath() + "/unifiedmemory/shaders/average.frag"); + success &= checkForCompilationError(m_averageFragmentShader, "average fragment shader"); + + if (!success) + { + return; + } + + gl::glLinkProgram(m_plotProgram); + + success &= checkForLinkerError(m_plotProgram, "plot program"); + + if (!success) + { + return; + } + + gl::glLinkProgram(m_averageProgram); + + success &= checkForLinkerError(m_averageProgram, "average program"); + + if (!success) + { + return; + } + + m_plotVertexOffsetLocation = gl::glGetUniformLocation(m_plotProgram, "u_vertexOffset"); + m_plotCountLocation = gl::glGetUniformLocation(m_plotProgram, "u_count"); + m_plotSensorCountLocation = gl::glGetUniformLocation(m_plotProgram, "u_sensorCount"); + m_plotColorLocation = gl::glGetUniformLocation(m_plotProgram, "u_color"); + m_plotCurrentSensorLocation = gl::glGetUniformLocation(m_plotProgram, "u_sensor"); + + m_averageSensorCountLocation = gl::glGetUniformLocation(m_averageProgram, "u_sensorCount"); + m_averageColorLocation = gl::glGetUniformLocation(m_averageProgram, "u_color"); + m_averageCurrentSensorLocation = gl::glGetUniformLocation(m_averageProgram, "u_sensor"); +} + +void Rendering::uninitialize() +{ + gl::glDeleteShader(m_plotVertexShader); + gl::glDeleteShader(m_averageVertexShader); + gl::glDeleteShader(m_plotFragmentShader); + gl::glDeleteProgram(m_plotProgram); + gl::glDeleteProgram(m_averageProgram); + gl::glDeleteVertexArrays(1, &m_plotVAO); + gl::glDeleteVertexArrays(1, &m_averageVAO); +} + +void Rendering::render() +{ + static const auto colors = std::array({{ + glm::vec3(228,26,28) / glm::vec3(255), + glm::vec3(55,126,184) / glm::vec3(255), + glm::vec3(77,175,74) / glm::vec3(255), + glm::vec3(152,78,163) / glm::vec3(255), + glm::vec3(255,127,0) / glm::vec3(255), + glm::vec3(255,255,51) / glm::vec3(255), + glm::vec3(166,86,40) / glm::vec3(255), + glm::vec3(247,129,191) / glm::vec3(255) + }}); + + const auto numIterations = m_dataBuffer->size() / m_sensorCount; + + if (!m_renderingInitialized) + { + gl::glDisable(gl::GL_DEPTH_TEST); + + gl::glUseProgram(m_plotProgram); + + gl::glUniform1i(m_plotSensorCountLocation, m_sensorCount); + gl::glUniform1i(m_plotCountLocation, numIterations); + + //gl::glUseProgram(0); + gl::glUseProgram(m_averageProgram); + + gl::glUniform1i(m_averageSensorCountLocation, m_sensorCount); + + gl::glUseProgram(0); + + gl::glPointSize(m_sensorCount > 8 ? 2.0f : 4.0f); + + gl::glBindVertexArray(m_plotVAO); + gl::glBindBuffer(gl::GL_ARRAY_BUFFER, m_dataBuffer->gpuIdentifier()); + gl::glEnableVertexAttribArray(0); + + gl::glVertexAttribPointer(0, 1, gl::GL_FLOAT, gl::GL_FALSE, sizeof(float), 0); + + gl::glBindVertexArray(0); + + gl::glBindVertexArray(m_averageVAO); + gl::glBindBuffer(gl::GL_ARRAY_BUFFER, m_averageBuffer->gpuIdentifier()); + gl::glEnableVertexAttribArray(0); + + gl::glVertexAttribPointer(0, 1, gl::GL_FLOAT, gl::GL_FALSE, sizeof(float), 0); + + gl::glBindVertexArray(0); + + gl::glBindBuffer(gl::GL_ARRAY_BUFFER, 0); + + m_renderingInitialized = true; + } + + gl::glBindFramebuffer(gl::GL_FRAMEBUFFER, m_fbo); + gl::glClear(gl::GL_COLOR_BUFFER_BIT); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, m_minBuffer->gpuIdentifier()); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, m_maxBuffer->gpuIdentifier()); + + gl::glUseProgram(m_averageProgram); + gl::glBindVertexArray(m_averageVAO); + + for (auto i = std::uint32_t(0); i < m_sensorCount; ++i) + { + gl::glUniform1i(m_averageCurrentSensorLocation, i); + gl::glUniform3fv(m_averageColorLocation, 1, glm::value_ptr(colors[i % colors.size()])); + + gl::glDrawArrays(gl::GL_POINTS, i, 1); + } + + gl::glUseProgram(m_plotProgram); + gl::glBindVertexArray(m_plotVAO); + + for (auto i = std::uint32_t(0); i < m_sensorCount; ++i) + { + gl::glUniform1i(m_plotCurrentSensorLocation, i); + gl::glUniform3fv(m_plotColorLocation, 1, glm::value_ptr(colors[i % colors.size()])); + + // From current until end + gl::glUniform1i(m_plotVertexOffsetLocation, -m_currentIndex); + gl::glDrawArrays(gl::GL_LINE_STRIP, numIterations*i + m_currentIndex, m_pointCount - m_currentIndex); + + // From start until current + gl::glUniform1i(m_plotVertexOffsetLocation, m_pointCount - m_currentIndex); + gl::glDrawArrays(gl::GL_LINE_STRIP, numIterations*i + 0, m_currentIndex); + + // From last to current + if (m_currentIndex + 8 < numIterations && m_pointCount == numIterations) + { + const auto range = std::array {{ numIterations*(i+1) - 1, numIterations*(i+0) + 0 }}; + gl::glUniform1i(m_plotVertexOffsetLocation, -m_currentIndex-1); + gl::glDrawElements(gl::GL_LINE_STRIP, 2, gl::GL_UNSIGNED_INT, range.data()); + } + } + + gl::glBindVertexArray(0); + gl::glUseProgram(0); + + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 0, 0); + gl::glBindBufferBase(gl::GL_SHADER_STORAGE_BUFFER, 1, 0); + + gl::glBindFramebuffer(gl::GL_FRAMEBUFFER, 0); +} + +void Rendering::captureImage() +{ + gl::glMemoryBarrier(gl::GL_FRAMEBUFFER_BARRIER_BIT | gl::GL_PIXEL_BUFFER_BARRIER_BIT); + gl::glBindBuffer(gl::GL_PIXEL_PACK_BUFFER, m_pixelBuffer->gpuIdentifier()); + + gl::glReadBuffer(gl::GL_BACK_LEFT); + gl::glReadnPixels(0, 0, m_width, m_height, gl::GL_RGB, gl::GL_UNSIGNED_BYTE, m_pixelBuffer->size(), nullptr); + + gl::glBindBuffer(gl::GL_PIXEL_PACK_BUFFER, 0); + + m_pixelBuffer->startWait(); +} + +void Rendering::resize(int width, int height) +{ + m_width = width; + m_height = height; + + m_pixelBuffer->resize(m_width * m_height * numColorComponents * sizeof(gl::GLubyte)); + + gl::glViewport(0, 0, m_width, m_height); +} + +void Rendering::updateGeometry(int stride, int sensorCount, int pointCount, int currentIndex) +{ + m_stride = stride; + m_sensorCount = sensorCount; + m_pointCount = pointCount; + m_currentIndex = currentIndex; +} + +void Rendering::saveCapturedImage(std::string && basename) +{ + const auto filename = std::move(basename) + "." + std::to_string(m_width) + "." + std::to_string(m_height) + ".rgb.ub.raw"; + + m_pixelBuffer->wait(); + + std::async(std::launch::async, [this, filename=std::move(filename)]() { + std::fstream stream(filename, std::fstream::out | std::fstream::binary); + + stream.write(reinterpret_cast(m_pixelBuffer->data()), m_pixelBuffer->size()); + + stream.close(); + }); +} diff --git a/source/unifiedmemory/Rendering.h b/source/unifiedmemory/Rendering.h new file mode 100644 index 0000000..ff0709e --- /dev/null +++ b/source/unifiedmemory/Rendering.h @@ -0,0 +1,66 @@ + +#pragma once + +#include + +#include "unified_vector.h" + + +class Rendering +{ +public: + Rendering(); + ~Rendering(); + + void initialize(unshared_gpu_vector * dataBuffer, unified_read_vector * averageBuffer, unshared_gpu_vector * minBuffer, unshared_gpu_vector * maxBuffer, unified_read_vector * pixelBuffer); + void uninitialize(); + + void render(); + void captureImage(); + + void resize(int width, int height); + void updateGeometry(int stride, int sensorCount, int pointCount, int currentIndex); + + void saveCapturedImage(std::string && basename); + +protected: + const gl::GLuint m_fbo; + + gl::GLuint m_plotVAO; + gl::GLuint m_averageVAO; + + gl::GLuint m_plotProgram; + gl::GLuint m_plotVertexShader; + gl::GLuint m_plotFragmentShader; + + gl::GLuint m_averageProgram; + gl::GLuint m_averageVertexShader; + gl::GLuint m_averageGeometryShader; + gl::GLuint m_averageFragmentShader; + + gl::GLint m_plotVertexOffsetLocation; + gl::GLint m_plotSensorCountLocation; + gl::GLint m_plotCountLocation; + gl::GLint m_plotColorLocation; + gl::GLint m_plotCurrentSensorLocation; + + gl::GLint m_averageSensorCountLocation; + gl::GLint m_averageColorLocation; + gl::GLint m_averageCurrentSensorLocation; + + bool m_renderingInitialized; + + std::uint32_t m_stride; + std::uint32_t m_sensorCount; + std::uint32_t m_pointCount; + std::uint32_t m_currentIndex; + + gl::GLint m_width; + gl::GLint m_height; + + unshared_gpu_vector * m_dataBuffer; + unified_read_vector * m_averageBuffer; + unshared_gpu_vector * m_minBuffer; + unshared_gpu_vector * m_maxBuffer; + unified_read_vector * m_pixelBuffer; +}; diff --git a/source/unifiedmemory/common.cpp b/source/unifiedmemory/common.cpp new file mode 100644 index 0000000..2c9cb92 --- /dev/null +++ b/source/unifiedmemory/common.cpp @@ -0,0 +1,167 @@ + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +using namespace gl; + + +namespace +{ + + +std::string determineDataPath() +{ + std::string path = cpplocate::locatePath("data/unifiedmemory", "share/unifiedmemory", nullptr); + if (path.empty()) path = "./data"; + else path = path + "/data"; + + return path; +} + + +} // namespace + + +// Read raw binary file into a char vector (probably the fastest way). +std::vector rawFromFile(const std::string & filePath) +{ + auto stream = std::ifstream(filePath, std::ios::in | std::ios::binary | std::ios::ate); + + if (!stream) + { + std::cerr << "Reading from file '" << filePath << "' failed." << std::endl; + return std::vector(); + } + + stream.seekg(0, std::ios::end); + + const auto size = stream.tellg(); + auto raw = std::vector(size); + + stream.seekg(0, std::ios::beg); + stream.read(raw.data(), size); + + return raw; +} + +std::vector rawFromFileF(const std::string &filePath) +{ + auto stream = std::ifstream(filePath.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + + if (!stream) + { + std::cerr << "Reading from file '" << filePath << "' failed." << std::endl; + return std::vector(); + } + + stream.seekg(0, std::ios::end); + + const auto size = stream.tellg(); + auto raw = std::vector(size / sizeof(float)); + + stream.seekg(0, std::ios::beg); + stream.read(reinterpret_cast(raw.data()), (size / sizeof(float)) * sizeof(float)); + + return raw; +} + +std::string textFromFile(const std::string & filePath) +{ + const auto text = rawFromFile(filePath); + return std::string(text.begin(), text.end()); +} + +void setShaderSourceAndCompile(const GLuint shader, const std::string & filename) +{ + const auto shaderSource = textFromFile(filename); + const auto shaderSource_ptr = shaderSource.c_str(); + if (shaderSource_ptr) + gl::glShaderSource(shader, 1, &shaderSource_ptr, 0); + + gl::glCompileShader(shader); +} + +bool checkForCompilationError(GLuint shader, const std::string & identifier) +{ + auto success = static_cast(GL_FALSE); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (success != static_cast(GL_FALSE)) + return true; + + auto length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); + + std::vector log(length); + + glGetShaderInfoLog(shader, length, &length, log.data()); + + std::cerr + << "Compiler error in " << identifier << ":" << std::endl + << std::string(log.data(), length) << std::endl; + + return false; +} + +bool rawToFile(const std::string & filePath, const std::vector & raw) +{ + auto stream = std::ofstream(filePath, std::ios::out | std::ios::binary); + + if (!stream) + { + std::cerr << "Writing to file '" << filePath << "' failed." << std::endl; + return false; + } + + stream.write(raw.data(), raw.size()); + + return true; +} + +bool checkForLinkerError(GLuint program, const std::string & identifier) +{ + auto success = static_cast(GL_FALSE); + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success != static_cast(GL_FALSE)) + return true; + + auto length = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); + + std::vector log(length); + + glGetProgramInfoLog(program, length, &length, log.data()); + + std::cerr + << "Linker error in " << identifier << ":" << std::endl + << std::string(log.data(), length) << std::endl; + + return false; + +} + +const std::string & dataPath() +{ + static const auto path = determineDataPath(); + + return path; +} diff --git a/source/unifiedmemory/common.h b/source/unifiedmemory/common.h new file mode 100644 index 0000000..e8e21b6 --- /dev/null +++ b/source/unifiedmemory/common.h @@ -0,0 +1,30 @@ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// Read raw binary file into a char vector (probably the fastest way). +std::vector rawFromFile(const std::string & filePath); +std::vector rawFromFileF(const std::string & filePath); + +bool rawToFile(const std::string & filePath, const std::vector & raw); + +std::string textFromFile(const std::string & filePath); + +void setShaderSourceAndCompile(gl::GLuint shader, const std::string & filename); +bool createShader(gl::GLenum type, const std::string & name, const std::string & source, gl::GLuint & id); +bool createProgram(const std::string & name, gl::GLuint vertexShader, gl::GLuint fragmentShader, gl::GLuint & id); + +bool checkForCompilationError(gl::GLuint shader, const std::string & identifier); +bool checkForLinkerError(gl::GLuint program, const std::string & identifier); + +const std::string & dataPath(); diff --git a/source/unifiedmemory/main.cpp b/source/unifiedmemory/main.cpp new file mode 100644 index 0000000..8819541 --- /dev/null +++ b/source/unifiedmemory/main.cpp @@ -0,0 +1,232 @@ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "Computation.h" +#include "Rendering.h" + + +namespace +{ + + +Computation computation; +Rendering rendering; + +const auto canvasWidth = 1440; // in pixel +const auto canvasHeight = 900; // in pixel +const auto fullScreen = false; // start application in fullscreen +const auto windowTitle = "Unified Memory Demo"; // Title of the window + +// "The size callback ... which is called when the window is resized." +// http://www.glfw.org/docs/latest/group__window.html#gaa40cd24840daa8c62f36cafc847c72b6 +void resizeCallback(GLFWwindow * /*window*/, int width, int height) +{ + rendering.resize(width, height); +} + +void keyCallback(GLFWwindow * window, int key, int /*scancode*/, int /*action*/, int /*mods*/) +{ + if (key == GLFW_KEY_ESCAPE) + { + glfwSetWindowShouldClose(window, GLFW_TRUE); + } +} + + +// "In case a GLFW function fails, an error is reported to the GLFW +// error callback. You can receive these reports with an error +// callback." http://www.glfw.org/docs/latest/quick.html#quick_capture_error +void errorCallback(int errnum, const char * errmsg) +{ + std::cerr << errnum << ": " << errmsg << std::endl; +} + + +} // namespace + + +void mainGL(GLFWwindow * window, std::istream * cin, bool saveScreenshots, bool printLog) +{ + glbinding::Binding::initialize(false); + +#ifndef NDEBUG + glbinding::setAfterCallback([](const glbinding::FunctionCall & /*functionCall*/) { + gl::GLenum error = glbinding::Binding::GetError.directCall(); + + if (error != gl::GL_NO_ERROR) + { + std::cerr << "OpenGL Error " << std::hex << error << std::dec << std::endl; + + throw error; + } + }); +#endif + + glbinding::setCallbackMaskExcept(glbinding::CallbackMask::After, { "glGetError" }); + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + glfwSwapInterval(0); + + unshared_gpu_vector dataBuffer; + unified_read_vector averageBuffer; + unshared_gpu_vector minBuffer; + unshared_gpu_vector maxBuffer; + unified_read_vector pixelBuffer; + + computation.initialize(cin, &dataBuffer, &averageBuffer, &minBuffer, &maxBuffer, 1024); + rendering.initialize(&dataBuffer, &averageBuffer, &minBuffer, &maxBuffer, &pixelBuffer); + rendering.resize(width, height); + + auto nextScreenshotTimestamp = std::uint64_t(0); + while (!glfwWindowShouldClose(window)) // main loop + { + // CPU <- unified: Load image from unified + // DSK <- CPU : Store image to disk + if (computation.currentTime() > nextScreenshotTimestamp && saveScreenshots) + { + rendering.saveCapturedImage("screenshot" + std::to_string(computation.currentTime())); + nextScreenshotTimestamp = computation.currentTime() + 1000000; // each second + } + + // CPU : Handle window events + glfwPollEvents(); + + // CPU : Process sensor changes + // CPU -> unified: Update sensor data on unified + const auto numNewEvents = computation.processEvents(); + + // unified: Compute average + // CPU <- unified: Transfer average + // CPU : Write average to log + //std::clog << numNewEvents << std::endl; + if (numNewEvents > 0) + { + computation.computeAverage(); + + if (!averageBuffer.empty() && printLog) + { + averageBuffer.wait(); + std::clog << computation.currentTime() << ";"; + std::clog << std::accumulate(averageBuffer.begin()+1, averageBuffer.begin()+computation.numSensors(), std::to_string(averageBuffer.front()), [](const std::string & res, float value) { + return res + ";" + std::to_string(value); + }) << std::endl; + } + } + + // unified: Render overview image + rendering.updateGeometry(averageBuffer.size(), computation.numSensors(), computation.availablePoints(), computation.currentIndex()); + rendering.render(); + + if (computation.currentTime() > nextScreenshotTimestamp && saveScreenshots) + { + rendering.captureImage(); + } + + // CPU -> unified: Swap buffers + glfwSwapBuffers(window); + } +} + + +int main(int argc, char ** argv) +{ + std::ios_base::sync_with_stdio(false); + std::cin.tie(nullptr); + std::cout.tie(nullptr); + std::clog.tie(nullptr); + std::cerr.tie(nullptr); + + const auto emulate = (argc >= 2 && std::string(argv[1]) == std::string("-e")) + || (argc >= 3 && std::string(argv[2]) == std::string("-e")) + || (argc >= 4 && std::string(argv[3]) == std::string("-e")); + const auto saveScreenshots = (argc >= 2 && std::string(argv[1]) == std::string("-s")) + || (argc >= 3 && std::string(argv[2]) == std::string("-s")) + || (argc >= 4 && std::string(argv[3]) == std::string("-s")); + const auto outputLog = (argc >= 2 && std::string(argv[1]) == std::string("-v")) + || (argc >= 3 && std::string(argv[2]) == std::string("-v")) + || (argc >= 4 && std::string(argv[3]) == std::string("-v")); + + auto input = &std::cin; + std::unique_ptr emulation; + if (emulate) + { + auto stream = new std::stringstream(); + + std::mt19937_64 generator; + std::normal_distribution distribution(100.0,5.0); + + for (auto i = 0; i < 10000; ++i) + { + *stream << i; + for (auto j = 0; j < 4; ++j) + { + *stream << ';' << distribution(generator); + } + *stream << '\n'; + } + + emulation.reset(stream); + input = stream; + } + + if (!glfwInit()) + { + return 1; + } + + glfwSetErrorCallback(errorCallback); + + glfwDefaultWindowHints(); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + GLFWwindow * window = nullptr; + + if (fullScreen) + { + const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + window = glfwCreateWindow(mode->width, mode->height, windowTitle, glfwGetPrimaryMonitor(), nullptr); + } + else + { + window = glfwCreateWindow(canvasWidth, canvasHeight, windowTitle, nullptr, nullptr); + } + + if (!window) + { + glfwTerminate(); + + return 2; + } + + glfwSetFramebufferSizeCallback(window, resizeCallback); + glfwSetKeyCallback(window, keyCallback); + + glfwMakeContextCurrent(window); + + mainGL(window, input, saveScreenshots, outputLog); + + glfwMakeContextCurrent(nullptr); + + glfwDestroyWindow(window); + + glfwTerminate(); + + return 0; +} diff --git a/source/unifiedmemory/unified_allocator.h b/source/unifiedmemory/unified_allocator.h new file mode 100644 index 0000000..9650c72 --- /dev/null +++ b/source/unifiedmemory/unified_allocator.h @@ -0,0 +1,92 @@ + +#pragma once + + +#include + +#include + +#include + + +class coherent_flags +{ +public: + static constexpr auto mapFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT | gl::GL_MAP_COHERENT_BIT; + static constexpr auto storageFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT | gl::GL_MAP_COHERENT_BIT; +}; + +class read_write_flags +{ +public: + static constexpr auto mapFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT | gl::GL_MAP_FLUSH_EXPLICIT_BIT; + static constexpr auto storageFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT; +}; + +class read_flags +{ +public: + static constexpr auto mapFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_PERSISTENT_BIT; + static constexpr auto storageFlags = gl::GL_MAP_READ_BIT | gl::GL_MAP_PERSISTENT_BIT; +}; + +class write_flags +{ +public: + static constexpr auto mapFlags = gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT | gl::GL_MAP_FLUSH_EXPLICIT_BIT | gl::GL_MAP_UNSYNCHRONIZED_BIT; + static constexpr auto storageFlags = gl::GL_MAP_WRITE_BIT | gl::GL_MAP_PERSISTENT_BIT; +}; + +class no_map_flags +{ +public: + static constexpr auto mapFlags = gl::GL_NONE_BIT; + static constexpr auto storageFlags = gl::GL_NONE_BIT; +}; + + +template +class unified_allocator +{ +public: + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using size_type = size_t; + using difference_type = ptrdiff_t; + + template + struct rebind + { + using other = unified_allocator; + }; + + inline unified_allocator() throw() {} + inline unified_allocator(const unified_allocator&) throw() {} + + template + inline unified_allocator(const unified_allocator&) throw() {} + + inline ~unified_allocator() throw() {} + + inline pointer address(reference r) { return &r; } + inline const_pointer address(const_reference r) const { return &r; } + + pointer allocate(size_type n, typename std::allocator::const_pointer hint = 0); + inline void deallocate(pointer p, size_type); + + inline void construct(pointer p, const_reference value) { new (p) value_type(value); } + inline void destroy(pointer p) { p->~value_type(); } + + inline size_type max_size() const throw() { return size_type(-1) / sizeof(T); } + + inline bool operator==(const unified_allocator&) { return true; } + inline bool operator!=(const unified_allocator& rhs) { return !operator==(rhs); } + + static std::map s_gpuBufferMap; +}; + + +#include "unified_allocator.inl" diff --git a/source/unifiedmemory/unified_allocator.inl b/source/unifiedmemory/unified_allocator.inl new file mode 100644 index 0000000..ab8c81a --- /dev/null +++ b/source/unifiedmemory/unified_allocator.inl @@ -0,0 +1,62 @@ + +#include +#include +#include + +template +std::map::const_pointer, gl::GLuint> unified_allocator::s_gpuBufferMap; + +template +typename unified_allocator::pointer +unified_allocator::allocate(size_type n, typename std::allocator::const_pointer hint) +{ + static const auto mapFlags = unified_flags::mapFlags; + static const auto storageFlags = unified_flags::storageFlags; + + (void)hint; + + const auto byteSize = n * sizeof(T); + + gl::GLuint bufferId = 0; + gl::glCreateBuffers(1, &bufferId); + + gl::glNamedBufferStorage(bufferId, byteSize, nullptr, storageFlags); + + auto res = pointer(nullptr); + + if ((gl::GL_MAP_PERSISTENT_BIT & mapFlags) != gl::MapBufferUsageMask::GL_NONE_BIT) + { + res = reinterpret_cast(gl::glMapNamedBufferRange(bufferId, 0, byteSize, mapFlags)); + } + else + { + res = reinterpret_cast(malloc(byteSize)); + } + + s_gpuBufferMap[res] = bufferId; + + return res; +} + +template +void +unified_allocator::deallocate(pointer p, size_type) +{ + static const auto mapFlags = unified_flags::mapFlags; + + auto bufferId = s_gpuBufferMap[p]; + + if (bufferId > 0) + { + if ((gl::GL_MAP_PERSISTENT_BIT & mapFlags) != gl::MapBufferUsageMask::GL_NONE_BIT) + { + gl::glUnmapNamedBuffer(bufferId); + } + else + { + free(p); + } + + gl::glDeleteBuffers(1, &bufferId); + } +} diff --git a/source/unifiedmemory/unified_vector.h b/source/unifiedmemory/unified_vector.h new file mode 100644 index 0000000..ea5763d --- /dev/null +++ b/source/unifiedmemory/unified_vector.h @@ -0,0 +1,84 @@ + +#pragma once + + +#include + +#include "unified_allocator.h" + + +template > +using unshared_cpu_vector = std::vector; + + +template +class unified_vector : public std::vector> +{ +public: + using allocator_type = unified_allocator; + + using std::vector::vector; + + gl::GLuint gpuIdentifier() const; + void flush() const; + void flush(std::size_t start, std::size_t end) const; + void startWait() const; + void wait() const; + +protected: + mutable gl::GLsync m_sync; +}; + +template +class unified_synchronized_vector : public std::vector> +{ +public: + using allocator_type = unified_allocator; + + using std::vector::vector; + + gl::GLuint gpuIdentifier() const; +}; + +template +class unified_read_vector : public std::vector> +{ +public: + using allocator_type = unified_allocator; + + using std::vector::vector; + + gl::GLuint gpuIdentifier() const; + void startWait() const; + void wait() const; + +protected: + mutable gl::GLsync m_sync; +}; + +template +class unified_write_vector : public std::vector> +{ +public: + using allocator_type = unified_allocator; + + using std::vector::vector; + + gl::GLuint gpuIdentifier() const; + void flush() const; + void flush(std::size_t start, std::size_t end) const; +}; + +template +class unshared_gpu_vector : public std::vector> +{ +public: + using allocator_type = unified_allocator; + + using std::vector::vector; + + gl::GLuint gpuIdentifier() const; +}; + + +#include "unified_vector.inl" diff --git a/source/unifiedmemory/unified_vector.inl b/source/unifiedmemory/unified_vector.inl new file mode 100644 index 0000000..8004055 --- /dev/null +++ b/source/unifiedmemory/unified_vector.inl @@ -0,0 +1,127 @@ + +#include + +template +gl::GLuint unified_vector::gpuIdentifier() const +{ + auto it = allocator_type::s_gpuBufferMap.find(this->data()); + + if (it == allocator_type::s_gpuBufferMap.end()) + { + return 0; + } + + return it->second; +} + +template +void unified_vector::startWait() const +{ + gl::glMemoryBarrier(gl::GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + m_sync = gl::glFenceSync(gl::GL_SYNC_GPU_COMMANDS_COMPLETE, gl::UnusedMask::GL_UNUSED_BIT); +} + +template +void unified_vector::wait() const +{ + gl::glClientWaitSync(m_sync, gl::SyncObjectMask::GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); + gl::glDeleteSync(m_sync); +} + +template +void unified_vector::flush() const +{ + const auto buffer = gpuIdentifier(); + gl::glFlushMappedNamedBufferRange(buffer, 0, sizeof(T) * this->size()); +} + +template +void unified_vector::flush(std::size_t start, std::size_t end) const +{ + const auto buffer = gpuIdentifier(); + gl::glFlushMappedNamedBufferRange(buffer, sizeof(T) * start, sizeof(T) * (end - start)); +} + + +template +gl::GLuint unified_synchronized_vector::gpuIdentifier() const +{ + auto it = allocator_type::s_gpuBufferMap.find(this->data()); + + if (it == allocator_type::s_gpuBufferMap.end()) + { + return 0; + } + + return it->second; +} + + +template +gl::GLuint unified_read_vector::gpuIdentifier() const +{ + auto it = allocator_type::s_gpuBufferMap.find(this->data()); + + if (it == allocator_type::s_gpuBufferMap.end()) + { + return 0; + } + + return it->second; +} + +template +void unified_read_vector::startWait() const +{ + gl::glMemoryBarrier(gl::GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + m_sync = gl::glFenceSync(gl::GL_SYNC_GPU_COMMANDS_COMPLETE, gl::UnusedMask::GL_UNUSED_BIT); +} + +template +void unified_read_vector::wait() const +{ + gl::glClientWaitSync(m_sync, gl::SyncObjectMask::GL_SYNC_FLUSH_COMMANDS_BIT, static_cast(-1)); + gl::glDeleteSync(m_sync); +} + + +template +gl::GLuint unified_write_vector::gpuIdentifier() const +{ + auto it = allocator_type::s_gpuBufferMap.find(this->data()); + + if (it == allocator_type::s_gpuBufferMap.end()) + { + return 0; + } + + return it->second; +} + +template +void unified_write_vector::flush() const +{ + const auto buffer = gpuIdentifier(); + gl::glFlushMappedNamedBufferRange(buffer, 0, sizeof(T) * this->size()); +} + +template +void unified_write_vector::flush(std::size_t start, std::size_t end) const +{ + const auto buffer = gpuIdentifier(); + gl::glFlushMappedNamedBufferRange(buffer, sizeof(T) * start, sizeof(T) * (end - start)); +} + + +template +gl::GLuint unshared_gpu_vector::gpuIdentifier() const +{ + auto it = allocator_type::s_gpuBufferMap.find(this->data()); + + if (it == allocator_type::s_gpuBufferMap.end()) + { + return 0; + } + + return it->second; +} diff --git a/source/version.h.in b/source/version.h.in new file mode 100644 index 0000000..08bafb7 --- /dev/null +++ b/source/version.h.in @@ -0,0 +1,15 @@ + +#define ${META_PROJECT_NAME_UPPER}_PROJECT_NAME "@META_PROJECT_NAME@" +#define ${META_PROJECT_NAME_UPPER}_PROJECT_DESCRIPTION "@META_PROJECT_DESCRIPTION@" + +#define ${META_PROJECT_NAME_UPPER}_AUTHOR_ORGANIZATION "@META_AUTHOR_ORGANIZATION@" +#define ${META_PROJECT_NAME_UPPER}_AUTHOR_DOMAIN "@META_AUTHOR_DOMAIN@" +#define ${META_PROJECT_NAME_UPPER}_AUTHOR_MAINTAINER "@META_AUTHOR_MAINTAINER@" + +#define ${META_PROJECT_NAME_UPPER}_VERSION_MAJOR "@META_VERSION_MAJOR@" +#define ${META_PROJECT_NAME_UPPER}_VERSION_MINOR "@META_VERSION_MINOR@" +#define ${META_PROJECT_NAME_UPPER}_VERSION_PATCH "@META_VERSION_PATCH@" +#define ${META_PROJECT_NAME_UPPER}_VERSION_REVISION "@META_VERSION_REVISION@" + +#define ${META_PROJECT_NAME_UPPER}_VERSION "@META_VERSION@" +#define ${META_PROJECT_NAME_UPPER}_NAME_VERSION "@META_NAME_VERSION@"